From 4e53cb51882120c08b3b3aaa182659e049e92f97 Mon Sep 17 00:00:00 2001 From: Marc Prud'hommeaux Date: Tue, 29 Jul 2025 17:12:22 +0200 Subject: [PATCH 1/4] Support 16 KB page sizes on Android (#81596) Android 15+ requires that native libraries be compiled with a linker flag to support 16 KB page sizes. See: https://developer.android.com/guide/practices/page-sizes#compile-r26-lower --- stdlib/cmake/modules/AddSwiftStdlib.cmake | 2 ++ utils/swift_build_support/swift_build_support/targets.py | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/stdlib/cmake/modules/AddSwiftStdlib.cmake b/stdlib/cmake/modules/AddSwiftStdlib.cmake index 0b18957eadb31..5021409ba061e 100644 --- a/stdlib/cmake/modules/AddSwiftStdlib.cmake +++ b/stdlib/cmake/modules/AddSwiftStdlib.cmake @@ -2504,6 +2504,8 @@ function(add_swift_target_library name) list(APPEND swiftlib_link_flags_all "-shared") # TODO: Instead of `lib${name}.so` find variable or target property which already have this value. list(APPEND swiftlib_link_flags_all "-Wl,-soname,lib${name}.so") + # Ensure compatibility with Android 15+ devices using 16KB memory pages. + list(APPEND swiftlib_link_flags_all "-Wl,-z,max-page-size=16384") endif() if (SWIFTLIB_BACK_DEPLOYMENT_LIBRARY) diff --git a/utils/swift_build_support/swift_build_support/targets.py b/utils/swift_build_support/swift_build_support/targets.py index 508d6204eaf68..31115a73e528a 100644 --- a/utils/swift_build_support/swift_build_support/targets.py +++ b/utils/swift_build_support/swift_build_support/targets.py @@ -164,7 +164,8 @@ def swift_flags(self, args): android_toolchain_path = self.ndk_toolchain_path(args) flags += '-sdk %s/sysroot ' % (android_toolchain_path) - flags += '-tools-directory %s/bin' % (android_toolchain_path) + flags += '-tools-directory %s/bin ' % (android_toolchain_path) + flags += '-Xclang-linker -Wl,-z,max-page-size=16384' return flags def cmake_options(self, args): From 8aeb9b2f88ef1e1f135c480122adbd155908d40b Mon Sep 17 00:00:00 2001 From: Finagolfin Date: Sat, 2 Aug 2025 19:42:34 +0530 Subject: [PATCH 2/4] Only bring over supporting change of `generate_linux_toolchain_file()` from fcc03898c, specifically the change that makes the following possible on linux: "* under Linux, do not cross compile LLVM when building for the host architecture -- that will ensure that the compiler-rt build will use the just built compiler and not the system one (which may not be new enough for this purpose);" --- .../swift_build_support/products/product.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/utils/swift_build_support/swift_build_support/products/product.py b/utils/swift_build_support/swift_build_support/products/product.py index 47e7ab79905a3..bf0f7fcf67181 100644 --- a/utils/swift_build_support/swift_build_support/products/product.py +++ b/utils/swift_build_support/swift_build_support/products/product.py @@ -389,7 +389,7 @@ def get_linux_target(self, platform, arch): sysroot_arch, vendor, abi = self.get_linux_target_components(arch) return '{}-{}-linux-{}'.format(sysroot_arch, vendor, abi) - def generate_linux_toolchain_file(self, platform, arch): + def generate_linux_toolchain_file(self, platform, arch, crosscompiling=True): """ Generates a new CMake tolchain file that specifies Linux as a target platform. @@ -402,8 +402,9 @@ def generate_linux_toolchain_file(self, platform, arch): toolchain_args = {} - toolchain_args['CMAKE_SYSTEM_NAME'] = 'Linux' - toolchain_args['CMAKE_SYSTEM_PROCESSOR'] = arch + if crosscompiling: + toolchain_args['CMAKE_SYSTEM_NAME'] = 'Linux' + toolchain_args['CMAKE_SYSTEM_PROCESSOR'] = arch # We only set the actual sysroot if we are actually cross # compiling. This is important since otherwise cmake seems to change the From 2c1ac1249550cadba5fa5f44d9597e0c2711e797 Mon Sep 17 00:00:00 2001 From: finagolfin Date: Sat, 2 Aug 2025 15:27:45 +0530 Subject: [PATCH 3/4] [build][android] Use a CMake toolchain file to cross-compile Testing (#83260) Also, disable a recently failing test for Android armv7. --- .../concat_string_literals.32.swift | 1 + .../swift_build_support/products/product.py | 53 +++++++++++++++---- .../products/swift_testing.py | 8 +++ .../swift_build_support/targets.py | 11 ++-- ...k_crosscompile_using_toolchain_always.test | 6 ++- 5 files changed, 65 insertions(+), 14 deletions(-) diff --git a/test/SILOptimizer/concat_string_literals.32.swift b/test/SILOptimizer/concat_string_literals.32.swift index 01390761c8c6d..d84c142397c99 100644 --- a/test/SILOptimizer/concat_string_literals.32.swift +++ b/test/SILOptimizer/concat_string_literals.32.swift @@ -4,6 +4,7 @@ // We have a separate test for 64-bit architectures. // REQUIRES: PTRSIZE=32 +// XFAIL: OS=linux-androideabi // NOTE: 25185.byteSwapped = 0x62 'a', 0x61 'b' // CHECK-LABEL: test_ascii_scalar_scalar2 diff --git a/utils/swift_build_support/swift_build_support/products/product.py b/utils/swift_build_support/swift_build_support/products/product.py index bf0f7fcf67181..6bd94c3cad836 100644 --- a/utils/swift_build_support/swift_build_support/products/product.py +++ b/utils/swift_build_support/swift_build_support/products/product.py @@ -403,18 +403,33 @@ def generate_linux_toolchain_file(self, platform, arch, crosscompiling=True): toolchain_args = {} if crosscompiling: - toolchain_args['CMAKE_SYSTEM_NAME'] = 'Linux' - toolchain_args['CMAKE_SYSTEM_PROCESSOR'] = arch + if platform == "linux": + toolchain_args['CMAKE_SYSTEM_NAME'] = 'Linux' + toolchain_args['CMAKE_SYSTEM_PROCESSOR'] = arch + elif platform == "android": + toolchain_args['CMAKE_SYSTEM_NAME'] = 'Android' + toolchain_args['CMAKE_SYSTEM_VERSION'] = self.args.android_api_level + toolchain_args['CMAKE_SYSTEM_PROCESSOR'] = arch if not arch == 'armv7' \ + else 'armv7-a' + toolchain_args['CMAKE_ANDROID_NDK'] = self.args.android_ndk + toolchain_args['CMAKE_FIND_ROOT_PATH'] = self.args.cross_compile_deps_path + # This is a workaround for a CMake 3.30+ bug, + # https://gitlab.kitware.com/cmake/cmake/-/issues/26154, and can + # be removed once that is fixed. + toolchain_args['CMAKE_SHARED_LINKER_FLAGS'] = '\"\"' # We only set the actual sysroot if we are actually cross # compiling. This is important since otherwise cmake seems to change the # RUNPATH to be a relative rather than an absolute path, breaking # certain cmark tests (and maybe others). - maybe_sysroot = self.get_linux_sysroot(platform, arch) - if maybe_sysroot is not None: - toolchain_args['CMAKE_SYSROOT'] = maybe_sysroot - - target = self.get_linux_target(platform, arch) + if platform == "linux": + maybe_sysroot = self.get_linux_sysroot(platform, arch) + if maybe_sysroot is not None: + toolchain_args['CMAKE_SYSROOT'] = maybe_sysroot + + target = self.get_linux_target(platform, arch) + elif platform == "android": + target = '%s-unknown-linux-android%s' % (arch, self.args.android_api_level) if self.toolchain.cc.endswith('clang'): toolchain_args['CMAKE_C_COMPILER_TARGET'] = target if self.toolchain.cxx.endswith('clang++'): @@ -460,10 +475,30 @@ def generate_toolchain_file_for_darwin_or_linux( platform, arch, macos_deployment_version=override_macos_deployment_version) self.cmake_options.define('CMAKE_TOOLCHAIN_FILE:PATH', toolchain_file) - elif platform == "linux": - toolchain_file = self.generate_linux_toolchain_file(platform, arch) + elif platform == "linux" or platform == "android": + # Always cross-compile for linux, but not on Android, as a native + # compile on Android does not use the NDK and its CMake config. + cross_compile = platform == "linux" or \ + self.is_cross_compile_target(host_target) + toolchain_file = self.generate_linux_toolchain_file(platform, arch, + cross_compile) self.cmake_options.define('CMAKE_TOOLCHAIN_FILE:PATH', toolchain_file) + if cross_compile and platform == "android": + resource_dir = None + # build-script-impl products build before the install and use + # the Swift stdlib from the compiler build directory instead, + # while products built even before that currently do not support + # cross-compiling Swift. + if not self.is_before_build_script_impl_product() and \ + not self.is_build_script_impl_product(): + install_path = self.host_install_destdir(host_target) + \ + self.args.install_prefix + resource_dir = '%s/lib/swift' % install_path + flags = targets.StdlibDeploymentTarget.get_target_for_name( + host_target).platform.swift_flags(self.args, resource_dir) + self.cmake_options.define('CMAKE_Swift_FLAGS', flags) + return toolchain_file def get_openbsd_toolchain_file(self): diff --git a/utils/swift_build_support/swift_build_support/products/swift_testing.py b/utils/swift_build_support/swift_build_support/products/swift_testing.py index 417056efdd0a2..177ea9f06232d 100644 --- a/utils/swift_build_support/swift_build_support/products/swift_testing.py +++ b/utils/swift_build_support/swift_build_support/products/swift_testing.py @@ -127,3 +127,11 @@ def install(self, host_target): install_prefix = install_destdir + self.args.install_prefix self.install_with_cmake(['install'], install_prefix) + + @classmethod + def is_build_script_impl_product(cls): + return False + + @classmethod + def is_before_build_script_impl_product(cls): + return False diff --git a/utils/swift_build_support/swift_build_support/targets.py b/utils/swift_build_support/swift_build_support/targets.py index 31115a73e528a..64a862eaf0e91 100644 --- a/utils/swift_build_support/swift_build_support/targets.py +++ b/utils/swift_build_support/swift_build_support/targets.py @@ -72,7 +72,7 @@ def contains(self, target_name): return True return False - def swift_flags(self, args): + def swift_flags(self, args, resource_path=None): """ Swift compiler flags for a platform, useful for cross-compiling """ @@ -154,12 +154,15 @@ def uses_host_tests(self): """ return True - def swift_flags(self, args): + def swift_flags(self, args, resource_path=None): flags = '-target %s-unknown-linux-android%s ' % (args.android_arch, args.android_api_level) - flags += '-resource-dir %s/swift-%s-%s/lib/swift ' % ( - args.build_root, self.name, args.android_arch) + if resource_path is not None: + flags += '-resource-dir %s ' % (resource_path) + else: + flags += '-resource-dir %s/swift-%s-%s/lib/swift ' % ( + args.build_root, self.name, args.android_arch) android_toolchain_path = self.ndk_toolchain_path(args) diff --git a/validation-test/BuildSystem/cmark_crosscompile_using_toolchain_always.test b/validation-test/BuildSystem/cmark_crosscompile_using_toolchain_always.test index 25dbee7709ea1..deea2cef7a19e 100644 --- a/validation-test/BuildSystem/cmark_crosscompile_using_toolchain_always.test +++ b/validation-test/BuildSystem/cmark_crosscompile_using_toolchain_always.test @@ -1,9 +1,13 @@ # REQUIRES: standalone_build -# REQUIRES: OS=macosx # RUN: %empty-directory(%t) # RUN: mkdir -p %t # RUN: SKIP_XCODE_VERSION_CHECK=1 SWIFT_BUILD_ROOT=%t %swift_src_root/utils/build-script --dry-run --install-all --cmake %cmake --skip-build-llvm --skip-build-swift 2>&1 | %FileCheck %s +# RUN: SKIP_XCODE_VERSION_CHECK=1 SWIFT_BUILD_ROOT=%t %swift_src_root/utils/build-script --dry-run --install-all --cmake %cmake --skip-build-llvm --skip-build-swift --cross-compile-hosts=android-aarch64 --skip-local-build --android --android-ndk %t/ndk/ 2>&1 | %FileCheck --check-prefix=ANDROID %s # CHECK: DRY_RUN! Writing Toolchain file to path:{{.*}}BuildScriptToolchain.cmake # CHECK: cmake {{.*}}-DCMAKE_TOOLCHAIN_FILE:PATH={{.*}}BuildScriptToolchain.cmake {{.*}}cmark + +# ANDROID: DRY_RUN! Writing Toolchain file to path:{{.*}}cmark-android-aarch64/BuildScriptToolchain.cmake +# ANDROID: cmake {{.*}}-DCMAKE_TOOLCHAIN_FILE:PATH={{.*}}cmark-android-aarch64/BuildScriptToolchain.cmake +# ANDROID: -DCMAKE_Swift_FLAGS=-target aarch64-unknown-linux-android From a77fcd5f832d7f7dee29f1888e7c30a4f64401b9 Mon Sep 17 00:00:00 2001 From: Finagolfin Date: Wed, 30 Jul 2025 16:41:16 +0530 Subject: [PATCH 4/4] [build] Make the new `--cross-compile-build-swift-tools` flag public This new flag makes it easy to build Swift cross-compilation toolchains, by disabling cross-compilation of all host tools, like the Swift compiler and various macros, building on prior pulls #38441 and #82163. Also, add two class methods to the Testing macros product so it works with #83260. --- .../build_swift/driver_arguments.py | 6 +++++ utils/build_swift/tests/expected_options.py | 2 ++ .../build_script_invocation.py | 2 ++ .../products/cmake_product.py | 6 ++--- .../swift_build_support/products/llvm.py | 12 +++++++--- .../products/swift_testing_macros.py | 23 +++++++++++++++++-- .../BuildSystem/android_cross_compile.test | 16 +++++++------ 7 files changed, 51 insertions(+), 16 deletions(-) diff --git a/utils/build_swift/build_swift/driver_arguments.py b/utils/build_swift/build_swift/driver_arguments.py index 2b7d6d0779911..e0d04d2282596 100644 --- a/utils/build_swift/build_swift/driver_arguments.py +++ b/utils/build_swift/build_swift/driver_arguments.py @@ -677,6 +677,12 @@ def create_argument_parser(): "for each cross-compiled toolchain's destdir, useful when building " "multiple toolchains and can be disabled if only cross-compiling one.") + option('--cross-compile-build-swift-tools', toggle_true, + default=True, + help="Cross-compile the Swift compiler, other host tools from the " + "compiler repository, and various macros for each listed " + "--cross-compile-hosts platform.") + option('--stdlib-deployment-targets', store, type=argparse.ShellSplitType(), default=None, diff --git a/utils/build_swift/tests/expected_options.py b/utils/build_swift/tests/expected_options.py index f8d1a3526ed76..b655d610923f7 100644 --- a/utils/build_swift/tests/expected_options.py +++ b/utils/build_swift/tests/expected_options.py @@ -155,6 +155,7 @@ 'compiler_vendor': defaults.COMPILER_VENDOR, 'coverage_db': None, 'cross_compile_append_host_target_to_destdir': True, + 'cross_compile_build_swift_tools': True, 'cross_compile_deps_path': None, 'cross_compile_hosts': [], 'infer_cross_compile_hosts_on_darwin': False, @@ -622,6 +623,7 @@ class BuildScriptImplOption(_BaseOption): EnableOption('--build-swift-clang-overlays'), EnableOption('--build-swift-remote-mirror'), EnableOption('--cross-compile-append-host-target-to-destdir'), + EnableOption('--cross-compile-build-swift-tools'), EnableOption('--color-in-tests'), EnableOption('--distcc'), EnableOption('--sccache'), diff --git a/utils/swift_build_support/swift_build_support/build_script_invocation.py b/utils/swift_build_support/swift_build_support/build_script_invocation.py index a0289515fd039..355d72fe21f4c 100644 --- a/utils/swift_build_support/swift_build_support/build_script_invocation.py +++ b/utils/swift_build_support/swift_build_support/build_script_invocation.py @@ -119,6 +119,8 @@ def convert_to_impl_arguments(self): "--cmake-generator", args.cmake_generator, "--cross-compile-append-host-target-to-destdir", str( args.cross_compile_append_host_target_to_destdir).lower(), + "--cross-compile-build-swift-tools", str( + args.cross_compile_build_swift_tools).lower(), "--build-jobs", str(args.build_jobs), "--lit-jobs", str(args.lit_jobs), "--common-cmake-options=%s" % ' '.join( diff --git a/utils/swift_build_support/swift_build_support/products/cmake_product.py b/utils/swift_build_support/swift_build_support/products/cmake_product.py index dc338334f284d..f1a9f4d28bfdc 100644 --- a/utils/swift_build_support/swift_build_support/products/cmake_product.py +++ b/utils/swift_build_support/swift_build_support/products/cmake_product.py @@ -24,7 +24,7 @@ def is_verbose(self): return self.args.verbose_build def build_with_cmake(self, build_targets, build_type, build_args, - prefer_native_toolchain=False): + prefer_native_toolchain=False, build_llvm=True): assert self.toolchain.cmake is not None cmake_build = [] _cmake = cmake.CMake(self.args, self.toolchain, @@ -71,9 +71,7 @@ def build_with_cmake(self, build_targets, build_type, build_args, env=env) is_llvm = self.product_name() == "llvm" - if (not is_llvm and not self.args.skip_build) or ( - is_llvm and self.args._build_llvm - ): + if (not is_llvm and not self.args.skip_build) or (is_llvm and build_llvm): cmake_opts = [self.build_dir, "--config", build_type] shell.call( diff --git a/utils/swift_build_support/swift_build_support/products/llvm.py b/utils/swift_build_support/swift_build_support/products/llvm.py index ffae1d6670200..72e400c55be35 100644 --- a/utils/swift_build_support/swift_build_support/products/llvm.py +++ b/utils/swift_build_support/swift_build_support/products/llvm.py @@ -249,10 +249,13 @@ def build(self, host_target): # space/time efficient than -g on that platform. llvm_cmake_options.define('LLVM_USE_SPLIT_DWARF:BOOL', 'YES') - if not self.args._build_llvm: + build = True + if not self.args._build_llvm or (not self.args.cross_compile_build_swift_tools + and self.is_cross_compile_target(host_target)): # Indicating we don't want to build LLVM at all should # override everything. build_targets = [] + build = False elif self.args.skip_build or not self.args.build_llvm: # We can't skip the build completely because the standalone # build of Swift depends on these. @@ -399,7 +402,8 @@ def build(self, host_target): self._handle_cxx_headers(host_target, platform) - self.build_with_cmake(build_targets, self.args.llvm_build_variant, []) + self.build_with_cmake(build_targets, self.args.llvm_build_variant, [], + build_llvm=build) # copy over the compiler-rt builtins for iOS/tvOS/watchOS to ensure # that Swift's stdlib can use compiler-rt builtins when targeting @@ -484,7 +488,9 @@ def should_install(self, host_target): Whether or not this product should be installed with the given arguments. """ - return self.args.install_llvm + return self.args.install_llvm and ( + self.args.cross_compile_build_swift_tools or + not self.is_cross_compile_target(host_target)) def install(self, host_target): """ diff --git a/utils/swift_build_support/swift_build_support/products/swift_testing_macros.py b/utils/swift_build_support/swift_build_support/products/swift_testing_macros.py index ddb3b553de734..d127424709c57 100644 --- a/utils/swift_build_support/swift_build_support/products/swift_testing_macros.py +++ b/utils/swift_build_support/swift_build_support/products/swift_testing_macros.py @@ -42,13 +42,24 @@ def should_clean(self, host_target): return True def should_build(self, host_target): - return True + build_macros = not self.is_cross_compile_target(host_target) or \ + self.args.cross_compile_build_swift_tools + if not build_macros: + print("Skipping building Testing Macros for %s, because the host tools " + "are not being built" % host_target) + return build_macros def should_test(self, host_target): return False def should_install(self, host_target): - return self.args.install_swift_testing_macros + install_macros = self.args.install_swift_testing_macros and \ + (not self.is_cross_compile_target(host_target) or + self.args.cross_compile_build_swift_tools) + if self.args.install_swift_testing_macros and not install_macros: + print("Skipping installing Testing Macros for %s, because the host tools " + "are not being built" % host_target) + return install_macros def _cmake_product(self, host_target): build_root = os.path.dirname(self.build_dir) @@ -121,3 +132,11 @@ def install(self, host_target): install_prefix = install_destdir + self.args.install_prefix self.install_with_cmake(['install'], install_prefix) + + @classmethod + def is_build_script_impl_product(cls): + return False + + @classmethod + def is_before_build_script_impl_product(cls): + return False diff --git a/validation-test/BuildSystem/android_cross_compile.test b/validation-test/BuildSystem/android_cross_compile.test index 8396568dce764..a371bcc47722b 100644 --- a/validation-test/BuildSystem/android_cross_compile.test +++ b/validation-test/BuildSystem/android_cross_compile.test @@ -1,11 +1,13 @@ # REQUIRES: standalone_build -# UNSUPPORTED: OS=macosx -# UNSUPPORTED: OS=ios -# UNSUPPORTED: OS=tvos -# UNSUPPORTED: OS=watchos # RUN: %empty-directory(%t) -# RUN: SKIP_XCODE_VERSION_CHECK=1 SWIFT_BUILD_ROOT=%t %swift_src_root/utils/build-script --dry-run --cmake %cmake --libdispatch --cross-compile-hosts=android-aarch64 --skip-local-build --android --android-ndk %t/ndk/ --android-arch aarch64 2>&1 | %FileCheck %s +# RUN: SKIP_XCODE_VERSION_CHECK=1 SWIFT_BUILD_ROOT=%t %swift_src_root/utils/build-script --dry-run --cmake %cmake --swift-testing --swift-testing-macros --install-swift-testing-macros --install-llvm --cross-compile-hosts=android-aarch64 --cross-compile-build-swift-tools=False --android --android-ndk %t/ndk/ --android-arch aarch64 2>&1 | %FileCheck %s -# CHECK: -DCMAKE_Swift_FLAGS{{.*}}-target {{.*}}unknown-linux-android{{.*}} -sdk -# CHECK: -DCMAKE_SYSTEM_NAME=Android {{.*}} -DCMAKE_ANDROID_NDK +# CHECK: pushd {{.*}}/llvm-android-aarch64 +# CHECK-NOT: cmake --build {{.*}}/llvm-android-aarch64 --config +# CHECK-NOT: cmake --build {{.*}}/llvm-android-aarch64 {{.*}} install-llvm +# CHECK: cmake {{.*}}-DSWIFT_INCLUDE_TOOLS:BOOL=FALSE{{.*}}/swift +# CHECK: Skipping building Testing Macros for android-aarch64, because the host tools are not being built +# CHECK: Skipping installing Testing Macros for android-aarch64, because the host tools are not being built +# CHECK: cmake {{.*}}-DCMAKE_TOOLCHAIN_FILE:PATH={{.*}}swifttesting-android-aarch64/BuildScriptToolchain.cmake +# CHECK: -DCMAKE_Swift_FLAGS=-target aarch64-unknown-linux-android{{.*}} -sdk