Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
2a70311
Support cross-compiling to WebAssembly and Android with Swift SDKs
AttilaTheFun Jun 13, 2026
9278761
Support shared-library and reactor outputs for Swift SDK cross-compil…
AttilaTheFun Jun 13, 2026
6766f8f
cross_compilation example: a browser web app for the wasm reactor
AttilaTheFun Jun 13, 2026
d26c89d
wasm: link at the global/table bases swiftc uses (fixes -O metadata c…
AttilaTheFun Jun 13, 2026
95c7274
ci: build the cross_compilation example (wasm + Android)
AttilaTheFun Jun 13, 2026
5c45529
docs: WINDOWS.md — native Windows host toolchain state and checklist
AttilaTheFun Jun 13, 2026
322e8ee
Build and test Swift natively on Windows
AttilaTheFun Jun 13, 2026
773259f
docs: remove WINDOWS.md handoff note (Windows host work landed and ve…
AttilaTheFun Jun 14, 2026
1bf9976
android: don't --exclude-libs,ALL (let linkshared export library symb…
AttilaTheFun Jun 15, 2026
a34a388
buildifier: silence external-path on the execroot path helper
AttilaTheFun Jun 15, 2026
8193d63
ci: run the Windows Swift install via shell_commands (not batch_comma…
AttilaTheFun Jun 15, 2026
127cbbb
ci: revert Windows key to batch_commands; add install-path diagnostics
AttilaTheFun Jun 15, 2026
7fb2f50
ci: expose the installed Swift toolchain to the Windows build
AttilaTheFun Jun 15, 2026
ba91db9
windows: default ProgramData when configuring the toolchain
AttilaTheFun Jun 15, 2026
df56380
ci: diagnostic — dump the MSVC vcvars env on the Windows runner
AttilaTheFun Jun 15, 2026
2c7143c
ci: set the MSVC INCLUDE/LIB env for the Windows Swift build
AttilaTheFun Jun 15, 2026
e6a27ec
ci: build Windows with Swift 6.3.2 (matches the cross-compile + verif…
AttilaTheFun Jun 15, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 49 additions & 9 deletions .bazelci/presubmit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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
Expand Down Expand Up @@ -97,11 +117,31 @@ 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
# Expose the installed Swift toolchain and the MSVC/Windows SDK environment to
# the build. The swift.org installer (`batch_commands` below) records its
# Toolchains/Runtimes bin on the user's Path and sets SDKROOT, and a Visual
# Studio "developer prompt" (vcvars) would set INCLUDE/LIB for swiftc's clang
# to find the C headers (errno.h, ...) and for the linker — but none of that
# reaches the already-running CI process. Set it all here. BazelCI expands
# these with os.path.expandvars, so %VAR% resolves and earlier keys (listed
# first) are available to later ones. The VS/SDK versions are pinned to the
# Bazel CI Windows image; update them if `vcvars64.bat`'s output changes.
environment:
SWIFT_VERSION: "6.3.2"
VCToolsInstallDir: "C:\\Program Files (x86)\\Microsoft Visual Studio\\2022\\BuildTools\\VC\\Tools\\MSVC\\14.39.33519"
WindowsSdkDir: "C:\\Program Files (x86)\\Windows Kits\\10"
WindowsSDKVersion: "10.0.26100.0"
Path: "%LOCALAPPDATA%\\Programs\\Swift\\Toolchains\\%SWIFT_VERSION%+Asserts\\usr\\bin;%LOCALAPPDATA%\\Programs\\Swift\\Runtimes\\%SWIFT_VERSION%\\usr\\bin;%LOCALAPPDATA%\\Programs\\Swift\\Tools\\%SWIFT_VERSION%;%VCToolsInstallDir%\\bin\\Hostx64\\x64;%WindowsSdkDir%\\bin\\%WindowsSDKVersion%\\x64;%PATH%"
SDKROOT: "%LOCALAPPDATA%\\Programs\\Swift\\Platforms\\%SWIFT_VERSION%\\Windows.platform\\Developer\\SDKs\\Windows.sdk"
INCLUDE: "%VCToolsInstallDir%\\include;%WindowsSdkDir%\\include\\%WindowsSDKVersion%\\ucrt;%WindowsSdkDir%\\include\\%WindowsSDKVersion%\\shared;%WindowsSdkDir%\\include\\%WindowsSDKVersion%\\um;%WindowsSdkDir%\\include\\%WindowsSDKVersion%\\winrt;%WindowsSdkDir%\\include\\%WindowsSDKVersion%\\cppwinrt"
LIB: "%VCToolsInstallDir%\\lib\\x64;%WindowsSdkDir%\\lib\\%WindowsSDKVersion%\\ucrt\\x64;%WindowsSdkDir%\\lib\\%WindowsSDKVersion%\\um\\x64"
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"
Expand Down
6 changes: 6 additions & 0 deletions .bazelrc
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
37 changes: 36 additions & 1 deletion MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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)
Expand Down
6 changes: 5 additions & 1 deletion doc/rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ On this page:

<pre>
swift_binary(<a href="#swift_binary-name">name</a>, <a href="#swift_binary-deps">deps</a>, <a href="#swift_binary-srcs">srcs</a>, <a href="#swift_binary-data">data</a>, <a href="#swift_binary-additional_linker_inputs">additional_linker_inputs</a>, <a href="#swift_binary-copts">copts</a>, <a href="#swift_binary-defines">defines</a>, <a href="#swift_binary-env">env</a>, <a href="#swift_binary-linkopts">linkopts</a>,
<a href="#swift_binary-malloc">malloc</a>, <a href="#swift_binary-module_name">module_name</a>, <a href="#swift_binary-package_name">package_name</a>, <a href="#swift_binary-plugins">plugins</a>, <a href="#swift_binary-stamp">stamp</a>, <a href="#swift_binary-swiftc_inputs">swiftc_inputs</a>)
<a href="#swift_binary-linkshared">linkshared</a>, <a href="#swift_binary-malloc">malloc</a>, <a href="#swift_binary-module_name">module_name</a>, <a href="#swift_binary-package_name">package_name</a>, <a href="#swift_binary-plugins">plugins</a>, <a href="#swift_binary-stamp">stamp</a>, <a href="#swift_binary-swiftc_inputs">swiftc_inputs</a>)
</pre>

Compiles and links Swift code into an executable binary.
Expand All @@ -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**


Expand All @@ -72,6 +75,7 @@ please use one of the platform-specific application rules in
| <a id="swift_binary-defines"></a>defines | A list of defines to add to the compilation command line.<br><br>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.<br><br>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 | `[]` |
| <a id="swift_binary-env"></a>env | Specifies additional environment variables to set when the test is executed by `bazel run` or `bazel test`.<br><br>The values of these environment variables are subject to `$(location)` and "Make variable" substitution.<br><br>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/`). | <a href="https://bazel.build/rules/lib/dict">Dictionary: String -> String</a> | optional | `{}` |
| <a id="swift_binary-linkopts"></a>linkopts | Additional linker options that should be passed to `clang`. These strings are subject to `$(location ...)` expansion. | List of strings | optional | `[]` |
| <a id="swift_binary-linkshared"></a>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.<br><br>On most platforms this produces a dynamic library named `lib<name>.so` (`.dylib` on Apple platforms) suitable for loading with `dlopen` / `System.loadLibrary` (e.g. an Android JNI library; export functions with `@_cdecl`).<br><br>When targeting WebAssembly it instead produces a "reactor" module (`<name>.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=<symbol>` (or `-Wl,--export=<symbol>`) flags in `linkopts`. | Boolean | optional | `False` |
| <a id="swift_binary-malloc"></a>malloc | Override the default dependency on `malloc`.<br><br>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. | <a href="https://bazel.build/concepts/labels">Label</a> | optional | `"@bazel_tools//tools/cpp:malloc"` |
| <a id="swift_binary-module_name"></a>module_name | The name of the Swift module being built.<br><br>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 | `""` |
| <a id="swift_binary-package_name"></a>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 | `""` |
Expand Down
137 changes: 137 additions & 0 deletions doc/standalone_toolchain.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<name>.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 `<name>.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=<symbol>"]`.

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
`@<toolchain_name>//:libcxx_shared_<arch>` (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
Expand Down
Loading
Loading