diff --git a/.bazelci/presubmit.yml b/.bazelci/presubmit.yml
index 5626e8a51..0e6ab7ae3 100644
--- a/.bazelci/presubmit.yml
+++ b/.bazelci/presubmit.yml
@@ -39,12 +39,16 @@ x_defaults:
- "-//test:output_file_map_default"
windows_common: &windows_common
platform: windows
- build_flags:
- # Override 'sandboxed' strategy set in .bazelrc because it's not
- # available on Windows
- - "--strategy=SwiftCompile="
build_targets:
- "//tools/..."
+ # Cross-platform Swift examples that exercise the Windows host toolchain:
+ # a `swift_binary` executable and a `linkshared` Windows DLL.
+ - "//examples/xplatform/hello_world"
+ - "//examples/xplatform/shared_library"
+ test_targets:
+ # Exercises XCTest discovery, the test runner, and `swift_test` execution
+ # on Windows.
+ - "//examples/xplatform/xctest"
tasks:
macos_latest:
@@ -62,6 +66,22 @@ tasks:
bazel: last_green
<<: *mac_common
+ macos_cross_compilation:
+ name: "Cross-compilation (wasm + Android)"
+ platform: macos_arm64
+ xcode_version: "26.2"
+ bazel: latest
+ # The //examples/cross_compilation targets are tagged `manual` (they fetch
+ # the Swift SDK bundles and, for Android, the NDK), so they are excluded
+ # from the `//examples/...` wildcard the other tasks build. List them
+ # explicitly here so the Swift-SDK cross-compilation toolchains are exercised
+ # in CI. Build-only: the trivial reactor exercises the link path, while
+ # runtime behavior is covered downstream by real consumers.
+ build_targets:
+ - "//examples/cross_compilation:Reactor.wasm"
+ - "//examples/cross_compilation:web_app"
+ - "//examples/cross_compilation:libSwiftJNI.so"
+
macos_latest_shell_scripts:
name: "macOS shell tests"
platform: macos_arm64
@@ -97,11 +117,20 @@ tasks:
- "curl https://download.swift.org/swift-${SWIFT_VERSION}-release/ubuntu2204/swift-${SWIFT_VERSION}-RELEASE/swift-${SWIFT_VERSION}-RELEASE-ubuntu22.04.tar.gz | tar xvz --strip-components=1 -C $SWIFT_HOME"
<<: *linux_common
- # TODO: re-enable when Windows in Bazel CI is properly configured for Swift.
- # windows_last_green:
- # name: "Last Green Bazel"
- # bazel: last_green
- # <<: *windows_common
+ windows:
+ name: "Current LTS"
+ bazel: latest
+ environment:
+ SWIFT_VERSION: "6.0.3"
+ # Install the Swift for Windows toolchain before building. The swift.org
+ # installer is a self-extracting bundle that supports an unattended install
+ # and sets `SDKROOT`/`Path` machine-wide, which the Swift autoconfiguration
+ # repository rule reads to discover the toolchain. Visual Studio (MSVC) is
+ # already present on the Bazel CI Windows image and is used for linking.
+ batch_commands:
+ - "curl -sSL -o %TEMP%\\swift-installer.exe https://download.swift.org/swift-%SWIFT_VERSION%-release/windows10/swift-%SWIFT_VERSION%-RELEASE/swift-%SWIFT_VERSION%-RELEASE-windows10.exe"
+ - "%TEMP%\\swift-installer.exe -q"
+ <<: *windows_common
doc_tests:
name: "Doc tests"
diff --git a/.bazelrc b/.bazelrc
index 0f9e3bf81..509ba4375 100644
--- a/.bazelrc
+++ b/.bazelrc
@@ -26,6 +26,12 @@ common:linux --repo_env=CC=clang
build:linux --cxxopt='-std=c++17' --host_cxxopt='-std=c++17'
common:linux --//test:apple_build_tests=False
+# Worker sandboxing copies the worker into a sandbox exec root and cleans it
+# between invocations. On Windows a running/recently-run executable cannot be
+# deleted, so that cleanup fails with "Permission denied". Run Swift workers
+# unsandboxed on Windows.
+build:windows --noworker_sandboxing
+
# This C2K warning causes zlib to fail to compile.
# There is an open issue about it on the zlib repository here:
# https://github.com/madler/zlib/issues/633
diff --git a/MODULE.bazel b/MODULE.bazel
index 30e9a8029..ceb82f839 100644
--- a/MODULE.bazel
+++ b/MODULE.bazel
@@ -82,7 +82,13 @@ use_repo(system_sdk, "system_sdk")
swift = use_extension("//swift:extensions.bzl", "swift", dev_dependency = True)
swift.toolchain(
name = "swift_toolchain",
- swift_version = "6.3",
+ swift_version = "6.3.2",
+)
+swift.wasm_sdk(
+ toolchain_name = "swift_toolchain",
+)
+swift.android_sdk(
+ toolchain_name = "swift_toolchain",
)
use_repo(
swift,
@@ -110,6 +116,35 @@ register_toolchains(
dev_dependency = True,
)
+register_toolchains(
+ # Swift SDK toolchains for cross-compiling to WebAssembly and Android;
+ # used by //examples/cross_compilation. As with the embedded toolchains
+ # above, we register only the host platforms used by CI rather than
+ # `@swift_toolchain//:all`, because rules_swift cannot yet auto-select a
+ # Linux distribution and `:all` would make the host/exec toolchain
+ # ambiguous across distros. A consumer that builds on a single host
+ # platform can simply register `@swift_toolchain//:all`.
+ "@swift_toolchain//:cc_toolchain_android_aarch64_ubuntu22.04",
+ "@swift_toolchain//:cc_toolchain_android_aarch64_ubuntu22.04-aarch64",
+ "@swift_toolchain//:cc_toolchain_android_aarch64_xcode",
+ "@swift_toolchain//:cc_toolchain_android_x86_64_ubuntu22.04",
+ "@swift_toolchain//:cc_toolchain_android_x86_64_ubuntu22.04-aarch64",
+ "@swift_toolchain//:cc_toolchain_android_x86_64_xcode",
+ "@swift_toolchain//:cc_toolchain_wasm32_ubuntu22.04",
+ "@swift_toolchain//:cc_toolchain_wasm32_ubuntu22.04-aarch64",
+ "@swift_toolchain//:cc_toolchain_wasm32_xcode",
+ "@swift_toolchain//:swift_toolchain_android_aarch64_ubuntu22.04",
+ "@swift_toolchain//:swift_toolchain_android_aarch64_ubuntu22.04-aarch64",
+ "@swift_toolchain//:swift_toolchain_android_aarch64_xcode",
+ "@swift_toolchain//:swift_toolchain_android_x86_64_ubuntu22.04",
+ "@swift_toolchain//:swift_toolchain_android_x86_64_ubuntu22.04-aarch64",
+ "@swift_toolchain//:swift_toolchain_android_x86_64_xcode",
+ "@swift_toolchain//:swift_toolchain_wasm32_ubuntu22.04",
+ "@swift_toolchain//:swift_toolchain_wasm32_ubuntu22.04-aarch64",
+ "@swift_toolchain//:swift_toolchain_wasm32_xcode",
+ dev_dependency = True,
+)
+
# Dev dependencies
bazel_dep(name = "bazel_skylib_gazelle_plugin", version = "1.5.0", dev_dependency = True)
bazel_dep(name = "gazelle", version = "0.46.0", dev_dependency = True)
diff --git a/doc/rules.md b/doc/rules.md
index 08c42c885..2b687046b 100755
--- a/doc/rules.md
+++ b/doc/rules.md
@@ -41,7 +41,7 @@ On this page:
swift_binary(name, deps, srcs, data, additional_linker_inputs, copts, defines, env, linkopts,
- malloc, module_name, package_name, plugins, stamp, swiftc_inputs)
+ linkshared, malloc, module_name, package_name, plugins, stamp, swiftc_inputs)
Compiles and links Swift code into an executable binary.
@@ -58,6 +58,9 @@ please use one of the platform-specific application rules in
[rules_apple](https://github.com/bazelbuild/rules_apple) instead of
`swift_binary`.
+Setting `linkshared = True` links a shared library or (on WebAssembly) a
+reactor module instead of an executable; see the `linkshared` attribute.
+
**ATTRIBUTES**
@@ -72,6 +75,7 @@ please use one of the platform-specific application rules in
| defines | A list of defines to add to the compilation command line.
Note that unlike C-family languages, Swift defines do not have values; they are simply identifiers that are either defined or undefined. So strings in this list should be simple identifiers, **not** `name=value` pairs.
Each string is prepended with `-D` and added to the command line. Unlike `copts`, these flags are added for the target and every target that depends on it, so use this attribute with caution. It is preferred that you add defines directly to `copts`, only using this feature in the rare case that a library needs to propagate a symbol up to those that depend on it. | List of strings | optional | `[]` |
| env | Specifies additional environment variables to set when the test is executed by `bazel run` or `bazel test`.
The values of these environment variables are subject to `$(location)` and "Make variable" substitution.
NOTE: The environment variables are not set when you run the target outside of Bazel (for example, by manually executing the binary in `bazel-bin/`). | Dictionary: String -> String | optional | `{}` |
| linkopts | Additional linker options that should be passed to `clang`. These strings are subject to `$(location ...)` expansion. | List of strings | optional | `[]` |
+| linkshared | If `True`, link the target as a shared library / loadable module instead of an executable, similar to `cc_binary`'s `linkshared`. The binary has no `main` entry point and the renamed-entry-point machinery is disabled.
On most platforms this produces a dynamic library named `lib.so` (`.dylib` on Apple platforms) suitable for loading with `dlopen` / `System.loadLibrary` (e.g. an Android JNI library; export functions with `@_cdecl`).
When targeting WebAssembly it instead produces a "reactor" module (`.wasm`, linked with `-mexec-model=reactor`): the module has no `_start`, runs its initializers via the exported `_initialize`, and exposes the functions a host instantiates and calls. Force-export those functions by passing `-Xlinker --export=` (or `-Wl,--export=`) flags in `linkopts`. | Boolean | optional | `False` |
| malloc | Override the default dependency on `malloc`.
By default, Swift binaries are linked against `@bazel_tools//tools/cpp:malloc"`, which is an empty library and the resulting binary will use libc's `malloc`. This label must refer to a `cc_library` rule. | Label | optional | `"@bazel_tools//tools/cpp:malloc"` |
| module_name | The name of the Swift module being built.
If left unspecified, the module name will be computed based on the target's build label, by stripping the leading `//` and replacing `/`, `:`, and other non-identifier characters with underscores. | String | optional | `""` |
| package_name | The semantic package of the Swift target being built. Targets with the same package_name can access APIs using the 'package' access control modifier in Swift 5.9+. | String | optional | `""` |
diff --git a/doc/standalone_toolchain.md b/doc/standalone_toolchain.md
index 7e96e51cf..413336918 100644
--- a/doc/standalone_toolchain.md
+++ b/doc/standalone_toolchain.md
@@ -146,6 +146,143 @@ bazel run @rules_swift//tools/swift-releases -- list \
main-snapshot-2024-08-01 --platform xcode --platform ubuntu22.04
```
+## Cross-compiling with Swift SDKs (WebAssembly and Android)
+
+swift.org publishes "Swift SDK" artifact bundles (the bundles consumed by
+`swift sdk install`) that let the host compiler cross-compile for platforms
+it cannot target by itself. The `swift` extension can download these and
+define matching Swift and C/C++ toolchains, so that plain `swift_library`
+and `swift_binary` targets build for those platforms under `--platforms`.
+
+Add the `wasm_sdk` and/or `android_sdk` tags, referencing the `toolchain`
+tag by name (the Swift module format is not stable across compiler
+versions, so the SDK is always downloaded for exactly the toolchain's
+version):
+
+```bzl
+swift.toolchain(
+ name = "swift_toolchain",
+ swift_version = "6.3.2",
+)
+
+swift.wasm_sdk(
+ toolchain_name = "swift_toolchain",
+)
+
+swift.android_sdk(
+ toolchain_name = "swift_toolchain",
+ # api_level = 28, # the default
+)
+
+register_toolchains(
+ # WebAssembly (wasm32-unknown-wasip1), per host platform you build on.
+ "@swift_toolchain//:swift_toolchain_wasm32_xcode",
+ "@swift_toolchain//:cc_toolchain_wasm32_xcode",
+ # Android, per architecture and host platform.
+ "@swift_toolchain//:swift_toolchain_android_aarch64_xcode",
+ "@swift_toolchain//:cc_toolchain_android_aarch64_xcode",
+ "@swift_toolchain//:swift_toolchain_android_x86_64_xcode",
+ "@swift_toolchain//:cc_toolchain_android_x86_64_xcode",
+)
+```
+
+If you build on a single host platform, you can register everything the
+extension generates (standalone, embedded, and Swift-SDK toolchains) in one
+line instead of listing the matrix:
+
+```bzl
+register_toolchains("@swift_toolchain//:all")
+```
+
+Avoid `:all` when you configure multiple Linux distributions, for the same
+reason the standalone host toolchains are registered explicitly: rules_swift
+cannot yet auto-select a distribution, so `:all` would make the host/exec
+toolchain ambiguous across them.
+
+Then build with a platform carrying the matching constraints, for example:
+
+```bzl
+platform(
+ name = "wasm32-wasip1",
+ constraint_values = [
+ "@platforms//cpu:wasm32",
+ "@platforms//os:wasi",
+ ],
+)
+
+platform(
+ name = "android-aarch64",
+ constraint_values = [
+ "@platforms//cpu:aarch64",
+ "@platforms//os:android",
+ ],
+)
+```
+
+```sh
+bazel build //my:binary --platforms=//:wasm32-wasip1
+```
+
+See `examples/cross_compilation` for a complete example, including building
+through a platform transition.
+
+### Shared libraries and WebAssembly reactors
+
+A plain `swift_binary` links an executable: a WASI *command* module for
+WebAssembly, or an ordinary executable for Android. To produce the artifacts
+those ecosystems actually load, set `linkshared = True`:
+
+* **Android (JNI):** produces `lib.so`, loadable with
+ `System.loadLibrary`. Export functions with `@_cdecl`; the Android Swift
+ SDK's `Android` module provides the JNI types, so the entry points can be
+ written entirely in Swift.
+* **WebAssembly (reactor):** produces `.wasm` linked with
+ `-mexec-model=reactor` — no `main`, initializers run via the exported
+ `_initialize`, and the module exposes the functions a JS host calls. Keep
+ each exported function with `linkopts = ["-Xlinker", "--export="]`.
+
+A `swift_binary(linkshared = True)` may depend on ordinary `swift_library`
+targets (and link them statically), so the platform-specific entry point and
+the shared business logic stay in separate, normal libraries.
+`examples/cross_compilation` builds a reactor and an Android JNI library this
+way, both depending on the same `Greeter` `swift_library`, and
+`examples/cross_compilation/android_app` shows the Kotlin app that loads the
+JNI library.
+
+Details worth knowing:
+
+* The Swift standard library is linked statically from the SDK, matching
+ the behavior of `swiftc` with these SDKs. WebAssembly binaries are
+ self-contained `wasm32-wasip1` modules (runnable with `wasmtime` et al.).
+* Android binaries link against the NDK's `libc++_shared.so`, which must be
+ packaged with the application. Reference it host-independently as
+ `@//:libcxx_shared_` (e.g.
+ `@swift_toolchain//:libcxx_shared_aarch64`); the alias selects the NDK for
+ the build host automatically.
+* The `android_sdk` tag downloads the Android NDK (for its sysroot and
+ clang) in addition to the Swift SDK. The NDK is only fetched when an
+ Android target is actually built; WebAssembly-only builds do not download
+ it. The NDK version and checksums can be overridden with the
+ `ndk_version` and `ndk_sha256s` attributes.
+* As with toolchains, checksums for the SDK bundles are bundled for a
+ curated list of releases (see
+ `swift/internal/extensions/swift_sdk_releases.bzl`); for other releases,
+ pass `sha256` explicitly.
+
+### Coexistence with `rules_apple`
+
+A common setup cross-compiles to WebAssembly/Android *and* builds the same
+app's Apple targets with `rules_apple`. The two resolve together cleanly: this
+line of `rules_swift` is `compatibility_level = 3` (the same as released
+`rules_swift` 3.x), so a current `rules_apple` release — 4.5.3 or the 5.0.0
+release candidates, both built against `rules_swift` 3.x — works alongside it.
+Bazel's version resolution keeps the higher of each shared transitive
+dependency (`apple_support`, `rules_cc`), which are backward compatible, so no
+extra pinning is required. If you are tracking this work from a fork via
+`git_override`, depend on such a `rules_apple` release; once the change lands
+in a published `rules_swift` that `rules_apple` itself depends on, the
+`git_override` is no longer needed.
+
## Using the extension from a non-root module
The extension is intended for the root module — it fails if a non-root
diff --git a/examples/cross_compilation/BUILD.bazel b/examples/cross_compilation/BUILD.bazel
new file mode 100644
index 000000000..d12e5c659
--- /dev/null
+++ b/examples/cross_compilation/BUILD.bazel
@@ -0,0 +1,102 @@
+load("//examples/embedded:transition.bzl", "transition_binary")
+load("//swift:swift.bzl", "swift_binary", "swift_library")
+
+package(default_visibility = ["//visibility:public"])
+
+# Platforms covered by the Swift SDK toolchains registered by this repository's
+# dev `MODULE.bazel` (via the `swift` extension's `wasm_sdk`/`android_sdk`
+# tags). Build the targets below with `--platforms` set to one of these.
+platform(
+ name = "wasm32-wasip1",
+ constraint_values = [
+ "@platforms//cpu:wasm32",
+ "@platforms//os:wasi",
+ ],
+)
+
+platform(
+ name = "android-aarch64",
+ constraint_values = [
+ "@platforms//cpu:aarch64",
+ "@platforms//os:android",
+ ],
+)
+
+# A plain library dependency, compiled for whichever platform depends on it.
+# Both platform-specific entry points below call into it, demonstrating that a
+# normal `swift_library` is reused unchanged across targets.
+swift_library(
+ name = "Greeter",
+ srcs = ["Sources/Greeter/Greeter.swift"],
+ module_name = "Greeter",
+ tags = ["manual"],
+)
+
+# ---------------------------------------------------------------------------
+# WebAssembly: a reactor module (no `main`; exports functions for a JS host).
+# ---------------------------------------------------------------------------
+
+swift_binary(
+ name = "Reactor",
+ srcs = ["Sources/Reactor/Reactor.swift"],
+ # Keep the exported functions in the linked module. `@_cdecl` names them;
+ # wasm-ld still needs an explicit `--export=` to retain each one.
+ linkopts = [
+ "-Xlinker",
+ "--export=greeting_into",
+ "-Xlinker",
+ "--export=greeting_length",
+ ],
+ linkshared = True,
+ tags = ["manual"],
+ deps = [":Greeter"],
+)
+
+# Build `:Reactor` for the wasm platform. Consumers can instead set
+# `--platforms=//examples/cross_compilation:wasm32-wasip1` on the command line.
+transition_binary(
+ name = "Reactor.wasm",
+ binary = ":Reactor",
+ platform = ":wasm32-wasip1",
+)
+
+# A static web app embedding the reactor: `index.html` + `Reactor.wasm` in one
+# directory. Serve it (e.g. `python3 -m http.server -d
+# bazel-bin/examples/cross_compilation/web_app`) and open it — the page calls
+# the reactor's exports from JavaScript and shows the greeting Swift produced.
+# `web/verify.mjs` does the same headlessly under Node. See `web/README.md`.
+genrule(
+ name = "web_app",
+ srcs = [
+ "web/index.html",
+ ":Reactor.wasm",
+ ],
+ outs = [
+ "web_app/index.html",
+ "web_app/Reactor.wasm",
+ ],
+ cmd = "cp $(location web/index.html) $(RULEDIR)/web_app/index.html && " +
+ "cp $(location :Reactor.wasm) $(RULEDIR)/web_app/Reactor.wasm",
+ tags = ["manual"],
+)
+
+# ---------------------------------------------------------------------------
+# Android: a JNI shared library, loaded by Kotlin via `System.loadLibrary`.
+# ---------------------------------------------------------------------------
+
+# `linkshared` produces `libSwiftJNI.so`. The JNI entry point is written in
+# Swift (it `import`s the SDK's `Android` module for the JNI types) and calls
+# into the `Greeter` library. See `android_app/` for the APK that loads it.
+swift_binary(
+ name = "SwiftJNI",
+ srcs = ["Sources/SwiftJNI/SwiftJNI.swift"],
+ linkshared = True,
+ tags = ["manual"],
+ deps = [":Greeter"],
+)
+
+transition_binary(
+ name = "libSwiftJNI.so",
+ binary = ":SwiftJNI",
+ platform = ":android-aarch64",
+)
diff --git a/examples/cross_compilation/README.md b/examples/cross_compilation/README.md
new file mode 100644
index 000000000..a22b4dd06
--- /dev/null
+++ b/examples/cross_compilation/README.md
@@ -0,0 +1,45 @@
+# Cross-compilation example (WebAssembly + Android)
+
+Builds plain `swift_library` / `swift_binary` targets for non-host platforms
+using the Swift SDK toolchains registered by this repository's `MODULE.bazel`
+(via the `swift` extension's `wasm_sdk` and `android_sdk` tags). See
+`doc/standalone_toolchain.md` for the toolchain setup.
+
+All targets are tagged `manual` because they download the Swift SDK bundles
+(and, for Android, the NDK) and require the cross toolchains to be registered.
+
+## Targets
+
+| Target | Output | Demonstrates |
+|---|---|---|
+| `:Greeter` | `.swiftmodule` + `.a` | A normal `swift_library` reused by both entry points below |
+| `:Reactor.wasm` | `Reactor.wasm` | A WebAssembly **reactor** (`swift_binary(linkshared)`), no `main`, with exported functions |
+| `:web_app` | `web_app/` | A static site embedding `Reactor.wasm`, driven from JS — see [`web/README.md`](web/README.md) |
+| `:libSwiftJNI.so` | `libSwiftJNI.so` | An Android **JNI shared library** (`swift_binary(linkshared)`) that calls `:Greeter` |
+
+```sh
+# WebAssembly reactor (runnable with wasmtime):
+bazel build //examples/cross_compilation:Reactor.wasm
+wasmtime run --invoke greeting_length \
+ bazel-bin/examples/cross_compilation/Reactor.wasm
+
+# WebAssembly in a browser: a static site that calls the reactor from JS.
+bazel build //examples/cross_compilation:web_app
+python3 -m http.server -d bazel-bin/examples/cross_compilation/web_app 8000
+# …then open http://localhost:8000 (see web/README.md)
+
+# Android JNI shared library:
+bazel build //examples/cross_compilation:libSwiftJNI.so
+```
+
+Each `transition_binary` target builds its `swift_binary` under the matching
+platform (`:wasm32-wasip1` / `:android-aarch64`); you can equivalently pass
+`--platforms=//examples/cross_compilation:wasm32-wasip1` on the command line.
+
+## Android app
+
+`android_app/` contains the Kotlin app (and a documented packaging recipe) that
+loads `libSwiftJNI.so` and calls into it, completing the
+Kotlin → Swift (JNI `.so`) → Swift library chain. Packaging an APK uses
+`rules_android` + an Android SDK in the consuming module; see
+`android_app/README.md`.
diff --git a/examples/cross_compilation/Sources/Greeter/Greeter.swift b/examples/cross_compilation/Sources/Greeter/Greeter.swift
new file mode 100644
index 000000000..e0ef934c8
--- /dev/null
+++ b/examples/cross_compilation/Sources/Greeter/Greeter.swift
@@ -0,0 +1,15 @@
+/// A plain `swift_library` used as a normal dependency of the
+/// platform-specific entry points (the Android JNI shared library and the
+/// WebAssembly reactor). Nothing in here is platform-specific; it is compiled
+/// for whichever platform the depending target is built for.
+public struct Greeter {
+ private let subject: String
+
+ public init(subject: String) {
+ self.subject = subject
+ }
+
+ public func greeting() -> String {
+ return "Hello from Swift, \(subject)!"
+ }
+}
diff --git a/examples/cross_compilation/Sources/Reactor/Reactor.swift b/examples/cross_compilation/Sources/Reactor/Reactor.swift
new file mode 100644
index 000000000..c9f0bbba5
--- /dev/null
+++ b/examples/cross_compilation/Sources/Reactor/Reactor.swift
@@ -0,0 +1,27 @@
+import Greeter
+
+// A WebAssembly "reactor" module: it has no `main`/entry point. Instead it
+// exports functions that a host (e.g. JavaScript via `WebAssembly.instantiate`)
+// calls after instantiation. The `@_cdecl` attribute gives each function a
+// plain C name; the linker still needs `--export=` (passed via `linkopts` in
+// the BUILD file) to keep them in the final module.
+
+/// Writes the greeting into `buffer` (NUL-terminated, truncated to `capacity`)
+/// and returns the number of bytes written, excluding the terminator.
+@_cdecl("greeting_into")
+public func greeting_into(_ buffer: UnsafeMutablePointer, _ capacity: Int32) -> Int32 {
+ let message = Greeter(subject: "WebAssembly").greeting()
+ let bytes = Array(message.utf8)
+ let limit = min(bytes.count, Int(capacity) - 1)
+ for index in 0 ..< limit {
+ buffer[index] = CChar(bitPattern: bytes[index])
+ }
+ buffer[limit] = 0
+ return Int32(limit)
+}
+
+/// Returns the length the greeting would occupy (so the host can size a buffer).
+@_cdecl("greeting_length")
+public func greeting_length() -> Int32 {
+ return Int32(Greeter(subject: "WebAssembly").greeting().utf8.count)
+}
diff --git a/examples/cross_compilation/Sources/SwiftJNI/SwiftJNI.swift b/examples/cross_compilation/Sources/SwiftJNI/SwiftJNI.swift
new file mode 100644
index 000000000..4b3336bd5
--- /dev/null
+++ b/examples/cross_compilation/Sources/SwiftJNI/SwiftJNI.swift
@@ -0,0 +1,24 @@
+import Android
+import Greeter
+
+// The JNI entry point, written entirely in Swift. `import Android` provides the
+// JNI types (`JNIEnv`, `jclass`, `jstring`, ...) from the Android Swift SDK, so
+// no C shim is needed. `@_cdecl` gives the function the exact symbol name JNI
+// looks up: `Java___`, with `.`/`_` escaped per the JNI
+// spec. It is the Kotlin-callable native implementation of:
+//
+// package com.example.swiftjni
+// class NativeBridge { external fun greetingFromSwift(): String }
+//
+// and it delegates to the `Greeter` `swift_library` (a normal dependency),
+// completing the Kotlin -> Swift (JNI .so) -> Swift library call chain.
+@_cdecl("Java_com_example_swiftjni_NativeBridge_greetingFromSwift")
+public func greetingFromSwift(
+ _ env: UnsafeMutablePointer,
+ _ clazz: jclass
+) -> jstring? {
+ let message = Greeter(subject: "Android").greeting()
+ return message.withCString { cString in
+ env.pointee!.pointee.NewStringUTF(env, cString)
+ }
+}
diff --git a/examples/cross_compilation/android_app/AndroidManifest.xml b/examples/cross_compilation/android_app/AndroidManifest.xml
new file mode 100644
index 000000000..9fa94dbfb
--- /dev/null
+++ b/examples/cross_compilation/android_app/AndroidManifest.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/cross_compilation/android_app/README.md b/examples/cross_compilation/android_app/README.md
new file mode 100644
index 000000000..fafe64f0d
--- /dev/null
+++ b/examples/cross_compilation/android_app/README.md
@@ -0,0 +1,120 @@
+# Android app: Kotlin → Swift JNI `.so` → Swift library
+
+This directory shows the application half of the
+Kotlin → Swift → Swift call chain that `rules_swift` enables:
+
+```
+MainActivity.kt ──► NativeBridge.greetingFromSwift() (Kotlin)
+ │ JNI (System.loadLibrary("SwiftJNI"))
+ ▼
+ //examples/cross_compilation:SwiftJNI → libSwiftJNI.so (swift_binary, linkshared)
+ @_cdecl("Java_..._greetingFromSwift") in SwiftJNI.swift (Swift)
+ │
+ ▼
+ //examples/cross_compilation:Greeter (swift_library)
+```
+
+The `rules_swift` side — building `libSwiftJNI.so` from a `swift_binary` that
+depends on a normal `swift_library`, and exposing the NDK's `libc++_shared.so`
+at a host-independent label — is fully implemented and exercised by
+`//examples/cross_compilation:libSwiftJNI.so`.
+
+Packaging that `.so` into an APK is the job of the Android rules
+(`rules_android` + `rules_kotlin`) and a local Android SDK. Those are heavy
+dependencies (`rules_android` pulls in `rules_go`, `gazelle`, Robolectric, and a
+conflicting protobuf), so `rules_swift` deliberately does **not** depend on them;
+the APK target lives in *your* module instead. The sources in this directory
+(`NativeBridge.kt`, `MainActivity.kt`, `AndroidManifest.xml`) are complete and
+ready to drop into such a module.
+
+## `MODULE.bazel` (in your app's module)
+
+```starlark
+bazel_dep(name = "rules_swift", version = "...") # or git_override to this fork
+bazel_dep(name = "rules_android", version = "0.7.3")
+bazel_dep(name = "rules_kotlin", version = "2.3.20")
+
+swift = use_extension("@rules_swift//swift:extensions.bzl", "swift")
+swift.toolchain(name = "swift_toolchain", swift_version = "6.3.2")
+swift.android_sdk(toolchain_name = "swift_toolchain")
+use_repo(swift, "swift_toolchain")
+
+# One line registers every Swift SDK toolchain (and the standalone host ones).
+register_toolchains("@swift_toolchain//:all")
+
+android_sdk = use_extension("@rules_android//rules/android_sdk_repository:rule.bzl", "android_sdk_repository_extension")
+use_repo(android_sdk, "androidsdk")
+register_toolchains("@androidsdk//:all")
+```
+
+Set `ANDROID_HOME` to a local Android SDK (with `platforms;android-34` and a
+recent `build-tools`).
+
+## `BUILD.bazel` (in your app's module)
+
+```starlark
+load("@rules_android//android:rules.bzl", "android_binary")
+load("@rules_kotlin//kotlin:android.bzl", "kt_android_library")
+
+# Lay the Swift JNI library and the NDK C++ runtime out as jniLibs for the
+# arm64-v8a ABI. `libSwiftJNI.so` is the swift_binary(linkshared) output; the
+# libc++_shared alias is host-independent (it selects the NDK for the build
+# host automatically).
+genrule(
+ name = "jni_libs",
+ srcs = [
+ "@rules_swift//examples/cross_compilation:libSwiftJNI.so",
+ "@swift_toolchain//:libcxx_shared_aarch64",
+ ],
+ outs = [
+ "lib/arm64-v8a/libSwiftJNI.so",
+ "lib/arm64-v8a/libc++_shared.so",
+ ],
+ cmd = """
+ srcs=($(SRCS))
+ mkdir -p $(RULEDIR)/lib/arm64-v8a
+ cp "$${srcs[0]}" $(RULEDIR)/lib/arm64-v8a/libSwiftJNI.so
+ cp "$${srcs[1]}" $(RULEDIR)/lib/arm64-v8a/libc++_shared.so
+ """,
+)
+
+kt_android_library(
+ name = "app_lib",
+ srcs = [
+ "java/com/example/swiftjni/MainActivity.kt",
+ "java/com/example/swiftjni/NativeBridge.kt",
+ ],
+ manifest = "AndroidManifest.xml",
+)
+
+android_binary(
+ name = "app",
+ manifest = "AndroidManifest.xml",
+ custom_package = "com.example.swiftjni",
+ # Bundle the native libraries laid out above.
+ resource_files = [],
+ deps = [":app_lib"],
+ # rules_android picks up `lib//*.so` produced by the genrule when it is
+ # provided as data; depending on your rules_android version you may instead
+ # place the .so files under `src/main/jniLibs//` or pass them through a
+ # `cc_library`/`android_library` `jni_libs` attribute.
+ data = [":jni_libs"],
+)
+```
+
+> The exact mechanism for adding pre-built `.so`s to an `android_binary` varies
+> by `rules_android` version; the constant is that `libSwiftJNI.so` and
+> `libc++_shared.so` must land under `lib/arm64-v8a/` (and the corresponding
+> directory for any other ABIs you build). Build the `.so` for `x86_64` with the
+> `//examples/cross_compilation:android-x86_64`-equivalent platform and place it
+> under `lib/x86_64/` to support the emulator.
+
+## Building
+
+```sh
+# The rules_swift-side artifact (verified by this repo):
+bazel build @rules_swift//examples/cross_compilation:libSwiftJNI.so
+
+# The APK (in your module, with the wiring above and ANDROID_HOME set):
+bazel build //path/to/android_app:app
+```
diff --git a/examples/cross_compilation/android_app/java/com/example/swiftjni/MainActivity.kt b/examples/cross_compilation/android_app/java/com/example/swiftjni/MainActivity.kt
new file mode 100644
index 000000000..d1cb84bf0
--- /dev/null
+++ b/examples/cross_compilation/android_app/java/com/example/swiftjni/MainActivity.kt
@@ -0,0 +1,15 @@
+package com.example.swiftjni
+
+import android.app.Activity
+import android.os.Bundle
+import android.widget.TextView
+
+/** Displays the greeting computed by Swift, reached via JNI. */
+class MainActivity : Activity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ val textView = TextView(this)
+ textView.text = NativeBridge.greetingFromSwift()
+ setContentView(textView)
+ }
+}
diff --git a/examples/cross_compilation/android_app/java/com/example/swiftjni/NativeBridge.kt b/examples/cross_compilation/android_app/java/com/example/swiftjni/NativeBridge.kt
new file mode 100644
index 000000000..62c59afa0
--- /dev/null
+++ b/examples/cross_compilation/android_app/java/com/example/swiftjni/NativeBridge.kt
@@ -0,0 +1,19 @@
+package com.example.swiftjni
+
+/**
+ * Loads the Swift JNI shared library (`libSwiftJNI.so`, built by the
+ * `//examples/cross_compilation:SwiftJNI` `swift_binary(linkshared = True)`)
+ * and exposes its Swift entry point to Kotlin.
+ *
+ * The native method binds by name to the Swift `@_cdecl` function
+ * `Java_com_example_swiftjni_NativeBridge_greetingFromSwift`, which in turn
+ * calls the `Greeter` `swift_library` — completing the
+ * Kotlin -> Swift (in the `.so`) -> Swift library call chain.
+ */
+object NativeBridge {
+ init {
+ System.loadLibrary("SwiftJNI")
+ }
+
+ external fun greetingFromSwift(): String
+}
diff --git a/examples/cross_compilation/web/README.md b/examples/cross_compilation/web/README.md
new file mode 100644
index 000000000..03ed9e007
--- /dev/null
+++ b/examples/cross_compilation/web/README.md
@@ -0,0 +1,40 @@
+# Web app: a Swift WebAssembly reactor in the browser
+
+A tiny static site that embeds `:Reactor.wasm` (a `swift_binary(linkshared =
+True)` reactor) and drives it from JavaScript — the page instantiates the
+module, runs the WASI reactor's `_initialize`, calls the exported
+`greeting_length` / `greeting_into`, reads the string Swift wrote into linear
+memory, and displays it.
+
+```sh
+# Assemble index.html + Reactor.wasm into one directory.
+bazel build //examples/cross_compilation:web_app
+
+# Serve it and open http://localhost:8000 in a browser.
+python3 -m http.server -d bazel-bin/examples/cross_compilation/web_app 8000
+```
+
+The page shows `“Hello from Swift, WebAssembly!”`.
+
+### Headless check
+
+`verify.mjs` runs the same flow under Node (a minimal WASI shim, no browser), so
+the example can be verified in CI / from the command line:
+
+```sh
+bazel build //examples/cross_compilation:Reactor.wasm
+node examples/cross_compilation/web/verify.mjs \
+ bazel-bin/examples/cross_compilation/Reactor.wasm
+# -> OK: Swift → WebAssembly greeting verified end-to-end
+```
+
+### Notes
+
+- The reactor imports `wasi_snapshot_preview1` for runtime startup; `index.html`
+ supplies a minimal shim (success stubs, `random_get` via Web Crypto). A real
+ app would use a WASI polyfill such as `@bjorn3/browser_wasi_shim`.
+- `linkshared = True` on a wasm target produces a **reactor** (no `_start`); the
+ host must call `_initialize()` once before any other export so Swift/C global
+ initializers run.
+- The output buffer is placed in a freshly `grow`n memory page, avoiding any
+ allocator import.
diff --git a/examples/cross_compilation/web/index.html b/examples/cross_compilation/web/index.html
new file mode 100644
index 000000000..cc7b1b1c7
--- /dev/null
+++ b/examples/cross_compilation/web/index.html
@@ -0,0 +1,88 @@
+
+
+
+
+
+ Swift → WebAssembly (rules_swift)
+
+
+
+
+ Swift, compiled to WebAssembly
+ A swift_binary(linkshared = True) reactor module
+ built with rules_swift — instantiated and driven from JavaScript:
+ loading…
+ The text above was produced by the Swift Greeter
+ library running in WebAssembly, read out of the module's linear memory.
+
+
+
+
diff --git a/examples/cross_compilation/web/verify.mjs b/examples/cross_compilation/web/verify.mjs
new file mode 100644
index 000000000..0934bfdc4
--- /dev/null
+++ b/examples/cross_compilation/web/verify.mjs
@@ -0,0 +1,58 @@
+// Headless end-to-end check of the WebAssembly reactor, mirroring index.html:
+// instantiate with a minimal WASI shim, run `_initialize`, then read the
+// greeting that Swift writes into linear memory. Exits non-zero on mismatch.
+//
+// node examples/cross_compilation/web/verify.mjs \
+// bazel-bin/examples/cross_compilation/Reactor.wasm
+//
+// (index.html does exactly this in a browser.)
+
+import { readFileSync } from "node:fs";
+import { webcrypto as crypto } from "node:crypto";
+
+const wasmPath = process.argv[2] ?? "bazel-bin/examples/cross_compilation/Reactor.wasm";
+const expected = "Hello from Swift, WebAssembly!";
+
+let instance;
+const dv = () => new DataView(instance.exports.memory.buffer);
+const u8 = () => new Uint8Array(instance.exports.memory.buffer);
+const SUCCESS = 0, BADF = 8;
+const wasi = {
+ args_sizes_get: (a, b) => { dv().setUint32(a, 0, true); dv().setUint32(b, 0, true); return SUCCESS; },
+ args_get: () => SUCCESS,
+ environ_sizes_get: (a, b) => { dv().setUint32(a, 0, true); dv().setUint32(b, 0, true); return SUCCESS; },
+ environ_get: () => SUCCESS,
+ fd_fdstat_get: (fd, ptr) => { for (let i = 0; i < 24; i++) dv().setUint8(ptr + i, 0); return SUCCESS; },
+ fd_prestat_get: () => BADF,
+ fd_prestat_dir_name: () => BADF,
+ fd_close: () => SUCCESS,
+ fd_read: (fd, iovs, n, nread) => { dv().setUint32(nread, 0, true); return SUCCESS; },
+ fd_seek: (fd, off, whence, newOff) => { dv().setUint32(newOff, 0, true); return SUCCESS; },
+ fd_write: (fd, iovs, n, nwritten) => {
+ let written = 0;
+ for (let i = 0; i < n; i++) written += dv().getUint32(iovs + i * 8 + 4, true);
+ dv().setUint32(nwritten, written, true);
+ return SUCCESS;
+ },
+ path_open: () => BADF,
+ proc_exit: (code) => { throw new Error("proc_exit(" + code + ")"); },
+ random_get: (ptr, len) => { crypto.getRandomValues(u8().subarray(ptr, ptr + len)); return SUCCESS; },
+};
+
+const bytes = readFileSync(wasmPath);
+instance = (await WebAssembly.instantiate(bytes, { wasi_snapshot_preview1: wasi })).instance;
+instance.exports._initialize();
+
+const length = instance.exports.greeting_length();
+const memory = instance.exports.memory;
+const ptr = memory.buffer.byteLength;
+memory.grow(Math.ceil((length + 1) / 65536));
+const written = instance.exports.greeting_into(ptr, length + 1);
+const greeting = new TextDecoder().decode(new Uint8Array(memory.buffer, ptr, written));
+
+console.log("greeting:", JSON.stringify(greeting));
+if (greeting !== expected) {
+ console.error(`FAIL: expected ${JSON.stringify(expected)}`);
+ process.exit(1);
+}
+console.log("OK: Swift → WebAssembly greeting verified end-to-end");
diff --git a/examples/xplatform/shared_library/BUILD b/examples/xplatform/shared_library/BUILD
new file mode 100644
index 000000000..ca2e1bd7f
--- /dev/null
+++ b/examples/xplatform/shared_library/BUILD
@@ -0,0 +1,12 @@
+load("//swift:swift_binary.bzl", "swift_binary")
+
+licenses(["notice"])
+
+# `linkshared` links a native dynamic library instead of an executable: a
+# `.dylib` on Apple platforms, a `.so` on Linux/Android, and a `.dll` (plus an
+# import `.lib`) on Windows.
+swift_binary(
+ name = "shared_library",
+ srcs = ["greeting.swift"],
+ linkshared = True,
+)
diff --git a/examples/xplatform/shared_library/greeting.swift b/examples/xplatform/shared_library/greeting.swift
new file mode 100644
index 000000000..41a9a8e71
--- /dev/null
+++ b/examples/xplatform/shared_library/greeting.swift
@@ -0,0 +1,18 @@
+// Copyright 2024 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+@_cdecl("greeting")
+public func greeting() -> Int32 {
+ return 42
+}
diff --git a/swift/extensions.bzl b/swift/extensions.bzl
index be6bbc0c1..7249ec678 100644
--- a/swift/extensions.bzl
+++ b/swift/extensions.bzl
@@ -15,13 +15,32 @@
"""Definitions for bzlmod module extensions."""
load("@bazel_features//:features.bzl", "bazel_features")
+load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
load("//swift/internal:repositories.bzl", "swift_rules_dependencies")
load("//swift/internal/extensions:standalone_toolchain.bzl", _standalone_toolchain = "standalone_toolchain")
load("//swift/internal/extensions:swift_releases.bzl", "SWIFT_RELEASES")
+load(
+ "//swift/internal/extensions:swift_sdk_releases.bzl",
+ "ANDROID_NDK_RELEASES",
+ "DEFAULT_ANDROID_NDK_VERSION",
+ "SWIFT_SDK_RELEASES",
+ "android_ndk_download_url",
+ "swift_sdk_download_url",
+)
+load(
+ "//swift/internal/extensions:swift_sdks.bzl",
+ "ANDROID_ARCHS",
+ "ANDROID_NDK_BUILD_FILE_CONTENT",
+ "swift_android_sdk_repository",
+ "swift_wasm_sdk_repository",
+)
load(
"//swift/internal/extensions:toolchains.bzl",
+ _android_libcxx_aliases = "android_libcxx_aliases",
+ _android_sdk_toolchains_for_platform = "android_sdk_toolchains_for_platform",
_toolchains_for_platform = "toolchains_for_platform",
_toolchains_repository = "toolchains_repository",
+ _wasm_sdk_toolchains_for_platform = "wasm_sdk_toolchains_for_platform",
)
load("//tools/explicit_modules:extensions.bzl", _system_sdk = "system_sdk")
@@ -40,6 +59,132 @@ def _non_module_deps_impl(module_ctx):
non_module_deps = module_extension(implementation = _non_module_deps_impl)
+def _ndk_host_os(platform):
+ """Returns the Android NDK host OS for a host toolchain platform name."""
+ return "darwin" if platform == "xcode" else "linux"
+
+def _setup_wasm_sdk(*, tag, toolchain_name, swift_version, platforms):
+ """Creates the repositories for a `swift.wasm_sdk` tag.
+
+ Args:
+ tag: The `wasm_sdk` tag.
+ toolchain_name: The name of the `swift.toolchain` tag the SDK extends.
+ swift_version: The Swift release version of that toolchain.
+ platforms: The host platforms the toolchain was created for.
+
+ Returns:
+ BUILD file content with the `toolchain` declarations to add to the
+ toolchains hub repository.
+ """
+ sha256 = tag.sha256
+ if not sha256:
+ if swift_version not in SWIFT_SDK_RELEASES:
+ fail("No known WebAssembly Swift SDK for version `{}`. Please choose one of {}, or provide the SDK's sha256.".format(
+ swift_version,
+ SWIFT_SDK_RELEASES.keys(),
+ ))
+ sha256 = SWIFT_SDK_RELEASES[swift_version]["wasm"]
+
+ build_file_content = ""
+ for platform in platforms:
+ repository_name = "{}_wasm_sdk_{}".format(toolchain_name, platform)
+ swift_wasm_sdk_repository(
+ name = repository_name,
+ sha256 = sha256,
+ swift_version = swift_version,
+ toolchain_repo = "{}_{}".format(toolchain_name, platform),
+ url = swift_sdk_download_url(swift_version, "wasm"),
+ )
+ build_file_content += _wasm_sdk_toolchains_for_platform(
+ platform = platform,
+ sdk_repository = repository_name,
+ )
+ return build_file_content
+
+def _setup_android_sdk(*, tag, toolchain_name, swift_version, platforms):
+ """Creates the repositories for a `swift.android_sdk` tag.
+
+ Args:
+ tag: The `android_sdk` tag.
+ toolchain_name: The name of the `swift.toolchain` tag the SDK extends.
+ swift_version: The Swift release version of that toolchain.
+ platforms: The host platforms the toolchain was created for.
+
+ Returns:
+ BUILD file content with the `toolchain` declarations to add to the
+ toolchains hub repository.
+ """
+ sha256 = tag.sha256
+ if not sha256:
+ if swift_version not in SWIFT_SDK_RELEASES:
+ fail("No known Android Swift SDK for version `{}`. Please choose one of {}, or provide the SDK's sha256.".format(
+ swift_version,
+ SWIFT_SDK_RELEASES.keys(),
+ ))
+ sha256 = SWIFT_SDK_RELEASES[swift_version]["android"]
+
+ ndk_version = tag.ndk_version or DEFAULT_ANDROID_NDK_VERSION
+ ndk_sha256s = tag.ndk_sha256s
+ if not ndk_sha256s:
+ if ndk_version not in ANDROID_NDK_RELEASES:
+ fail("No known Android NDK release `{}`. Please choose one of {}, or provide the NDK's sha256s.".format(
+ ndk_version,
+ ANDROID_NDK_RELEASES.keys(),
+ ))
+ ndk_sha256s = ANDROID_NDK_RELEASES[ndk_version]
+
+ host_oses = {_ndk_host_os(platform): None for platform in platforms}
+ ndk_repos_by_host = {}
+ for host_os in host_oses:
+ ndk_repo = "{}_android_ndk_{}".format(toolchain_name, host_os)
+ ndk_repos_by_host[host_os] = ndk_repo
+ http_archive(
+ name = ndk_repo,
+ build_file_content = ANDROID_NDK_BUILD_FILE_CONTENT,
+ sha256 = ndk_sha256s.get(host_os, ""),
+ strip_prefix = "android-ndk-" + ndk_version,
+ url = android_ndk_download_url(ndk_version, host_os),
+ )
+
+ # Host-independent aliases for the NDK's `libc++_shared.so`, so an APK rule
+ # can bundle it without naming the build host.
+ build_file_content = _android_libcxx_aliases(
+ ndk_repos_by_host = ndk_repos_by_host,
+ archs = ANDROID_ARCHS,
+ )
+ for platform in platforms:
+ ndk_repo = "{}_android_ndk_{}".format(toolchain_name, _ndk_host_os(platform))
+ repository_name = "{}_android_sdk_{}".format(toolchain_name, platform)
+ swift_android_sdk_repository(
+ name = repository_name,
+ api_level = tag.api_level,
+ host_swiftc = "@{}_{}//:usr/bin/swiftc".format(toolchain_name, platform),
+ ndk_repo = ndk_repo,
+ ndk_source_properties = "@{}//:source.properties".format(ndk_repo),
+ sha256 = sha256,
+ swift_version = swift_version,
+ toolchain_repo = "{}_{}".format(toolchain_name, platform),
+ url = swift_sdk_download_url(swift_version, "android"),
+ )
+ build_file_content += _android_sdk_toolchains_for_platform(
+ platform = platform,
+ sdk_repository = repository_name,
+ archs = ANDROID_ARCHS,
+ )
+ return build_file_content
+
+def _sdk_tags_by_toolchain_name(tags, kind):
+ """Groups SDK tags by the toolchain they extend, rejecting duplicates."""
+ tags_by_name = {}
+ for tag in tags:
+ if tag.toolchain_name in tags_by_name:
+ fail("Only one `{}` tag may be used per toolchain, got multiple for `{}`.".format(
+ kind,
+ tag.toolchain_name,
+ ))
+ tags_by_name[tag.toolchain_name] = tag
+ return tags_by_name
+
def _standalone_toolchain_impl(module_ctx):
root_module = None
for mod in module_ctx.modules:
@@ -50,6 +195,31 @@ def _standalone_toolchain_impl(module_ctx):
if not root_module:
fail("Could not find a root module. This should never happen.")
+ wasm_sdk_tags = _sdk_tags_by_toolchain_name(
+ root_module.tags.wasm_sdk,
+ "wasm_sdk",
+ )
+ android_sdk_tags = _sdk_tags_by_toolchain_name(
+ root_module.tags.android_sdk,
+ "android_sdk",
+ )
+
+ toolchain_names = [
+ toolchain.name
+ for toolchain in root_module.tags.toolchain
+ ]
+ for kind, tags in (
+ ("wasm_sdk", wasm_sdk_tags),
+ ("android_sdk", android_sdk_tags),
+ ):
+ for toolchain_name in tags:
+ if toolchain_name not in toolchain_names:
+ fail("The `{}` tag references unknown toolchain `{}`. Please use the name of a `toolchain` tag: {}".format(
+ kind,
+ toolchain_name,
+ toolchain_names,
+ ))
+
toolchains_build_file_content = ""
for toolchain in root_module.tags.toolchain:
if toolchain.swift_version and toolchain.swift_version_file:
@@ -81,6 +251,23 @@ def _standalone_toolchain_impl(module_ctx):
platform = platform,
toolchain_repository = repository_name,
)
+
+ platforms = [platform for platform, _ in swift_releases]
+ if toolchain.name in wasm_sdk_tags:
+ toolchains_build_file_content += _setup_wasm_sdk(
+ tag = wasm_sdk_tags[toolchain.name],
+ toolchain_name = toolchain.name,
+ swift_version = swift_version,
+ platforms = platforms,
+ )
+ if toolchain.name in android_sdk_tags:
+ toolchains_build_file_content += _setup_android_sdk(
+ tag = android_sdk_tags[toolchain.name],
+ toolchain_name = toolchain.name,
+ swift_version = swift_version,
+ platforms = platforms,
+ )
+
_toolchains_repository(
name = toolchain.name,
build_file_content = toolchains_build_file_content,
@@ -94,6 +281,87 @@ def _standalone_toolchain_impl(module_ctx):
**metadata_kwargs
)
+_wasm_sdk = tag_class(
+ attrs = {
+ "sha256": attr.string(
+ doc = """\
+The expected SHA-256 of the SDK artifact bundle. May be omitted for Swift
+versions known to this version of rules_swift.
+""",
+ ),
+ "toolchain_name": attr.string(
+ doc = "The name of the `toolchain` tag to add this Swift SDK to.",
+ mandatory = True,
+ ),
+ },
+ doc = """\
+Downloads the WebAssembly Swift SDK matching a `toolchain` tag's Swift version
+and defines Swift and C++ toolchains targeting `wasm32-unknown-wasip1`.
+
+Register the generated toolchains for the host platforms you build on, e.g.:
+
+```starlark
+register_toolchains(
+ "@swift_toolchain//:swift_toolchain_wasm32_xcode",
+ "@swift_toolchain//:cc_toolchain_wasm32_xcode",
+)
+```
+
+and build with a platform that has the `@platforms//os:wasi` and
+`@platforms//cpu:wasm32` constraints.
+""",
+)
+
+_android_sdk = tag_class(
+ attrs = {
+ "api_level": attr.int(
+ default = 28,
+ doc = "The Android API level to target.",
+ ),
+ "ndk_sha256s": attr.string_dict(
+ doc = """\
+A dictionary of NDK host OS ("darwin", "linux") to the expected SHA-256 of the
+NDK archive. May be omitted for NDK versions known to this version of
+rules_swift.
+""",
+ ),
+ "ndk_version": attr.string(
+ doc = """\
+The Android NDK release (e.g. "r27c") whose sysroot and clang are used. The
+Android Swift SDK requires r27 or later. Defaults to a version known to work
+with the supported Swift releases.
+""",
+ ),
+ "sha256": attr.string(
+ doc = """\
+The expected SHA-256 of the SDK artifact bundle. May be omitted for Swift
+versions known to this version of rules_swift.
+""",
+ ),
+ "toolchain_name": attr.string(
+ doc = "The name of the `toolchain` tag to add this Swift SDK to.",
+ mandatory = True,
+ ),
+ },
+ doc = """\
+Downloads the Android Swift SDK matching a `toolchain` tag's Swift version
+(along with the Android NDK) and defines Swift and C++ toolchains targeting
+`aarch64-unknown-linux-android` and `x86_64-unknown-linux-android`.
+
+Register the generated toolchains for the host platforms you build on, e.g.:
+
+```starlark
+register_toolchains(
+ "@swift_toolchain//:swift_toolchain_android_aarch64_xcode",
+ "@swift_toolchain//:cc_toolchain_android_aarch64_xcode",
+)
+```
+
+and build with a platform that has the `@platforms//os:android` and
+`@platforms//cpu:aarch64` (or `x86_64`) constraints.
+""",
+)
+
_toolchain = tag_class(attrs = {
"name": attr.string(
doc = "Repository name of the generated toolchain",
@@ -114,6 +382,8 @@ their hashes. For instance:
swift = module_extension(
implementation = _standalone_toolchain_impl,
tag_classes = {
+ "android_sdk": _android_sdk,
"toolchain": _toolchain,
+ "wasm_sdk": _wasm_sdk,
},
)
diff --git a/swift/internal/compiling.bzl b/swift/internal/compiling.bzl
index 0db3d6b62..208053449 100644
--- a/swift/internal/compiling.bzl
+++ b/swift/internal/compiling.bzl
@@ -1667,7 +1667,14 @@ def _declare_per_source_output_file(actions, extension, target_name, src):
The declared `File`.
"""
objs_dir = "{}_objs".format(target_name)
- owner_rel_path = owner_relative_path(src)
+
+ # Spaces in object file paths break response-file parsing for the Windows
+ # archiver and linker (`lib.exe`/`link.exe`), which treat an unquoted space
+ # as an argument separator. Sanitize them in the derived output path so that
+ # targets whose sources live in directories containing spaces (for example
+ # swift-argument-parser's "Parsable Properties") can be archived and linked
+ # on Windows. Paths without spaces are unaffected.
+ owner_rel_path = owner_relative_path(src).replace(" ", "_")
basename = paths.basename(owner_rel_path)
dirname = paths.join(objs_dir, paths.dirname(owner_rel_path))
diff --git a/swift/internal/extensions/BUILD.bazel b/swift/internal/extensions/BUILD.bazel
index bbe55c646..87c28d3ab 100644
--- a/swift/internal/extensions/BUILD.bazel
+++ b/swift/internal/extensions/BUILD.bazel
@@ -18,6 +18,18 @@ bzl_library(
visibility = ["//swift:__subpackages__"],
)
+bzl_library(
+ name = "swift_sdk_releases",
+ srcs = ["swift_sdk_releases.bzl"],
+ visibility = ["//swift:__subpackages__"],
+)
+
+bzl_library(
+ name = "swift_sdks",
+ srcs = ["swift_sdks.bzl"],
+ visibility = ["//swift:__subpackages__"],
+)
+
bzl_library(
name = "toolchains",
srcs = ["toolchains.bzl"],
diff --git a/swift/internal/extensions/BUILD.bazel.tpl b/swift/internal/extensions/BUILD.bazel.tpl
index 231ecbcb9..f0e9c8192 100644
--- a/swift/internal/extensions/BUILD.bazel.tpl
+++ b/swift/internal/extensions/BUILD.bazel.tpl
@@ -13,6 +13,54 @@ exports_files([
"usr/bin/llvm-objcopy",
])
+### Tools referenced by Swift SDK cross-compilation repositories. ###
+# See swift/internal/extensions/swift_sdks.bzl.
+exports_files([
+ "usr/bin/clang",
+ "usr/bin/llvm-ar",
+ "usr/bin/swift-autolink-extract",
+ "usr/bin/swift-symbolgraph-extract",
+ "usr/bin/swiftc",
+])
+
+# The subset of the toolchain that Swift compile actions for a Swift SDK
+# target need: the driver/frontend and their libraries, plus clang's builtin
+# headers for the clang importer. The Swift standard library for the target
+# comes from the SDK, not from here.
+filegroup(
+ name = "swift_sdk_compiler_inputs",
+ srcs = glob(
+ [
+ "usr/bin/swift*",
+ "usr/lib/clang/**",
+ "usr/lib/lib*.dylib",
+ "usr/lib/lib*.so*",
+ "usr/lib/swift/host/**",
+ "usr/lib/swift/linux/**",
+ "usr/lib/swift/macosx/**",
+ ],
+ allow_empty = True,
+ ),
+)
+
+# The subset of the toolchain that link actions driven by this toolchain's
+# clang need.
+filegroup(
+ name = "swift_sdk_linker_inputs",
+ srcs = glob(
+ [
+ "usr/bin/clang*",
+ "usr/bin/ld.lld",
+ "usr/bin/ld64.lld",
+ "usr/bin/lld",
+ "usr/bin/llvm-ar",
+ "usr/bin/wasm-ld",
+ "usr/lib/clang/**",
+ ],
+ allow_empty = True,
+ ),
+)
+
filegroup(
name = "files",
srcs = glob(
diff --git a/swift/internal/extensions/swift_sdk_releases.bzl b/swift/internal/extensions/swift_sdk_releases.bzl
new file mode 100644
index 000000000..dfd87ee59
--- /dev/null
+++ b/swift/internal/extensions/swift_sdk_releases.bzl
@@ -0,0 +1,70 @@
+"""Swift SDK and Android NDK release version mappings.
+
+This module defines checksums for the artifacts needed to cross-compile Swift
+for platforms that are not covered by a host toolchain, using the official
+"Swift SDK" artifact bundles published by swift.org (the bundles installed by
+`swift sdk install`).
+
+The Swift module format is not stable across compiler versions, so a Swift SDK
+can only be used with the host toolchain from exactly the same release; the
+keys of `SWIFT_SDK_RELEASES` therefore mirror the keys of `SWIFT_RELEASES` in
+`swift_releases.bzl`. Checksums are published in
+https://www.swift.org/api/v1/install/releases.json.
+"""
+
+SWIFT_SDK_RELEASES = {
+ "6.3.2": {
+ "android": "939e933549d12d28f2e0bf71019d734d309859e9773c572657ce565a81f85d68",
+ "wasm": "a61f0584c93283589f8b2f42db05c1f9a182b506c2957271402992655591dd7c",
+ },
+}
+
+# The Android Swift SDK bundle ships without an NDK sysroot (its
+# setup-android-sdk.sh script normally symlinks one in from a local NDK
+# install), so the NDK is fetched hermetically as well. Checksums are for the
+# zips at https://dl.google.com/android/repository/android-ndk-{version}-{os}.zip
+DEFAULT_ANDROID_NDK_VERSION = "r27c"
+
+ANDROID_NDK_RELEASES = {
+ "r27c": {
+ "darwin": "8c5685457c58a88527367d46d3f14e8c727d962c39f85344cff0c0768a73c3b7",
+ "linux": "59c2f6dc96743b5daf5d1626684640b20a6bd2b1d85b13156b90333741bad5cc",
+ },
+}
+
+def swift_sdk_download_url(swift_version, sdk):
+ """Returns the download URL for a Swift SDK artifact bundle.
+
+ Args:
+ swift_version: The Swift release version (e.g. "6.3.2").
+ sdk: The SDK kind; one of "wasm" or "android".
+
+ Returns:
+ The URL of the `.artifactbundle.tar.gz` for the given release.
+ """
+ if "-snapshot-" in swift_version:
+ fail("Swift SDKs are only supported for release versions, got `{}`".format(
+ swift_version,
+ ))
+ return (
+ "https://download.swift.org/swift-{version}-release/{sdk}-sdk/" +
+ "swift-{version}-RELEASE/swift-{version}-RELEASE_{sdk}.artifactbundle.tar.gz"
+ ).format(
+ sdk = sdk,
+ version = swift_version,
+ )
+
+def android_ndk_download_url(ndk_version, host_os):
+ """Returns the download URL for an Android NDK release.
+
+ Args:
+ ndk_version: The NDK release name (e.g. "r27c").
+ host_os: The host OS the NDK runs on; one of "darwin" or "linux".
+
+ Returns:
+ The URL of the NDK zip for the given release and host.
+ """
+ return "https://dl.google.com/android/repository/android-ndk-{version}-{host_os}.zip".format(
+ host_os = host_os,
+ version = ndk_version,
+ )
diff --git a/swift/internal/extensions/swift_sdks.bzl b/swift/internal/extensions/swift_sdks.bzl
new file mode 100644
index 000000000..4d06ae17b
--- /dev/null
+++ b/swift/internal/extensions/swift_sdks.bzl
@@ -0,0 +1,523 @@
+"""Repository rules for downloading and configuring Swift SDKs.
+
+A "Swift SDK" is the artifact bundle published by swift.org for
+cross-compiling Swift to platforms that the host toolchain cannot target by
+itself (currently WebAssembly and Android); they are the bundles that
+`swift sdk install` consumes.
+
+Each repository created by these rules pairs one Swift SDK with one standalone
+host toolchain repository (created by `standalone_toolchain`) and defines:
+
+ * a `swift_toolchain` that compiles against the SDK's sysroot and Swift
+ resource directory, and links against its static Swift runtime; and
+ * a rules_cc `cc_toolchain` that drives the matching clang for the target
+ (the host toolchain's clang for WebAssembly, the Android NDK's clang for
+ Android), which `swift_binary`/`cc_*` rules use to link.
+
+The `toolchain` declarations that register these for a given target platform
+are generated into the toolchains hub repository; see `toolchains.bzl`.
+
+Because the Swift module format is not stable across compiler versions, a
+Swift SDK must come from exactly the same release as the host toolchain it is
+paired with; the `swift` module extension enforces this by deriving both from
+the same `swift.toolchain` tag.
+"""
+
+# BUILD file written into the Android NDK repository fetched alongside the
+# Android Swift SDK. The prebuilt directory name varies by host
+# ("darwin-x86_64", "linux-x86_64"), hence the wildcards.
+ANDROID_NDK_BUILD_FILE_CONTENT = """\
+package(default_visibility = ["//visibility:public"])
+
+filegroup(
+ name = "clang",
+ srcs = glob(["toolchains/llvm/prebuilt/*/bin/clang"]),
+)
+
+filegroup(
+ name = "llvm_ar",
+ srcs = glob(["toolchains/llvm/prebuilt/*/bin/llvm-ar"]),
+)
+
+filegroup(
+ name = "toolchain_files",
+ srcs = glob([
+ "toolchains/llvm/prebuilt/*/bin/*",
+ "toolchains/llvm/prebuilt/*/lib/**",
+ "toolchains/llvm/prebuilt/*/sysroot/**",
+ ]),
+)
+
+# The shared C++ runtime that must be packaged into any Android application
+# that contains Swift code.
+filegroup(
+ name = "libcxx_shared_aarch64",
+ srcs = glob(["toolchains/llvm/prebuilt/*/sysroot/usr/lib/aarch64-linux-android/libc++_shared.so"]),
+)
+
+filegroup(
+ name = "libcxx_shared_x86_64",
+ srcs = glob(["toolchains/llvm/prebuilt/*/sysroot/usr/lib/x86_64-linux-android/libc++_shared.so"]),
+)
+"""
+
+# Files in the host toolchain that compile actions need: the driver/frontend
+# binaries, their libraries, and clang's builtin headers (used by the clang
+# importer when the Swift SDK's resource directory does not bundle them).
+_HOST_COMPILER_INPUTS = "swift_sdk_compiler_inputs"
+
+# Files in the host toolchain that link actions driven by its clang need.
+_HOST_LINKER_INPUTS = "swift_sdk_linker_inputs"
+
+_CC_TOOLCHAIN_TEMPLATE = """
+cc_tool(
+ name = "clang",
+ src = "{clang}",
+ data = {clang_data},
+ tags = ["manual"],
+)
+
+cc_tool(
+ name = "ar",
+ src = "{ar}",
+ tags = ["manual"],
+)
+
+cc_tool_map(
+ name = "cc_tools",
+ tags = ["manual"],
+ tools = {{
+ "@rules_cc//cc/toolchains/actions:ar_actions": ":ar",
+ "@rules_cc//cc/toolchains/actions:assembly_actions": ":clang",
+ "@rules_cc//cc/toolchains/actions:c_compile": ":clang",
+ "@rules_cc//cc/toolchains/actions:cpp_compile_actions": ":clang",
+ "@rules_cc//cc/toolchains/actions:link_actions": ":clang",
+ }},
+)
+"""
+
+_CC_TOOLCHAIN_FOR_TARGET_TEMPLATE = """
+cc_args(
+ name = "cc_args_{suffix}",
+ actions = [
+ "@rules_cc//cc/toolchains/actions:compile_actions",
+ "@rules_cc//cc/toolchains/actions:link_actions",
+ ],
+ args = {args},
+)
+
+cc_args(
+ name = "cc_link_args_{suffix}",
+ actions = [
+ "@rules_cc//cc/toolchains/actions:link_actions",
+ ],
+ args = {link_args},
+)
+
+cc_make_variable(
+ name = "cc_target_triple_{suffix}",
+ value = "{triple}",
+ variable_name = "CC_TARGET_TRIPLE",
+)
+
+cc_toolchain(
+ name = "cc_toolchain_{suffix}",
+ args = [
+ ":cc_args_{suffix}",
+ ":cc_link_args_{suffix}",
+ ],
+ compiler = "clang",
+ enabled_features = [
+ "@rules_cc//cc/toolchains/args/archiver_flags:feature",
+ "@rules_cc//cc/toolchains/args/libraries_to_link:feature",
+ "@rules_cc//cc/toolchains/args/link_flags:feature",
+ # Needed so `swift_binary(linkshared = True)` links a shared library
+ # (passes `-shared` for the dynamic_library link action).
+ "@rules_cc//cc/toolchains/args/shared_flag:feature",
+ ],
+ make_variables = [
+ ":cc_target_triple_{suffix}",
+ ],
+ tool_map = ":cc_tools",
+)
+"""
+
+_SWIFT_TOOLCHAIN_TEMPLATE = """
+swift_toolchain(
+ name = "swift_toolchain_{suffix}",
+ arch = "{arch}",
+ copts = {copts},
+ features = {features},
+ linker_inputs = {linker_inputs},
+ linkopts = {linkopts},
+ os = "{os}",
+ parsed_version = "{swift_version}",
+ sdkroot = "{sdkroot}",
+ swift_tools = ":tools",
+ version_file = ".swift-version",
+)
+"""
+
+_BUILD_HEADER_TEMPLATE = """\
+load("@rules_cc//cc/toolchains:args.bzl", "cc_args")
+load("@rules_cc//cc/toolchains:make_variable.bzl", "cc_make_variable")
+load("@rules_cc//cc/toolchains:tool.bzl", "cc_tool")
+load("@rules_cc//cc/toolchains:tool_map.bzl", "cc_tool_map")
+load("@rules_cc//cc/toolchains:toolchain.bzl", "cc_toolchain")
+load("@rules_swift//swift/toolchains:swift_toolchain.bzl", "swift_toolchain")
+load("@rules_swift//swift/toolchains:swift_tools.bzl", "swift_tools")
+
+package(default_visibility = ["//visibility:public"])
+
+filegroup(
+ name = "sdk_files",
+ srcs = glob(["{bundle_dir}/**"]),
+)
+
+swift_tools(
+ name = "tools",
+ swift_driver = "@{toolchain_repo}//:usr/bin/swiftc",
+ swift_autolink_extract = "@{toolchain_repo}//:usr/bin/swift-autolink-extract",
+ swift_symbolgraph_extract = "@{toolchain_repo}//:usr/bin/swift-symbolgraph-extract",
+ additional_inputs = {compiler_inputs},
+)
+"""
+
+def _execroot_relative_path(path):
+ """Returns the execution-root-relative path for an external repository path.
+
+ Args:
+ path: An absolute `path` (or string) below the output base's
+ `external` directory.
+
+ Returns:
+ The same path expressed relative to the execution root, suitable for
+ baking into command line flags.
+ """
+ path_str = str(path)
+ if "/external/" not in path_str:
+ fail("Expected a path inside an external repository, got: " + path_str)
+ return "external/" + path_str.rsplit("/external/", 1)[1]
+
+def _build_list(items, indent = " "):
+ """Formats a list of strings as a multi-line BUILD file list literal."""
+ if not items:
+ return "[]"
+ lines = ["["]
+ for item in items:
+ lines.append("{} \"{}\",".format(indent, item))
+ lines.append(indent + "]")
+ return "\n".join(lines)
+
+def _download_sdk_bundle(repository_ctx):
+ """Downloads and extracts the Swift SDK artifact bundle for a repository.
+
+ Returns:
+ The name of the top-level `.artifactbundle` directory.
+ """
+ repository_ctx.download_and_extract(
+ url = repository_ctx.attr.url,
+ sha256 = repository_ctx.attr.sha256,
+ )
+ repository_ctx.file(".swift-version", repository_ctx.attr.swift_version)
+
+ bundles = [
+ entry.basename
+ for entry in repository_ctx.path(".").readdir()
+ if entry.basename.endswith(".artifactbundle")
+ ]
+ if len(bundles) != 1:
+ fail(("Expected the archive at {} to contain exactly one " +
+ ".artifactbundle directory, found: {}").format(
+ repository_ctx.attr.url,
+ bundles,
+ ))
+ return bundles[0]
+
+def _common_attrs():
+ return {
+ "sha256": attr.string(
+ doc = "The expected SHA-256 of the SDK artifact bundle.",
+ mandatory = True,
+ ),
+ "swift_version": attr.string(
+ doc = "The Swift release version the SDK belongs to.",
+ mandatory = True,
+ ),
+ "toolchain_repo": attr.string(
+ doc = """\
+Name of the `standalone_toolchain` repository providing the host tools that
+this SDK is paired with.
+""",
+ mandatory = True,
+ ),
+ "url": attr.string(
+ doc = "The download URL of the SDK artifact bundle.",
+ mandatory = True,
+ ),
+ }
+
+def _swift_wasm_sdk_impl(repository_ctx):
+ bundle_dir = _download_sdk_bundle(repository_ctx)
+ toolchain_repo = repository_ctx.attr.toolchain_repo
+
+ repo_root = "external/" + repository_ctx.name
+ sdk_dir = "{}/{}/{}".format(
+ repo_root,
+ bundle_dir,
+ "{0}/wasm32-unknown-wasip1".format(bundle_dir.removesuffix(".artifactbundle")),
+ )
+ if not repository_ctx.path(sdk_dir.removeprefix(repo_root + "/")).exists:
+ fail("The WebAssembly Swift SDK bundle has an unexpected layout; " +
+ "missing " + sdk_dir)
+ wasi_sdk = sdk_dir + "/WASI.sdk"
+ resource_dir = sdk_dir + "/swift.xctoolchain/usr/lib/swift_static"
+
+ build_content = _BUILD_HEADER_TEMPLATE.format(
+ bundle_dir = bundle_dir,
+ compiler_inputs = _build_list([
+ ":sdk_files",
+ "@{}//:{}".format(toolchain_repo, _HOST_COMPILER_INPUTS),
+ ]),
+ toolchain_repo = toolchain_repo,
+ )
+
+ build_content += _SWIFT_TOOLCHAIN_TEMPLATE.format(
+ arch = "wasm32",
+ copts = _build_list([
+ "-resource-dir",
+ resource_dir,
+ ]),
+ features = _build_list([
+ "swift.module_map_no_private_headers",
+ "swift.no_embed_debug_module",
+ # wasm-ld cannot alias a renamed entry point back to the symbol
+ # that wasi-libc's startup code expects.
+ "swift.no_entry_point_rename",
+ "swift.use_autolink_extract",
+ # The file prefix map would make the worker resolve the Xcode
+ # developer directory on macOS hosts, which this toolchain does
+ # not depend on.
+ "-swift.file_prefix_map",
+ ]),
+ linker_inputs = _build_list([":sdk_files"]),
+ # The runtime objects and libraries that `swiftc` would add when
+ # linking a static executable for WASI; see
+ # `swift_static/wasi/static-executable-args.lnk` in the SDK.
+ linkopts = _build_list([
+ "{}/wasi/wasm32/swiftrt.o".format(resource_dir),
+ "-L{}/wasi".format(resource_dir),
+ "-lc++",
+ "-lc++abi",
+ "-lswiftSwiftOnoneSupport",
+ "-ldl",
+ "-lm",
+ "-lwasi-emulated-mman",
+ "-lwasi-emulated-signal",
+ "-lwasi-emulated-process-clocks",
+ # Place the linear-memory data and the indirect function table at the
+ # same bases `swiftc` uses for its own wasm links. The Swift driver
+ # always passes these to wasm-ld; in particular `--table-base=4096`
+ # is required — optimized (`-O`) Swift relies on the indirect
+ # function table starting where the runtime/codegen expects it, and
+ # without it generic-metadata instantiation reads out of bounds at
+ # runtime (`__swift_instantiateGenericMetadata` faults). `-Onone`
+ # happens to tolerate the default base, which masks the bug.
+ "-Wl,--global-base=4096",
+ "-Wl,--table-base=4096",
+ ]),
+ os = "wasi",
+ sdkroot = wasi_sdk,
+ suffix = "wasm32",
+ swift_version = repository_ctx.attr.swift_version,
+ )
+
+ build_content += _CC_TOOLCHAIN_TEMPLATE.format(
+ ar = "@{}//:usr/bin/llvm-ar".format(toolchain_repo),
+ clang = "@{}//:usr/bin/clang".format(toolchain_repo),
+ clang_data = _build_list([
+ ":sdk_files",
+ "@{}//:{}".format(toolchain_repo, _HOST_LINKER_INPUTS),
+ ]),
+ )
+
+ build_content += _CC_TOOLCHAIN_FOR_TARGET_TEMPLATE.format(
+ args = _build_list([
+ "--target=wasm32-unknown-wasip1",
+ "--sysroot=" + wasi_sdk,
+ ]),
+ # The Swift SDK's clang resource directory provides the compiler
+ # builtins (libclang_rt) for wasm32, which the host toolchain's own
+ # resource directory does not include.
+ link_args = _build_list([
+ "-resource-dir",
+ resource_dir + "/clang",
+ ]),
+ suffix = "wasm32",
+ triple = "wasm32-unknown-wasip1",
+ )
+
+ repository_ctx.file("BUILD.bazel", build_content)
+
+swift_wasm_sdk_repository = repository_rule(
+ attrs = _common_attrs(),
+ doc = """\
+Downloads the WebAssembly Swift SDK artifact bundle and defines Swift and C++
+toolchains that target `wasm32-unknown-wasip1` using a standalone host
+toolchain's compiler.
+""",
+ implementation = _swift_wasm_sdk_impl,
+)
+
+# The architectures the Android Swift SDK provides resources for and that
+# `@platforms//cpu` can express. (The SDK also supports armv7, which can be
+# added on demand.)
+ANDROID_ARCHS = ["aarch64", "x86_64"]
+
+def _swift_android_sdk_impl(repository_ctx):
+ bundle_dir = _download_sdk_bundle(repository_ctx)
+ toolchain_repo = repository_ctx.attr.toolchain_repo
+ ndk_repo = repository_ctx.attr.ndk_repo
+ api_level = repository_ctx.attr.api_level
+
+ repo_root = "external/" + repository_ctx.name
+ sdk_dir_relative = bundle_dir + "/swift-android"
+ if not repository_ctx.path(sdk_dir_relative + "/swift-sdk.json").exists:
+ fail("The Android Swift SDK bundle has an unexpected layout; " +
+ "missing {}/{}/swift-sdk.json".format(repo_root, sdk_dir_relative))
+ lib_dir = "{}/{}/swift-resources/usr/lib".format(repo_root, sdk_dir_relative)
+
+ # The NDK's sysroot is the SDK to compile against; the Swift SDK bundle
+ # deliberately ships without one (its setup-android-sdk.sh script would
+ # symlink in a locally installed NDK).
+ ndk_root = repository_ctx.path(repository_ctx.attr.ndk_source_properties).dirname
+ prebuilts = ndk_root.get_child("toolchains", "llvm", "prebuilt").readdir()
+ if len(prebuilts) != 1:
+ fail("Expected exactly one prebuilt toolchain in the Android NDK, " +
+ "found: " + str(prebuilts))
+ ndk_sysroot = _execroot_relative_path(prebuilts[0].get_child("sysroot"))
+
+ # The Android Swift SDK's resource directories do not bundle clang's
+ # builtin headers, so the clang importer must be pointed at the host
+ # toolchain's copy (which matches the clang embedded in swiftc).
+ host_usr = repository_ctx.path(repository_ctx.attr.host_swiftc).dirname.dirname
+ clang_versions = host_usr.get_child("lib", "clang").readdir()
+ if len(clang_versions) != 1:
+ fail("Expected exactly one clang version directory in the host " +
+ "toolchain, found: " + str(clang_versions))
+ clang_builtin_headers = _execroot_relative_path(
+ clang_versions[0].get_child("include"),
+ )
+
+ build_content = _BUILD_HEADER_TEMPLATE.format(
+ bundle_dir = bundle_dir,
+ compiler_inputs = _build_list([
+ ":sdk_files",
+ "@{}//:{}".format(toolchain_repo, _HOST_COMPILER_INPUTS),
+ "@{}//:toolchain_files".format(ndk_repo),
+ ]),
+ toolchain_repo = toolchain_repo,
+ )
+
+ build_content += _CC_TOOLCHAIN_TEMPLATE.format(
+ ar = "@{}//:llvm_ar".format(ndk_repo),
+ clang = "@{}//:clang".format(ndk_repo),
+ clang_data = _build_list([
+ ":sdk_files",
+ "@{}//:toolchain_files".format(ndk_repo),
+ ]),
+ )
+
+ for arch in ANDROID_ARCHS:
+ triple = "{}-unknown-linux-android{}".format(arch, api_level)
+ resource_dir = "{}/swift_static-{}".format(lib_dir, arch)
+
+ build_content += _SWIFT_TOOLCHAIN_TEMPLATE.format(
+ arch = arch,
+ copts = _build_list([
+ "-resource-dir",
+ resource_dir,
+ "-Xcc",
+ "-I" + clang_builtin_headers,
+ ]),
+ features = _build_list([
+ "swift.lld_gc_workaround",
+ "swift.module_map_no_private_headers",
+ "swift.use_autolink_extract",
+ "swift.use_module_wrap",
+ # The file prefix map would make the worker resolve the Xcode
+ # developer directory on macOS hosts, which this toolchain
+ # does not depend on.
+ "-swift.file_prefix_map",
+ ]),
+ linker_inputs = _build_list([":sdk_files"]),
+ # The runtime objects and libraries that `swiftc` would add when
+ # statically linking the stdlib for Android; see
+ # `swift_static-{arch}/android/static-stdlib-args.lnk` in the SDK.
+ # The 16 KiB max page size is required by Android 15+.
+ #
+ # NOTE: that `.lnk` also passes `-Wl,--exclude-libs,ALL`, which we
+ # deliberately omit. `swiftc` compiles the user's code into the main
+ # object files and only the Swift runtime arrives via static
+ # archives, so `--exclude-libs,ALL` hides just the runtime there. In
+ # the Bazel model a `swift_binary`'s deps (`swift_library`) are
+ # themselves static archives, so `--exclude-libs,ALL` also demotes
+ # the user's own exported symbols (e.g. `@_cdecl("Java_…")` JNI entry
+ # points defined in a library) to local — they vanish from `.dynsym`
+ # and `System.loadLibrary` can't bind them. Omitting it lets a
+ # `linkshared` library export its symbols; a consumer that wants to
+ # hide the runtime can pass a linker version script listing the
+ # symbols to export (the standard way to control a JNI `.so`).
+ linkopts = _build_list([
+ "{}/android/{}/swiftrt.o".format(resource_dir, arch),
+ "-L{}/android".format(resource_dir),
+ "-ldl",
+ "-llog",
+ "-lm",
+ "-lstdc++",
+ "-Wl,-z,max-page-size=16384",
+ ]),
+ os = "android",
+ sdkroot = ndk_sysroot,
+ suffix = arch,
+ swift_version = repository_ctx.attr.swift_version,
+ )
+
+ build_content += _CC_TOOLCHAIN_FOR_TARGET_TEMPLATE.format(
+ args = _build_list(["--target=" + triple]),
+ link_args = _build_list(["-Wl,-z,max-page-size=16384"]),
+ suffix = arch,
+ triple = triple,
+ )
+
+ repository_ctx.file("BUILD.bazel", build_content)
+
+swift_android_sdk_repository = repository_rule(
+ attrs = _common_attrs() | {
+ "api_level": attr.int(
+ doc = "The Android API level to target.",
+ mandatory = True,
+ ),
+ "host_swiftc": attr.label(
+ doc = """\
+The host toolchain's `swiftc`, used to locate the clang builtin headers that
+match the clang embedded in the Swift compiler.
+""",
+ mandatory = True,
+ ),
+ "ndk_repo": attr.string(
+ doc = "Name of the repository containing the Android NDK.",
+ mandatory = True,
+ ),
+ "ndk_source_properties": attr.label(
+ doc = "The NDK repository's `source.properties` file (its directory is the NDK root).",
+ mandatory = True,
+ ),
+ },
+ doc = """\
+Downloads the Android Swift SDK artifact bundle and defines Swift and C++
+toolchains that target `{aarch64,x86_64}-unknown-linux-android` using a
+standalone host toolchain's Swift compiler and the Android NDK's clang.
+""",
+ implementation = _swift_android_sdk_impl,
+)
diff --git a/swift/internal/extensions/toolchains.bzl b/swift/internal/extensions/toolchains.bzl
index 0caaaef37..cbd8e92f6 100644
--- a/swift/internal/extensions/toolchains.bzl
+++ b/swift/internal/extensions/toolchains.bzl
@@ -43,24 +43,149 @@ toolchain(
"""
-def toolchains_for_platform(platform, toolchain_repository):
+_SDK_TOOLCHAIN_PLATFORM = """
+# Swift SDK toolchains from repository: `{sdk_repository}`
+toolchain(
+ name = "swift_toolchain_{target}_{platform}",
+ exec_compatible_with = {exec_compatible_with},
+ target_compatible_with = {target_compatible_with},
+ toolchain = "@{sdk_repository}//:swift_toolchain_{target_suffix}",
+ toolchain_type = "@rules_swift//toolchains:toolchain_type",
+ visibility = ["//visibility:public"],
+)
+
+toolchain(
+ name = "cc_toolchain_{target}_{platform}",
+ exec_compatible_with = {exec_compatible_with},
+ target_compatible_with = {target_compatible_with},
+ toolchain = "@{sdk_repository}//:cc_toolchain_{target_suffix}",
+ toolchain_type = "@bazel_tools//tools/cpp:toolchain_type",
+ visibility = ["//visibility:public"],
+)
+"""
+
+def _exec_compatible_with_for_platform(platform):
# This assumption is baked into the API so we have to go along with it
if platform == "xcode":
- exec_compatible_with = [
+ return [
"@platforms//os:macos",
]
- else:
- exec_compatible_with = [
- "@platforms//os:linux",
- "@platforms//cpu:{}".format("aarch64" if "aarch64" in platform else "x86_64"),
- ]
+ return [
+ "@platforms//os:linux",
+ "@platforms//cpu:{}".format("aarch64" if "aarch64" in platform else "x86_64"),
+ ]
+def toolchains_for_platform(platform, toolchain_repository):
return _TOOLCHAIN_PLATFORM.format(
- exec_compatible_with = exec_compatible_with,
+ exec_compatible_with = _exec_compatible_with_for_platform(platform),
platform = platform,
toolchain_repository = toolchain_repository,
)
+def wasm_sdk_toolchains_for_platform(platform, sdk_repository):
+ """Returns `toolchain` declarations for a WebAssembly Swift SDK.
+
+ Args:
+ platform: The host platform name (e.g. "xcode" or "ubuntu22.04") whose
+ standalone toolchain the SDK is paired with.
+ sdk_repository: The name of the repository created by
+ `swift_wasm_sdk_repository`.
+
+ Returns:
+ BUILD file content declaring the Swift and C++ toolchains.
+ """
+ return _SDK_TOOLCHAIN_PLATFORM.format(
+ exec_compatible_with = _exec_compatible_with_for_platform(platform),
+ platform = platform,
+ sdk_repository = sdk_repository,
+ target = "wasm32",
+ target_compatible_with = [
+ "@platforms//os:wasi",
+ "@platforms//cpu:wasm32",
+ ],
+ target_suffix = "wasm32",
+ )
+
+def android_sdk_toolchains_for_platform(platform, sdk_repository, archs):
+ """Returns `toolchain` declarations for an Android Swift SDK.
+
+ Args:
+ platform: The host platform name (e.g. "xcode" or "ubuntu22.04") whose
+ standalone toolchain the SDK is paired with.
+ sdk_repository: The name of the repository created by
+ `swift_android_sdk_repository`.
+ archs: The Android architectures ("aarch64", "x86_64") to declare
+ toolchains for.
+
+ Returns:
+ BUILD file content declaring the Swift and C++ toolchains.
+ """
+ content = ""
+ for arch in archs:
+ content += _SDK_TOOLCHAIN_PLATFORM.format(
+ exec_compatible_with = _exec_compatible_with_for_platform(platform),
+ platform = platform,
+ sdk_repository = sdk_repository,
+ target = "android_" + arch,
+ target_compatible_with = [
+ "@platforms//os:android",
+ "@platforms//cpu:" + arch,
+ ],
+ target_suffix = arch,
+ )
+ return content
+
+_NDK_HOST_OS_CONSTRAINT = {
+ "darwin": "@platforms//os:macos",
+ "linux": "@platforms//os:linux",
+}
+
+def android_libcxx_aliases(ndk_repos_by_host, archs):
+ """Returns host-independent aliases for the NDK's `libc++_shared.so`.
+
+ The NDK is fetched into a host-specific repository, but its
+ `libc++_shared.so` (which an APK containing Swift code must bundle) is a
+ target artifact whose content does not depend on the build host. These
+ aliases let packaging rules reference it without naming the host, by
+ selecting the NDK repository for the host the build runs on.
+
+ Args:
+ ndk_repos_by_host: A dict mapping NDK host OS ("darwin", "linux") to
+ the name of the corresponding NDK repository.
+ archs: The Android architectures ("aarch64", "x86_64").
+
+ Returns:
+ BUILD file content declaring one `libcxx_shared_` alias per arch.
+ """
+ hosts = sorted(ndk_repos_by_host.keys())
+ default_repo = ndk_repos_by_host[hosts[0]]
+
+ content = ""
+ for arch in archs:
+ branches = "".join([
+ ' "{}": "@{}//:libcxx_shared_{}",\n'.format(
+ _NDK_HOST_OS_CONSTRAINT[host],
+ ndk_repos_by_host[host],
+ arch,
+ )
+ for host in hosts
+ ])
+ content += """\
+alias(
+ name = "libcxx_shared_{arch}",
+ actual = select({{
+{branches} "//conditions:default": "@{default_repo}//:libcxx_shared_{arch}",
+ }}),
+ visibility = ["//visibility:public"],
+)
+
+""".format(
+ arch = arch,
+ branches = branches,
+ default_repo = default_repo,
+ )
+ return content
+
def _toolchains_impl(repository_ctx):
repository_ctx.file("BUILD.bazel", repository_ctx.attr.build_file_content)
diff --git a/swift/internal/feature_names.bzl b/swift/internal/feature_names.bzl
index e371f611a..c5b8928ae 100644
--- a/swift/internal/feature_names.bzl
+++ b/swift/internal/feature_names.bzl
@@ -321,6 +321,13 @@ SWIFT_FEATURE_DECLARE_SWIFTSOURCEINFO = "swift.emit_swiftsourceinfo"
# system command line limit.
SWIFT_FEATURE_NO_EMBED_DEBUG_MODULE = "swift.no_embed_debug_module"
+# If enabled, the entry point of a `swift_binary` is not renamed to a
+# target-specific symbol (which is otherwise aliased back to `main` at link
+# time so that the binary's code can also be linked into another binary, such
+# as a test executable). Toolchains whose linkers cannot create such aliases
+# (e.g. wasm-ld, which has no `--defsym`) should enable this feature.
+SWIFT_FEATURE_NO_ENTRY_POINT_RENAME = "swift.no_entry_point_rename"
+
# If enabled, the toolchain will directly generate from the raw proto files
# and not from the DescriptorSets.
#
diff --git a/swift/internal/swift_autoconfiguration.bzl b/swift/internal/swift_autoconfiguration.bzl
index 35b183cb8..fa3ed5d4d 100644
--- a/swift/internal/swift_autoconfiguration.bzl
+++ b/swift/internal/swift_autoconfiguration.bzl
@@ -179,6 +179,22 @@ def _normalized_linux_cpu(cpu):
return "x86_64"
return cpu
+def _normalized_windows_cpu(cpu):
+ """Normalizes a host CPU name to the value Swift uses on Windows.
+
+ The returned value is used both as the toolchain's `arch` and as the
+ architecture component of the Swift SDK's library layout (for example
+ `usr/lib/swift/windows/x86_64`) and the target triple.
+ """
+ cpu = cpu.lower()
+ if cpu in ("amd64", "x86_64", "x64"):
+ return "x86_64"
+ if cpu in ("arm64", "aarch64"):
+ return "aarch64"
+ if cpu in ("x86", "i686"):
+ return "i686"
+ return cpu
+
def _resolve_toolchain_root(repository_ctx, swiftc_path):
"""Returns the Swift toolchain root directory for `swiftc_path`.
@@ -269,15 +285,27 @@ xcode_swift_toolchain(
]),
)
+def _python_executable_works(repository_ctx, python_bin):
+ """Returns True if `python_bin` is a real, runnable Python interpreter.
+
+ On Windows, `python3.exe`/`python.exe` found on `PATH` are frequently the
+ Microsoft Store "App execution alias" stubs rather than real interpreters:
+ when run non-interactively they print a message pointing at the Store and
+ exit nonzero. Probe the candidate so those stubs are skipped in favor of a
+ working interpreter later on `PATH`.
+ """
+ if not python_bin:
+ return False
+ result = repository_ctx.execute([python_bin, "-c", "print('ok')"])
+ return result.return_code == 0 and result.stdout.strip() == "ok"
+
def _get_python_bin(repository_ctx):
if "PYTHON_BIN_PATH" in repository_ctx.os.environ:
return repository_ctx.os.environ.get("PYTHON_BIN_PATH").strip()
- out = repository_ctx.which("python3.exe")
- if out:
- return out
- out = repository_ctx.which("python.exe")
- if out:
- return out
+ for name in ("python3.exe", "python.exe", "python3", "python"):
+ candidate = repository_ctx.which(name)
+ if _python_executable_works(repository_ctx, candidate):
+ return candidate
return None
def _create_windows_toolchain(*, repository_ctx):
@@ -294,6 +322,7 @@ Swift toolchain.
"""
root = path_to_swiftc.dirname.dirname
+ arch = _normalized_windows_cpu(repository_ctx.os.arch)
enabled_features = [
SWIFT_FEATURE_CODEVIEW_DEBUG_INFO,
SWIFT_FEATURE_DECLARE_SWIFTSOURCEINFO,
@@ -305,11 +334,25 @@ Swift toolchain.
disabled_features = []
version_file, parsed_version = _write_swift_version(repository_ctx, path_to_swiftc)
+
+ # Normalize SDKROOT to forward slashes with no trailing separator: the raw
+ # environment value typically ends in a backslash, which is both invalid at
+ # the end of a Python raw-string literal (used below) and produces doubled
+ # separators when joined.
+ sdkroot = repository_ctx.os.environ["SDKROOT"].replace("\\", "/").rstrip("/")
+
+ # The platform `Info.plist` (which records the bundled XCTest version) sits
+ # three levels above the SDK root, i.e. `/Info.plist`.
+ info_plist = sdkroot + "/../../../Info.plist"
+ python_bin = _get_python_bin(repository_ctx)
+ if not python_bin:
+ fail("Could not find a working Python 3 interpreter on PATH; it is " +
+ "required to read the XCTest version from the Swift SDK's Info.plist.")
xctest_version = repository_ctx.execute([
- _get_python_bin(repository_ctx),
+ python_bin,
"-c",
- "import os, plistlib; " +
- "print(plistlib.loads(open(os.path.join(r'{}', '..', '..', '..', 'Info.plist'), 'rb').read(), fmt=plistlib.FMT_XML)['DefaultProperties']['XCTEST_VERSION'])".format(repository_ctx.os.environ["SDKROOT"]),
+ "import plistlib; " +
+ "print(plistlib.load(open(r'{}', 'rb'))['DefaultProperties']['XCTEST_VERSION'])".format(info_plist),
])
env = {
@@ -320,7 +363,7 @@ Swift toolchain.
return """\
swift_toolchain(
name = "windows-toolchain",
- arch = "x86_64",
+ arch = "{arch}",
features = [{features}],
os = "windows",
root = "{root}",
@@ -332,11 +375,12 @@ swift_toolchain(
xctest_version = "{xctest_version}",
)
""".format(
+ arch = arch,
features = ", ".join(['"{}"'.format(feature) for feature in enabled_features] + ['"-{}"'.format(feature) for feature in disabled_features]),
root = root,
env = env,
parsed_version = parsed_version,
- sdkroot = repository_ctx.os.environ["SDKROOT"].replace("\\", "/"),
+ sdkroot = sdkroot,
xctest_version = xctest_version.stdout.rstrip(),
version_file = version_file,
)
diff --git a/swift/swift_binary.bzl b/swift/swift_binary.bzl
index 10689482c..fefd1c3f4 100644
--- a/swift/swift_binary.bzl
+++ b/swift/swift_binary.bzl
@@ -14,6 +14,7 @@
"""Implementation of the `swift_binary` rule."""
+load("@bazel_skylib//lib:dicts.bzl", "dicts")
load("@bazel_skylib//lib:paths.bzl", "paths")
load("@rules_cc//cc/common:cc_common.bzl", "cc_common")
load("@rules_cc//cc/common:cc_info.bzl", "CcInfo")
@@ -22,6 +23,7 @@ load("//swift/internal:compiling.bzl", "compile")
load(
"//swift/internal:feature_names.bzl",
"SWIFT_FEATURE_ADD_TARGET_NAME_TO_OUTPUT",
+ "SWIFT_FEATURE_NO_ENTRY_POINT_RENAME",
)
load("//swift/internal:features.bzl", "is_feature_enabled")
load(
@@ -76,6 +78,12 @@ def _maybe_parse_as_library_copts(srcs):
srcs[0].basename != "main.swift"
return ["-parse-as-library"] if use_parse_as_library else []
+def _is_wasm(ctx):
+ """Returns True if the target platform is WebAssembly."""
+ return ctx.target_platform_has_constraint(
+ ctx.attr._wasi_os_constraint[platform_common.ConstraintValueInfo],
+ )
+
def _swift_binary_impl(ctx):
toolchains = find_all_toolchains(ctx)
feature_configuration = configure_features_for_binary(
@@ -85,6 +93,17 @@ def _swift_binary_impl(ctx):
unsupported_features = ctx.disabled_features,
)
+ is_wasm = _is_wasm(ctx)
+
+ # A binary linked as a shared object (`linkshared`) or a WebAssembly
+ # reactor has no `main`, so the entry-point rename (and the matching
+ # `--defsym main=...` at link time) must be skipped, just as it is when the
+ # toolchain requests it via `swift.no_entry_point_rename`.
+ skip_entry_point = ctx.attr.linkshared or is_feature_enabled(
+ feature_configuration = feature_configuration,
+ feature_name = SWIFT_FEATURE_NO_ENTRY_POINT_RENAME,
+ )
+
srcs = ctx.files.srcs
output_groups = {}
module_contexts = []
@@ -99,7 +118,21 @@ def _swift_binary_impl(ctx):
ctx.label,
feature_configuration = feature_configuration,
)
- entry_point_name = entry_point_function_name(module_name)
+
+ if skip_entry_point:
+ entry_point_name = None
+ entry_point_copts = []
+ else:
+ # Use a custom entry point name so that the binary's code can
+ # also be linked into another process (like a test executable)
+ # without having its main function collide.
+ entry_point_name = entry_point_function_name(module_name)
+ entry_point_copts = [
+ "-Xfrontend",
+ "-entry-point-function-name",
+ "-Xfrontend",
+ entry_point_name,
+ ]
include_dev_srch_paths = include_developer_search_paths(ctx.attr)
@@ -111,15 +144,7 @@ def _swift_binary_impl(ctx):
ctx,
ctx.attr.copts,
ctx.attr.swiftc_inputs,
- ) + _maybe_parse_as_library_copts(srcs) + [
- # Use a custom entry point name so that the binary's code can
- # also be linked into another process (like a test executable)
- # without having its main function collide.
- "-Xfrontend",
- "-entry-point-function-name",
- "-Xfrontend",
- entry_point_name,
- ],
+ ) + _maybe_parse_as_library_copts(srcs) + entry_point_copts,
defines = ctx.attr.defines,
feature_configuration = feature_configuration,
include_dev_srch_paths = include_dev_srch_paths,
@@ -178,6 +203,24 @@ def _swift_binary_impl(ctx):
else:
name = ctx.label.name
+ # When targeting WebAssembly a `linkshared` binary is a "reactor" module: it
+ # is still produced as an `executable`-shaped wasm file (not a `-shared`
+ # dynamic library), but linked with the reactor execution model so it has no
+ # `_start`/`main` and instead exports functions for a host to call.
+ # Everywhere else, `linkshared` produces a real dynamic library
+ # (`lib.so` / `.dylib`), matching `cc_binary`'s `linkshared`.
+ shared_link_flags = []
+ if ctx.attr.linkshared and not is_wasm:
+ output_type = "dynamic_library"
+ else:
+ output_type = "executable"
+ if ctx.attr.linkshared and is_wasm:
+ shared_link_flags = ["-mexec-model=reactor"]
+
+ # Give WebAssembly outputs the conventional `.wasm` extension.
+ if is_wasm:
+ name = name + ".wasm"
+
linking_outputs = register_link_binary_action(
actions = ctx.actions,
additional_inputs = ctx.files.additional_linker_inputs,
@@ -189,18 +232,29 @@ def _swift_binary_impl(ctx):
label = ctx.label,
module_contexts = module_contexts,
name = name,
- output_type = "executable",
+ output_type = output_type,
stamp = ctx.attr.stamp,
toolchains = toolchains,
- user_link_flags = binary_link_flags + entry_point_linkopts,
+ user_link_flags = (
+ binary_link_flags + entry_point_linkopts + shared_link_flags
+ ),
variables_extension = variables_extension,
)
+ if output_type == "dynamic_library":
+ library_to_link = linking_outputs.library_to_link
+ output_file = (
+ library_to_link.resolved_symlink_dynamic_library or
+ library_to_link.dynamic_library
+ )
+ else:
+ output_file = linking_outputs.executable
+
providers = [
DefaultInfo(
- executable = linking_outputs.executable,
+ executable = output_file,
files = depset(
- [linking_outputs.executable] + additional_debug_outputs,
+ [output_file] + additional_debug_outputs,
),
runfiles = ctx.runfiles(
collect_data = True,
@@ -278,9 +332,36 @@ def _swift_binary_impl(ctx):
return providers
swift_binary = rule(
- attrs = binary_rule_attrs(
- additional_deps_providers = [[SwiftCompilerPluginInfo]],
- stamp_default = -1,
+ attrs = dicts.add(
+ binary_rule_attrs(
+ additional_deps_providers = [[SwiftCompilerPluginInfo]],
+ stamp_default = -1,
+ ),
+ {
+ "linkshared": attr.bool(
+ default = False,
+ doc = """\
+If `True`, link the target as a shared library / loadable module instead of an
+executable, similar to `cc_binary`'s `linkshared`. The binary has no `main`
+entry point and the renamed-entry-point machinery is disabled.
+
+On most platforms this produces a dynamic library named `lib.so`
+(`.dylib` on Apple platforms) suitable for loading with `dlopen` /
+`System.loadLibrary` (e.g. an Android JNI library; export functions with
+`@_cdecl`).
+
+When targeting WebAssembly it instead produces a "reactor" module
+(`.wasm`, linked with `-mexec-model=reactor`): the module has no
+`_start`, runs its initializers via the exported `_initialize`, and exposes
+the functions a host instantiates and calls. Force-export those functions by
+passing `-Xlinker --export=` (or `-Wl,--export=`) flags in
+`linkopts`.
+""",
+ ),
+ "_wasi_os_constraint": attr.label(
+ default = Label("@platforms//os:wasi"),
+ ),
+ },
),
doc = """\
Compiles and links Swift code into an executable binary.
@@ -296,6 +377,9 @@ If you want to create a multi-architecture binary or a bundled application,
please use one of the platform-specific application rules in
[rules_apple](https://github.com/bazelbuild/rules_apple) instead of
`swift_binary`.
+
+Setting `linkshared = True` links a shared library or (on WebAssembly) a
+reactor module instead of an executable; see the `linkshared` attribute.
""",
exec_groups = {
# The `plugins` attribute associates its `exec` transition with this
diff --git a/swift/swift_test.bzl b/swift/swift_test.bzl
index f6065bfa1..25cd3d8a9 100644
--- a/swift/swift_test.bzl
+++ b/swift/swift_test.bzl
@@ -94,6 +94,7 @@ def _generate_test_discovery_srcs(
*,
actions,
deps,
+ env = {},
name,
objc_test_discovery,
owner_module_name,
@@ -109,6 +110,9 @@ def _generate_test_discovery_srcs(
Args:
actions: The context's actions object.
deps: The list of direct dependencies of the test target.
+ env: Environment variables to set when running the discovery tool. On
+ Windows this must include a `Path` that contains the Swift runtime
+ DLLs, otherwise the (Swift) discovery executable fails to launch.
name: The name of the target being built, which will be used to derive
the basename of the directory containing the generated files.
objc_test_discovery: If `True`, the runner should use Objective-C-based
@@ -197,6 +201,7 @@ def _generate_test_discovery_srcs(
actions.run(
arguments = [args],
+ env = env,
executable = test_discoverer,
exec_group = _DISCOVER_TESTS_EXEC_GROUP,
inputs = inputs,
@@ -411,6 +416,7 @@ def _swift_test_impl(ctx):
discovery_srcs = _generate_test_discovery_srcs(
actions = ctx.actions,
deps = ctx.attr.deps,
+ env = toolchains.swift.test_configuration.env,
name = ctx.label.name,
objc_test_discovery = objc_test_discovery,
owner_module_name = module_name,
diff --git a/swift/toolchains/BUILD b/swift/toolchains/BUILD
index 89d84641d..df9328c65 100644
--- a/swift/toolchains/BUILD
+++ b/swift/toolchains/BUILD
@@ -138,6 +138,21 @@ toolchain(
visibility = ["//visibility:public"],
)
+toolchain(
+ name = "windows-swift-toolchain-aarch64",
+ exec_compatible_with = [
+ "@platforms//os:windows",
+ "@platforms//cpu:aarch64",
+ ],
+ target_compatible_with = [
+ "@platforms//os:windows",
+ "@platforms//cpu:aarch64",
+ ],
+ toolchain = "@rules_swift_local_config//:windows-toolchain",
+ toolchain_type = "//toolchains:toolchain_type",
+ visibility = ["//visibility:public"],
+)
+
# Consumed by Bazel integration tests.
filegroup(
name = "for_bazel_tests",
diff --git a/swift/toolchains/swift_toolchain.bzl b/swift/toolchains/swift_toolchain.bzl
index af31dcec9..905c3e303 100644
--- a/swift/toolchains/swift_toolchain.bzl
+++ b/swift/toolchains/swift_toolchain.bzl
@@ -267,6 +267,7 @@ def _all_action_configs(os, arch, target_triple, sdkroot, xctest_version, additi
actions = all_compile_action_names() + [
SWIFT_ACTION_DUMP_AST,
SWIFT_ACTION_PRECOMPILE_C_MODULE,
+ SWIFT_ACTION_SYMBOL_GRAPH_EXTRACT,
],
configurators = [
add_arg(
@@ -294,6 +295,7 @@ def _all_action_configs(os, arch, target_triple, sdkroot, xctest_version, additi
actions = all_compile_action_names() + [
SWIFT_ACTION_DUMP_AST,
SWIFT_ACTION_PRECOMPILE_C_MODULE,
+ SWIFT_ACTION_SYMBOL_GRAPH_EXTRACT,
],
configurators = [
add_arg(
@@ -364,6 +366,13 @@ def _swift_windows_linkopts_cc_info(
"-LIBPATH:{}".format(platform_lib_dir),
"-LIBPATH:{}".format(paths.join(sdkroot, "..", "..", "Library", "XCTest-{}".format(xctest_version), "usr", "lib", "swift", "windows", arch)),
runtime_object_path,
+ # Swift marks references to symbols in other modules (for example the
+ # type metadata accessors a generated test runner references via
+ # `@testable import`) as `dllimport`. Bazel links everything statically,
+ # so those symbols resolve locally and `link.exe` emits LNK4217. The
+ # warning is benign for static linking; suppress it so it is not fatal
+ # under `/WX` (treat-warnings-as-errors).
+ "-IGNORE:4217",
]
return CcInfo(
@@ -443,12 +452,58 @@ def _swift_unix_linkopts_cc_info(
),
)
+def _swift_sdk_linkopts_cc_info(
+ toolchain_label,
+ linkopts,
+ linker_inputs):
+ """Returns a `CcInfo` with linker flags provided by the toolchain target.
+
+ This is used for toolchains that target platforms whose Swift runtime
+ libraries come from a Swift SDK (such as WebAssembly or Android) rather
+ than from the host toolchain or system; the repository that defines the
+ toolchain provides the exact search paths and runtime objects to link.
+
+ Args:
+ toolchain_label: The label of the Swift toolchain that will act as the
+ owner of the linker input propagating the flags.
+ linkopts: A list of linker flags from the toolchain's `linkopts`
+ attribute.
+ linker_inputs: A list of `File`s that should be available to link
+ actions using these flags.
+
+ Returns:
+ A `CcInfo` provider that will provide linker flags to binaries that
+ depend on Swift targets.
+ """
+ return CcInfo(
+ linking_context = cc_common.create_linking_context(
+ linker_inputs = depset([
+ cc_common.create_linker_input(
+ owner = toolchain_label,
+ user_link_flags = depset(linkopts),
+ additional_inputs = depset(linker_inputs),
+ ),
+ ]),
+ ),
+ )
+
def _entry_point_linkopts_provider(*, entry_point_name):
"""Returns linkopts to customize the entry point of a binary."""
return struct(
linkopts = ["-Wl,--defsym,main={}".format(entry_point_name)],
)
+def _windows_entry_point_linkopts_provider(*, entry_point_name):
+ """Returns linkopts to customize the entry point of a binary on Windows.
+
+ MSVC `link.exe` does not understand the GNU `ld` `--defsym` alias used on
+ other platforms; `/ALTERNATENAME` is the equivalent, resolving the
+ CRT-referenced `main` symbol to the renamed Swift entry point.
+ """
+ return struct(
+ linkopts = ["/ALTERNATENAME:main={}".format(entry_point_name)],
+ )
+
def _parse_target_system_name(*, arch, os, target_system_name):
"""Returns the target system name set by the CC toolchain or attempts to create one based on the OS and arch."""
@@ -457,6 +512,13 @@ def _parse_target_system_name(*, arch, os, target_system_name):
if os == "linux":
return "%s-unknown-linux-gnu" % arch
+ elif os == "windows":
+ # The MSVC cc toolchain reports a `target_gnu_system_name` of "local",
+ # so synthesize the triple. The `-msvc` environment is required: while
+ # `swiftc` defaults to it, `swift-symbolgraph-extract` (used for XCTest
+ # test discovery) matches the module layout's triple exactly and fails
+ # to load modules built for `*-windows-msvc` if it is omitted.
+ return "%s-unknown-windows-msvc" % arch
else:
return "%s-unknown-%s" % (arch, os)
@@ -472,7 +534,12 @@ def _swift_toolchain_impl(ctx):
target_triples.parse(ctx.var.get("CC_TARGET_TRIPLE") or target_system_name),
)
- if "clang" not in cc_toolchain.compiler:
+ # On Windows the Swift toolchain composes with Bazel's MSVC C++ toolchain
+ # (`msvc-cl`): `swiftc` uses its bundled clang for the clang importer and
+ # links via MSVC `link.exe`, so a clang CC toolchain is neither present nor
+ # required. Elsewhere (Linux), Swift drives the configured cc toolchain for
+ # C/C++ interop and linking, which must be clang.
+ if ctx.attr.os != "windows" and "clang" not in cc_toolchain.compiler:
fail("Swift requires the configured CC toolchain use clang. " +
"Either use the locally installed LLVM by setting `CC=clang` in your environment " +
"before invoking Bazel, or configure a Bazel LLVM CC toolchain. " +
@@ -509,6 +576,12 @@ def _swift_toolchain_impl(ctx):
)
elif ctx.attr.os == "none":
swift_linkopts_cc_info = CcInfo()
+ elif ctx.attr.linkopts or ctx.attr.linker_inputs:
+ swift_linkopts_cc_info = _swift_sdk_linkopts_cc_info(
+ ctx.label,
+ ctx.attr.linkopts,
+ ctx.files.linker_inputs,
+ )
else:
swift_linkopts_cc_info = _swift_unix_linkopts_cc_info(
ctx.attr.arch,
@@ -574,7 +647,7 @@ def _swift_toolchain_impl(ctx):
bindir = "bin64"
elif ctx.attr.arch == "i686":
bindir = "bin32"
- elif ctx.attr.arch == "arm64":
+ elif ctx.attr.arch in ("aarch64", "arm64"):
bindir = "bin64a"
else:
fail("unsupported arch `{}`".format(ctx.attr.arch))
@@ -600,7 +673,9 @@ def _swift_toolchain_impl(ctx):
cross_import_overlays = collect_cross_import_overlays(ctx.attr.cross_import_overlays),
debug_outputs_provider = None,
developer_dirs = [],
- entry_point_linkopts_provider = _entry_point_linkopts_provider,
+ entry_point_linkopts_provider = (
+ _windows_entry_point_linkopts_provider if ctx.attr.os == "windows" else _entry_point_linkopts_provider
+ ),
feature_allowlists = [
target[SwiftFeatureAllowlistInfo]
for target in ctx.attr.feature_allowlists
@@ -763,6 +838,25 @@ normally.
""",
mandatory = False,
),
+ "linker_inputs": attr.label_list(
+ allow_files = True,
+ doc = """\
+Files that must be available to link actions when `linkopts` is set, such as
+the Swift runtime libraries of a Swift SDK.
+""",
+ ),
+ "linkopts": attr.string_list(
+ doc = """\
+Linker flags that must be passed when linking binaries that contain Swift
+code, such as search paths for (and inputs from) the `linker_inputs`
+attribute.
+
+When set, these flags *replace* the default flags that the toolchain would
+otherwise compute for the target operating system; they are meant to be used
+by toolchains whose Swift runtime libraries come from a Swift SDK (for
+example, WebAssembly or Android) rather than from the host toolchain.
+""",
+ ),
"sdkroot": attr.string(
doc = """\
The root of a SDK to be used for building the target.
diff --git a/tools/test_observer/BUILD b/tools/test_observer/BUILD
index 241cd7a39..0c9ead01b 100644
--- a/tools/test_observer/BUILD
+++ b/tools/test_observer/BUILD
@@ -10,11 +10,11 @@ swift_library(
"BazelXMLTestObserver.swift",
"Concurrency.swift",
"JSON.swift",
- "LinuxXCTestRunner.swift",
"Locked.swift",
"ObjectiveCXCTestRunner.swift",
"ShardingFilteringTestCollector.swift",
"StringInterpolation+XMLEscaping.swift",
+ "SwiftCorelibsXCTestRunner.swift",
"SwiftTestingRunner.swift",
"XUnitTestRecorder.swift",
],
diff --git a/tools/test_observer/Locked.swift b/tools/test_observer/Locked.swift
index 06a897817..51b44b436 100644
--- a/tools/test_observer/Locked.swift
+++ b/tools/test_observer/Locked.swift
@@ -16,10 +16,53 @@
import Darwin
#elseif canImport(Glibc)
import Glibc
+#elseif canImport(WinSDK)
+ import WinSDK
#else
#error("Unsupported platform")
#endif
+// The platform lock primitive stored alongside the value. POSIX platforms use a
+// `pthread_mutex_t`; Windows uses a slim reader/writer lock (`SRWLOCK`), which
+// needs no explicit destruction.
+#if canImport(WinSDK)
+ private typealias LockPrimitive = SRWLOCK
+#else
+ private typealias LockPrimitive = pthread_mutex_t
+#endif
+
+private func _lockInitialize(_ lock: UnsafeMutablePointer) {
+ #if canImport(WinSDK)
+ InitializeSRWLock(lock)
+ #else
+ _ = pthread_mutex_init(lock, nil)
+ #endif
+}
+
+private func _lockDestroy(_ lock: UnsafeMutablePointer) {
+ #if canImport(WinSDK)
+ // `SRWLOCK`s do not require destruction.
+ #else
+ _ = pthread_mutex_destroy(lock)
+ #endif
+}
+
+private func _lockAcquire(_ lock: UnsafeMutablePointer) {
+ #if canImport(WinSDK)
+ AcquireSRWLockExclusive(lock)
+ #else
+ _ = pthread_mutex_lock(lock)
+ #endif
+}
+
+private func _lockRelease(_ lock: UnsafeMutablePointer) {
+ #if canImport(WinSDK)
+ ReleaseSRWLockExclusive(lock)
+ #else
+ _ = pthread_mutex_unlock(lock)
+ #endif
+}
+
/// A wrapper around a value that can be accessed safely from multiple threads in synchronized
/// contexts.
///
@@ -27,10 +70,10 @@
/// as XCTest's observer, both are called in synchronous contexts only, but we don't know what
/// thread the calls are coming from.
public struct Locked: Sendable where Value: Sendable {
- private final class _Storage: ManagedBuffer {
+ private final class _Storage: ManagedBuffer {
deinit {
withUnsafeMutablePointerToElements { lock in
- _ = pthread_mutex_destroy(lock)
+ _lockDestroy(lock)
}
}
}
@@ -38,9 +81,9 @@ public struct Locked: Sendable where Value: Sendable {
// Swift 6 requires this to be declared as `nonisolated(unsafe)`, but older compilers emit a
// warning claiming (incorrectly) that it's redundant.
#if compiler(>=6)
- private nonisolated(unsafe) var _storage: ManagedBuffer
+ private nonisolated(unsafe) var _storage: ManagedBuffer
#else
- private var _storage: ManagedBuffer
+ private var _storage: ManagedBuffer
#endif
/// The value behind the lock.
@@ -52,7 +95,7 @@ public struct Locked: Sendable where Value: Sendable {
public init(_ value: Value) {
_storage = _Storage.create(minimumCapacity: 1, makingHeaderWith: { _ in value })
_storage.withUnsafeMutablePointerToElements { lock in
- _ = pthread_mutex_init(lock, nil)
+ _lockInitialize(lock)
}
}
@@ -65,8 +108,8 @@ public struct Locked: Sendable where Value: Sendable {
_ body: (inout Value) throws -> Result
) rethrows -> Result {
try _storage.withUnsafeMutablePointers { rawValue, lock in
- _ = pthread_mutex_lock(lock)
- defer { _ = pthread_mutex_unlock(lock) }
+ _lockAcquire(lock)
+ defer { _lockRelease(lock) }
return try body(&rawValue.pointee)
}
}
diff --git a/tools/test_observer/LinuxXCTestRunner.swift b/tools/test_observer/SwiftCorelibsXCTestRunner.swift
similarity index 89%
rename from tools/test_observer/LinuxXCTestRunner.swift
rename to tools/test_observer/SwiftCorelibsXCTestRunner.swift
index 0ffd9a5b1..811b0ddbc 100644
--- a/tools/test_observer/LinuxXCTestRunner.swift
+++ b/tools/test_observer/SwiftCorelibsXCTestRunner.swift
@@ -12,18 +12,20 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#if os(Linux)
+#if os(Linux) || os(Windows)
import Foundation
import XCTest
- public typealias XCTestRunner = LinuxXCTestRunner
+ public typealias XCTestRunner = SwiftCorelibsXCTestRunner
- /// A test runner for tests that use the XCTest framework on Linux.
+ /// A test runner for tests that use the XCTest framework on platforms that use
+ /// swift-corelibs-xctest (Linux and Windows).
///
/// This test runner uses test case entries that were constructed by scanning the symbol graph
- /// output of the compiler.
+ /// output of the compiler, since those platforms lack the Objective-C runtime used for test
+ /// discovery on Apple platforms.
@MainActor
- public enum LinuxXCTestRunner {
+ public enum SwiftCorelibsXCTestRunner {
/// A wrapper around a single test from an `XCTestCaseEntry` used by the test collector.
private struct Test: Testable {
/// The type of the `XCTestCase` that contains the test.
diff --git a/tools/test_observer/SwiftTestingRunner.swift b/tools/test_observer/SwiftTestingRunner.swift
index 87b461b0a..4bf6a71b2 100644
--- a/tools/test_observer/SwiftTestingRunner.swift
+++ b/tools/test_observer/SwiftTestingRunner.swift
@@ -18,6 +18,8 @@ import Foundation
import Darwin
#elseif canImport(Glibc)
import Glibc
+#elseif canImport(WinSDK)
+ import WinSDK
#else
#error("Unsupported platform")
#endif
@@ -293,7 +295,7 @@ private struct SwiftTestingEntryPoint {
/// Creates the entry point by looking it up by name in the current process, or fails if the
/// entry point is not found.
init?() {
- guard let entryPointRaw = dlsym(rtldDefault, "swt_abiv0_getEntryPoint") else {
+ guard let entryPointRaw = _loadSwiftTestingSymbol("swt_abiv0_getEntryPoint") else {
return nil
}
let abiv0_getEntryPoint = unsafeBitCast(
@@ -344,18 +346,43 @@ private struct SwiftTestingEntryPoint {
}
}
-// `RTLD_DEFAULT` is only defined on Linux when `_GNU_SOURCE` is defined. Just redefine it
-// here for convenience.
-#if compiler(>=5.10)
- #if os(Linux)
- private nonisolated(unsafe) let rtldDefault = UnsafeMutableRawPointer(bitPattern: 0)
- #else
- private nonisolated(unsafe) let rtldDefault = UnsafeMutableRawPointer(bitPattern: -2)
- #endif
+/// Looks up a symbol exported by the swift-testing framework in the current process, returning
+/// `nil` if it is not present (i.e. swift-testing was not linked into the test binary).
+#if canImport(WinSDK)
+ private func _loadSwiftTestingSymbol(_ name: String) -> UnsafeMutableRawPointer? {
+ // `GetProcAddress` resolves a symbol from a specific module, so check the modules that may
+ // export the swift-testing ABI: the test executable itself (when statically linked) and the
+ // `Testing.dll` shared library.
+ let modules: [HMODULE?] = [
+ GetModuleHandleW(nil),
+ "Testing.dll".withCString(encodedAs: UTF16.self) { GetModuleHandleW($0) },
+ ]
+ for module in modules {
+ guard let module else { continue }
+ if let symbol = name.withCString({ GetProcAddress(module, $0) }) {
+ return unsafeBitCast(symbol, to: UnsafeMutableRawPointer.self)
+ }
+ }
+ return nil
+ }
#else
- #if os(Linux)
- private let rtldDefault = UnsafeMutableRawPointer(bitPattern: 0)
+ // `RTLD_DEFAULT` is only defined on Linux when `_GNU_SOURCE` is defined. Just redefine it
+ // here for convenience.
+ #if compiler(>=5.10)
+ #if os(Linux)
+ private nonisolated(unsafe) let rtldDefault = UnsafeMutableRawPointer(bitPattern: 0)
+ #else
+ private nonisolated(unsafe) let rtldDefault = UnsafeMutableRawPointer(bitPattern: -2)
+ #endif
#else
- private let rtldDefault = UnsafeMutableRawPointer(bitPattern: -2)
+ #if os(Linux)
+ private let rtldDefault = UnsafeMutableRawPointer(bitPattern: 0)
+ #else
+ private let rtldDefault = UnsafeMutableRawPointer(bitPattern: -2)
+ #endif
#endif
+
+ private func _loadSwiftTestingSymbol(_ name: String) -> UnsafeMutableRawPointer? {
+ return dlsym(rtldDefault, name)
+ }
#endif
diff --git a/tools/worker/work_processor.cc b/tools/worker/work_processor.cc
index df6021c84..5e79f94a4 100644
--- a/tools/worker/work_processor.cc
+++ b/tools/worker/work_processor.cc
@@ -33,6 +33,30 @@
namespace {
+#if defined(_WIN32)
+// On Windows, `std::filesystem` honors the legacy MAX_PATH (260 character) limit
+// unless a path uses the extended-length "\\?\" prefix. The incremental storage
+// area (`bazel-out/.../_swift_incremental/...`) routinely produces paths longer
+// than that, so normalize to an absolute, normalized, backslash-separated path
+// with the prefix applied before performing filesystem operations.
+std::filesystem::path LongPath(const std::filesystem::path& path) {
+ std::error_code ec;
+ std::filesystem::path absolute = std::filesystem::absolute(path, ec);
+ if (ec) {
+ return path;
+ }
+ std::wstring native = absolute.lexically_normal().make_preferred().wstring();
+ if (native.compare(0, 4, L"\\\\?\\") != 0) {
+ native.insert(0, L"\\\\?\\");
+ }
+ return std::filesystem::path(native);
+}
+#else
+std::filesystem::path LongPath(const std::filesystem::path& path) {
+ return path;
+}
+#endif
+
bool copy_file(const std::filesystem::path& from,
const std::filesystem::path& to, std::error_code& ec) noexcept {
#if defined(__APPLE__)
@@ -44,7 +68,7 @@ bool copy_file(const std::filesystem::path& from,
ec = std::error_code();
return true;
#else
- return std::filesystem::copy_file(from, to, ec);
+ return std::filesystem::copy_file(LongPath(from), LongPath(to), ec);
#endif
}
@@ -52,7 +76,7 @@ static bool TouchFile(const std::filesystem::path& path,
std::ostringstream& output) {
std::error_code ec;
if (!path.parent_path().empty()) {
- std::filesystem::create_directories(path.parent_path(), ec);
+ std::filesystem::create_directories(LongPath(path.parent_path()), ec);
if (ec) {
output << "swift_worker: Could not create directory "
<< path.parent_path() << " (" << ec.message() << ")\n";
@@ -60,7 +84,7 @@ static bool TouchFile(const std::filesystem::path& path,
}
}
- std::ofstream stream(path);
+ std::ofstream stream(LongPath(path));
if (!stream) {
output << "swift_worker: Could not create " << path << "\n";
return false;
@@ -227,7 +251,7 @@ void WorkProcessor::ProcessWorkRequest(
// requested.
if (!emit_swift_source_info &&
expected_object_path.extension() == ".swiftsourceinfo") {
- std::filesystem::remove(expected_object_path);
+ std::filesystem::remove(LongPath(expected_object_path));
}
// Bazel creates the intermediate directories for the files declared at
@@ -251,7 +275,7 @@ void WorkProcessor::ProcessWorkRequest(
for (const auto& dir_path : dir_paths) {
std::error_code ec;
- std::filesystem::create_directories(dir_path, ec);
+ std::filesystem::create_directories(LongPath(dir_path), ec);
if (ec) {
stderr_stream << "swift_worker: Could not create directory " << dir_path
<< " (" << ec.message() << ")\n";
@@ -267,7 +291,7 @@ void WorkProcessor::ProcessWorkRequest(
auto inputs = output_file_map.incremental_inputs();
bool all_inputs_exist = std::all_of(
inputs.cbegin(), inputs.cend(), [](const auto& expected_object_pair) {
- return std::filesystem::exists(expected_object_pair.second);
+ return std::filesystem::exists(LongPath(expected_object_pair.second));
});
if (all_inputs_exist) {
@@ -286,12 +310,12 @@ void WorkProcessor::ProcessWorkRequest(
} else {
auto cleanup_outputs = output_file_map.incremental_cleanup_outputs();
for (const auto& cleanup_output : cleanup_outputs) {
- if (!std::filesystem::exists(cleanup_output)) {
+ if (!std::filesystem::exists(LongPath(cleanup_output))) {
continue;
}
std::error_code ec;
- std::filesystem::remove(cleanup_output, ec);
+ std::filesystem::remove(LongPath(cleanup_output), ec);
if (ec) {
stderr_stream << "swift_worker: Could not remove " << cleanup_output
<< " (" << ec.message() << ")\n";
@@ -337,10 +361,10 @@ void WorkProcessor::ProcessWorkRequest(
// next run.
for (const auto& expected_object_pair :
output_file_map.incremental_inputs()) {
- if (std::filesystem::exists(expected_object_pair.first)) {
- if (std::filesystem::exists(expected_object_pair.second)) {
+ if (std::filesystem::exists(LongPath(expected_object_pair.first))) {
+ if (std::filesystem::exists(LongPath(expected_object_pair.second))) {
// CopyFile fails if the file already exists
- std::filesystem::remove(expected_object_pair.second);
+ std::filesystem::remove(LongPath(expected_object_pair.second));
}
std::error_code ec;
copy_file(expected_object_pair.first, expected_object_pair.second, ec);