Skip to content
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

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

Closed
wants to merge 1 commit into from
Closed
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
@@ -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
@@ -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.
104 changes: 86 additions & 18 deletions Makefile.pre.in
Original file line number Diff line number Diff line change
@@ -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.
@@ -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)
@@ -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);
@@ -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)
@@ -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.
@@ -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" ;; \
@@ -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
@@ -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
@@ -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:
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
@@ -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__)
25 changes: 19 additions & 6 deletions Python/dynload_shlib.c
Original file line number Diff line number Diff line change
@@ -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
@@ -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,
};
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