Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 38 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,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"
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