Skip to content

gh-114099: Add configure/Makefile changes to support iOS builds. #115063

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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