Skip to content

Commit

Permalink
Add configure/Makefile changes to support iOS builds.
Browse files Browse the repository at this point in the history
  • Loading branch information
freakboy3742 committed Feb 6, 2024
1 parent 01dceba commit aa50087
Show file tree
Hide file tree
Showing 58 changed files with 2,458 additions and 445 deletions.
16 changes: 16 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@ Lib/test/data/*
/_bootstrap_python
/Makefile
/Makefile.pre
iOS/Resources/Info.plist
tvOS/Resources/Info.plist
watchOS/Resources/Info.plist
Mac/Makefile
Mac/PythonLauncher/Info.plist
Mac/PythonLauncher/Makefile
Expand Down Expand Up @@ -147,6 +150,19 @@ Tools/msi/obj
Tools/ssl/amd64
Tools/ssl/win32
Tools/freeze/test/outdir
Tools/iOSTestbed/build
Tools/iOSTestbed/Python.xcframework/ios-arm64/bin
Tools/iOSTestbed/Python.xcframework/ios-arm64/include
Tools/iOSTestbed/Python.xcframework/ios-arm64/lib
Tools/iOSTestbed/Python.xcframework/ios-arm64/Python.framework
Tools/iOSTestbed/Python.xcframework/ios-arm64_x86_64-simulator/bin
Tools/iOSTestbed/Python.xcframework/ios-arm64_x86_64-simulator/include
Tools/iOSTestbed/Python.xcframework/ios-arm64_x86_64-simulator/lib
Tools/iOSTestbed/Python.xcframework/ios-arm64_x86_64-simulator/Python.framework
Tools/iOSTestbed/iOSTestbed/iOSTestbed-Info.plist
Tools/iOSTestbed/iOSTestbed.xcodeproj/project.xcworkspace
Tools/iOSTestbed/iOSTestbed.xcodeproj/xcuserdata
Tools/iOSTestbed/iOSTestbed.xcodeproj/xcshareddata

# The frozen modules are always generated by the build so we don't
# keep them in the repo. Also see Tools/build/freeze_modules.py.
Expand Down
104 changes: 86 additions & 18 deletions Makefile.pre.in
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,8 @@ PYTHONFRAMEWORK= @PYTHONFRAMEWORK@
PYTHONFRAMEWORKDIR= @PYTHONFRAMEWORKDIR@
PYTHONFRAMEWORKPREFIX= @PYTHONFRAMEWORKPREFIX@
PYTHONFRAMEWORKINSTALLDIR= @PYTHONFRAMEWORKINSTALLDIR@
PYTHONFRAMEWORKINSTALLNAMEPREFIX= @PYTHONFRAMEWORKINSTALLNAMEPREFIX@
RESSRCDIR= @RESSRCDIR@
# Deployment target selected during configure, to be checked
# by distutils. The export statement is needed to ensure that the
# deployment target is active during build.
Expand Down Expand Up @@ -865,7 +867,7 @@ libpython3.so: libpython$(LDVERSION).so
$(BLDSHARED) $(NO_AS_NEEDED) -o $@ -Wl,-h$@ $^

libpython$(LDVERSION).dylib: $(LIBRARY_OBJS)
$(CC) -dynamiclib -Wl,-single_module $(PY_CORE_LDFLAGS) -undefined dynamic_lookup -Wl,-install_name,$(prefix)/lib/libpython$(LDVERSION).dylib -Wl,-compatibility_version,$(VERSION) -Wl,-current_version,$(VERSION) -o $@ $(LIBRARY_OBJS) $(DTRACE_OBJS) $(SHLIBS) $(LIBC) $(LIBM); \
$(CC) -dynamiclib $(PY_CORE_LDFLAGS) -undefined dynamic_lookup -Wl,-install_name,$(PYTHONFRAMEWORKINSTALLNAMEPREFIX)/lib/libpython$(LDVERSION).dylib -Wl,-compatibility_version,$(VERSION) -Wl,-current_version,$(VERSION) -o $@ $(LIBRARY_OBJS) $(DTRACE_OBJS) $(SHLIBS) $(LIBC) $(LIBM); \


libpython$(VERSION).sl: $(LIBRARY_OBJS)
Expand All @@ -890,14 +892,13 @@ $(BUILDPYTHON)-gdb.py: $(SRC_GDB_HOOKS)
# This rule is here for OPENSTEP/Rhapsody/MacOSX. It builds a temporary
# minimal framework (not including the Lib directory and such) in the current
# directory.
RESSRCDIR=Mac/Resources/framework
$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK): \
$(LIBRARY) \
$(RESSRCDIR)/Info.plist
$(INSTALL) -d -m $(DIRMODE) $(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)
$(CC) -o $(LDLIBRARY) $(PY_CORE_LDFLAGS) -dynamiclib \
-all_load $(LIBRARY) -Wl,-single_module \
-install_name $(DESTDIR)$(PYTHONFRAMEWORKINSTALLDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK) \
-all_load $(LIBRARY) \
-install_name $(PYTHONFRAMEWORKINSTALLNAMEPREFIX)/Versions/$(VERSION)/$(PYTHONFRAMEWORK) \
-compatibility_version $(VERSION) \
-current_version $(VERSION) \
-framework CoreFoundation $(LIBS);
Expand All @@ -909,6 +910,21 @@ $(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK): \
$(LN) -fsn Versions/Current/$(PYTHONFRAMEWORK) $(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK)
$(LN) -fsn Versions/Current/Resources $(PYTHONFRAMEWORKDIR)/Resources

# This rule is for iOS, which requires an annoyingly just slighly different
# format for frameworks to macOS. It *doesn't* use a versioned framework, and
# the Info.plist must be in the root of the framework.
$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK): \
$(LIBRARY) \
$(RESSRCDIR)/Info.plist
$(INSTALL) -d -m $(DIRMODE) $(PYTHONFRAMEWORKDIR)
$(CC) -o $(LDLIBRARY) $(PY_CORE_LDFLAGS) -dynamiclib \
-all_load $(LIBRARY) \
-install_name $(PYTHONFRAMEWORKINSTALLNAMEPREFIX)/$(PYTHONFRAMEWORK) \
-compatibility_version $(VERSION) \
-current_version $(VERSION) \
-framework CoreFoundation $(LIBS);
$(INSTALL_DATA) $(RESSRCDIR)/Info.plist $(PYTHONFRAMEWORKDIR)/Info.plist

# This rule builds the Cygwin Python DLL and import library if configured
# for a shared core library; otherwise, this rule is a noop.
$(DLLLIBRARY) libpython$(LDVERSION).dll.a: $(LIBRARY_OBJS)
Expand Down Expand Up @@ -1944,6 +1960,21 @@ testuniversal: all
$(RUNSHARED) /usr/libexec/oah/translate \
./$(BUILDPYTHON) -E -m test -j 0 -u all $(TESTOPTS)

# Run the test suite on the iOS simulator.
# Must be run on a macOS machine with a full Xcode install that has an iPhone SE
# (3rd edition) simulator available, after running `make install` on a configuration
# with --enable-framework="./Tools/iOSTestbed/Python.xcframework/ios-arm64_x86_64-simulator"
XCRESULT=./build/$(MULTIARCH).$(shell date +%s).xcresult
.PHONY: testiOS
testiOS:
# Run the test suite for the Xcode project, targeting the iOS simulator.
# If the suite fails, extract and print the console output, then re-raise the failure
if ! xcodebuild test -project ./Tools/iOSTestbed/iOSTestbed.xcodeproj -scheme "iOSTestbed" -destination "platform=iOS Simulator,name=iPhone SE (3rd Generation)" -resultBundlePath $(XCRESULT) ; then \
xcrun xcresulttool get --path $(XCRESULT) --id $$(xcrun xcresulttool get --path $(XCRESULT) --format json | $(PYTHON_FOR_BUILD) -c "import sys, json; result = json.load(sys.stdin); print(result['actions']['_values'][0]['actionResult']['logRef']['id']['_value'])"); \
echo ; \
exit 1; \
fi

# Like test, but using --slow-ci which enables all test resources and use
# longer timeout. Run an optional pybuildbot.identify script to include
# information about the build environment.
Expand Down Expand Up @@ -1983,7 +2014,7 @@ multissltest: all
# which can lead to two parallel `./python setup.py build` processes that
# step on each others toes.
.PHONY: install
install: @FRAMEWORKINSTALLFIRST@ commoninstall bininstall maninstall @FRAMEWORKINSTALLLAST@
install: @FRAMEWORKINSTALLFIRST@ @INSTALLTARGETS@ @FRAMEWORKINSTALLLAST@
if test "x$(ENSUREPIP)" != "xno" ; then \
case $(ENSUREPIP) in \
upgrade) ensurepip="--upgrade" ;; \
Expand Down Expand Up @@ -2574,20 +2605,36 @@ frameworkinstallstructure: $(LDLIBRARY)
exit 1; \
else true; \
fi
@for i in $(prefix)/Resources/English.lproj $(prefix)/lib; do\
if test ! -d $(DESTDIR)$$i; then \
echo "Creating directory $(DESTDIR)$$i"; \
$(INSTALL) -d -m $(DIRMODE) $(DESTDIR)$$i; \
else true; \
# iOS/tvOS/watchOS uses a non-versioned framework with Info.plist in the
# framework root, no .lproj data, and binaries
@if test "$(MACHDEP)" = ios -o "$(MACHDEP)" = tvos -o "$(MACHDEP)" = watchos; then \
if test -d $(PYTHONFRAMEWORKPREFIX)/include; then \
echo "Clearing stale header symlink directory"; \
rm -rf $(PYTHONFRAMEWORKPREFIX)/include; \
fi; \
done
$(LN) -fsn include/python$(LDVERSION) $(DESTDIR)$(prefix)/Headers
sed 's/%VERSION%/'"`$(RUNSHARED) ./$(BUILDPYTHON) -c 'import platform; print(platform.python_version())'`"'/g' < $(RESSRCDIR)/Info.plist > $(DESTDIR)$(prefix)/Resources/Info.plist
$(LN) -fsn $(VERSION) $(DESTDIR)$(PYTHONFRAMEWORKINSTALLDIR)/Versions/Current
$(LN) -fsn Versions/Current/$(PYTHONFRAMEWORK) $(DESTDIR)$(PYTHONFRAMEWORKINSTALLDIR)/$(PYTHONFRAMEWORK)
$(LN) -fsn Versions/Current/Headers $(DESTDIR)$(PYTHONFRAMEWORKINSTALLDIR)/Headers
$(LN) -fsn Versions/Current/Resources $(DESTDIR)$(PYTHONFRAMEWORKINSTALLDIR)/Resources
$(INSTALL_SHARED) $(LDLIBRARY) $(DESTDIR)$(PYTHONFRAMEWORKPREFIX)/$(LDLIBRARY)
$(INSTALL) -d -m $(DIRMODE) $(PYTHONFRAMEWORKINSTALLDIR); \
sed 's/%VERSION%/'"`$(RUNSHARED) $(PYTHON_FOR_BUILD) -c 'import platform; print(platform.python_version())'`"'/g' < $(RESSRCDIR)/Info.plist > $(PYTHONFRAMEWORKINSTALLDIR)/Info.plist; \
$(INSTALL_SHARED) $(LDLIBRARY) $(PYTHONFRAMEWORKPREFIX)/$(LDLIBRARY); \
$(INSTALL) -d -m $(DIRMODE) $(BINDIR); \
for file in $(RESSRCDIR)/bin/* ; do \
$(INSTALL) -m $(EXEMODE) $$file $(BINDIR); \
done; \
else \
for i in $(prefix)/Resources/English.lproj $(prefix)/lib; do \
if test ! -d $(DESTDIR)$$i; then \
echo "Creating directory $(DESTDIR)$$i"; \
$(INSTALL) -d -m $(DIRMODE) $(DESTDIR)$$i; \
else true; \
fi; \
done; \
$(LN) -fsn include/python$(LDVERSION) $(DESTDIR)$(prefix)/Headers; \
sed 's/%VERSION%/'"`$(RUNSHARED) $(PYTHON_FOR_BUILD) -c 'import platform; print(platform.python_version())'`"'/g' < $(RESSRCDIR)/Info.plist > $(DESTDIR)$(prefix)/Resources/Info.plist; \
$(LN) -fsn $(VERSION) $(DESTDIR)$(PYTHONFRAMEWORKINSTALLDIR)/Versions/Current; \
$(LN) -fsn Versions/Current/$(PYTHONFRAMEWORK) $(DESTDIR)$(PYTHONFRAMEWORKINSTALLDIR)/$(PYTHONFRAMEWORK); \
$(LN) -fsn Versions/Current/Headers $(DESTDIR)$(PYTHONFRAMEWORKINSTALLDIR)/Headers; \
$(LN) -fsn Versions/Current/Resources $(DESTDIR)$(PYTHONFRAMEWORKINSTALLDIR)/Resources; \
$(INSTALL_SHARED) $(LDLIBRARY) $(DESTDIR)$(PYTHONFRAMEWORKPREFIX)/$(LDLIBRARY); \
fi

# This installs Mac/Lib into the framework
# Install a number of symlinks to keep software that expects a normal unix
Expand Down Expand Up @@ -2629,6 +2676,19 @@ frameworkaltinstallunixtools:
frameworkinstallextras:
cd Mac && $(MAKE) installextras DESTDIR="$(DESTDIR)"

# On iOS, bin/lib can't live inside the framework; include needs to be called
# "Headers", but *must* be in the framework, and *not* include the `python3.X`
# subdirectory. The install has put these folders in the same folder as
# Python.framework; Move the headers to their final framework-compatible home.
.PHONY: frameworkinstallmobileheaders
frameworkinstallmobileheaders:
if test -d $(PYTHONFRAMEWORKINSTALLDIR)/Headers; then \
echo "Removing old framework headers"; \
rm -rf $(PYTHONFRAMEWORKINSTALLDIR)/Headers; \
fi
mv "$(PYTHONFRAMEWORKPREFIX)/include/python$(VERSION)" "$(PYTHONFRAMEWORKINSTALLDIR)/Headers"
$(LN) -fs "$(PYTHONFRAMEWORKDIR)" "$(PYTHONFRAMEWORKPREFIX)/include/python$(VERSION)"

# Build the toplevel Makefile
Makefile.pre: $(srcdir)/Makefile.pre.in config.status
CONFIG_FILES=Makefile.pre CONFIG_HEADERS= ./config.status
Expand Down Expand Up @@ -2756,6 +2816,14 @@ clean-retain-profile: pycremoval
-find build -type f -a ! -name '*.gc??' -exec rm -f {} ';'
-rm -f Include/pydtrace_probes.h
-rm -f profile-gen-stamp
-rm -rf Tools/iOSTestbed/Python.xcframework/ios-arm64/bin
-rm -rf Tools/iOSTestbed/Python.xcframework/ios-arm64/lib
-rm -rf Tools/iOSTestbed/Python.xcframework/ios-arm64/include
-rm -rf Tools/iOSTestbed/Python.xcframework/ios-arm64/Python.framework
-rm -rf Tools/iOSTestbed/Python.xcframework/ios-arm64_x86_64-simulator/bin
-rm -rf Tools/iOSTestbed/Python.xcframework/ios-arm64_x86_64-simulator/lib
-rm -rf Tools/iOSTestbed/Python.xcframework/ios-arm64_x86_64-simulator/include
-rm -rf Tools/iOSTestbed/Python.xcframework/ios-arm64_x86_64-simulator/Python.framework

.PHONY: profile-removal
profile-removal:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Added configuration changes to support building iOS/tvOS/watchOS frameworks.
35 changes: 35 additions & 0 deletions Misc/platform_triplet.c
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,42 @@ PLATFORM_TRIPLET=i386-gnu
# error unknown platform triplet
# endif
#elif defined(__APPLE__)
# include "TargetConditionals.h"
# if TARGET_OS_IOS
# if TARGET_OS_SIMULATOR
# if __x86_64__
PLATFORM_TRIPLET=iphonesimulator-x86_64
# else
PLATFORM_TRIPLET=iphonesimulator-arm64
# endif
# else
PLATFORM_TRIPLET=iphoneos-arm64
# endif
# elif TARGET_OS_TV
# if TARGET_OS_SIMULATOR
# if __x86_64__
PLATFORM_TRIPLET=appletvsimulator-x86_64
# else
PLATFORM_TRIPLET=appletvsimulator-arm64
# endif
# else
PLATFORM_TRIPLET=appletvos-arm64
# endif
# elif TARGET_OS_WATCH
# if TARGET_OS_SIMULATOR
# if __x86_64__
PLATFORM_TRIPLET=watchsimulator-x86_64
# else
PLATFORM_TRIPLET=watchsimulator-arm64
# endif
# else
PLATFORM_TRIPLET=watchos-arm64_32
# endif
# elif TARGET_OS_OSX
PLATFORM_TRIPLET=darwin
# else
# error unknown Apple platform
# endif
#elif defined(__VXWORKS__)
PLATFORM_TRIPLET=vxworks
#elif defined(__wasm32__)
Expand Down
25 changes: 19 additions & 6 deletions Python/dynload_shlib.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@
#define LEAD_UNDERSCORE ""
#endif

#ifdef __APPLE__
# include "TargetConditionals.h"
#endif /* __APPLE__ */

/* The .so extension module ABI tag, supplied by the Makefile via
Makefile.pre.in and configure. This is used to discriminate between
incompatible .so files so that extensions for different Python builds can
Expand All @@ -38,12 +42,21 @@ const char *_PyImport_DynLoadFiletab[] = {
#ifdef __CYGWIN__
".dll",
#else /* !__CYGWIN__ */
"." SOABI ".so",
#ifdef ALT_SOABI
"." ALT_SOABI ".so",
#endif
".abi" PYTHON_ABI_STRING ".so",
".so",
# ifdef __APPLE__
# if TARGET_OS_IPHONE
# define SHLIB_SUFFIX ".dylib"
# else
# define SHLIB_SUFFIX ".so"
# endif
# else
# define SHLIB_SUFFIX ".so"
# endif
"." SOABI SHLIB_SUFFIX,
# ifdef ALT_SOABI
"." ALT_SOABI SHLIB_SUFFIX,
# endif
".abi" PYTHON_ABI_STRING SHLIB_SUFFIX,
SHLIB_SUFFIX,
#endif /* __CYGWIN__ */
NULL,
};
Expand Down
44 changes: 44 additions & 0 deletions Tools/iOSTestbed/Python.xcframework/Info.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>AvailableLibraries</key>
<array>
<dict>
<key>BinaryPath</key>
<string>Python.framework/Python</string>
<key>LibraryIdentifier</key>
<string>ios-arm64</string>
<key>LibraryPath</key>
<string>Python.framework</string>
<key>SupportedArchitectures</key>
<array>
<string>arm64</string>
</array>
<key>SupportedPlatform</key>
<string>ios</string>
</dict>
<dict>
<key>BinaryPath</key>
<string>Python.framework/Python</string>
<key>LibraryIdentifier</key>
<string>ios-arm64_x86_64-simulator</string>
<key>LibraryPath</key>
<string>Python.framework</string>
<key>SupportedArchitectures</key>
<array>
<string>arm64</string>
<string>x86_64</string>
</array>
<key>SupportedPlatform</key>
<string>ios</string>
<key>SupportedPlatformVariant</key>
<string>simulator</string>
</dict>
</array>
<key>CFBundlePackageType</key>
<string>XFWK</string>
<key>XCFrameworkFormatVersion</key>
<string>1.0</string>
</dict>
</plist>
4 changes: 4 additions & 0 deletions Tools/iOSTestbed/Python.xcframework/ios-arm64/README
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
This directory is intentionally empty.

It should be used as a target for `--enable-framework` when compiling an iOS on-device
build for testing purposes.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
This directory is intentionally empty.

It should be used as a target for `--enable-framework` when compiling an iOS simulator
build for testing purposes (either x86_64 or ARM64).
Loading

0 comments on commit aa50087

Please sign in to comment.