Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
a8edbcf
Initial plan
Copilot Jul 13, 2026
29dcff8
Add macOS (xcode) support to swift-syntax-rs build
Copilot Jul 13, 2026
13d2a12
swift-syntax-rs: make BUILD.bazel targets Windows-incompatible; expli…
Copilot Jul 13, 2026
0662060
Set macOS minimum deployment target for Swift builds in .bazelrc
Copilot Jul 13, 2026
4ba85eb
Also set MACOSX_DEPLOYMENT_TARGET in action_env for exec config
Copilot Jul 13, 2026
377ee49
Scope CC=clang to Linux; use --macos_minimum_os=10.14 on macOS
Copilot Jul 13, 2026
aebf307
Set macOS deployment target back to 10.15
Copilot Jul 13, 2026
4596c69
Scope macOS deployment-target fix to Swift compiles via --swiftcopt=-…
Copilot Jul 13, 2026
381a4a1
Register --swiftcopt flag_alias in .bazelrc for macOS Swift builds
Copilot Jul 13, 2026
e452647
Disable swift-autolink-extract on macOS to fix Swift build
Copilot Jul 13, 2026
967e298
Patch rules_swift to skip Linux-only linkopts on macOS
Copilot Jul 13, 2026
0122729
rules_swift patch: point macOS platform_lib_dir at usr/lib/swift/macosx
Copilot Jul 13, 2026
66071dd
Use xcode_swift_toolchain on macOS instead of patched Unix toolchain
Copilot Jul 13, 2026
7c84693
Wire up apple_support xcode_config so @system_sdk selects resolve on …
Copilot Jul 13, 2026
f4c41e4
bazel: give apple_support cc toolchains priority on macOS
Copilot Jul 13, 2026
dca60ea
bazel: use_repo(apple_cc_configure, "local_config_apple_cc_toolchains")
Copilot Jul 13, 2026
86bcb72
bazel: drop swiftcopt -target workaround (obsoleted by apple_cc toolc…
Copilot Jul 13, 2026
de5c1f7
bazel: drop --swiftcopt/--host_swiftcopt flag aliases (no longer used)
Copilot Jul 13, 2026
a29be0b
clean up wordy comments across macOS bring-up changes
Copilot Jul 13, 2026
32dcc0f
bazel: scope Xcode-coupled flags to Swift builds via --config=swift_m…
Copilot Jul 13, 2026
45a42b0
Revert swift.yml change: swift/ builds C++, not Swift code
Copilot Jul 13, 2026
26ec221
bazel: auto-enable swift_macos on macOS so Swift builds work zero-config
Copilot Jul 13, 2026
720f41a
bazel: scope Xcode flags to //unified/swift-syntax-rs via transition
Copilot Jul 14, 2026
a7dcd56
MODULE.bazel: update apple_support/local_config_* comments per review
Copilot Jul 14, 2026
5bee639
bazel: apply Xcode transition to swift_library only, drop Rust wrappers
Copilot Jul 14, 2026
f604997
bazel: load CcInfo from @rules_cc for Bazel 9 compatibility
Copilot Jul 14, 2026
240267e
swift-syntax-rs: declare macOS 10.15 platform for cargo build
Copilot Jul 14, 2026
284dcb3
Remove opt-in swift_macos config from .bazelrc
Copilot Jul 14, 2026
f8a9b0d
clean up added comments and revert cleanup edits to adapter.rs/swift.…
Copilot Jul 14, 2026
266eef5
Address review comments on swift-syntax-rs macOS toolchain docs
Copilot Jul 14, 2026
96cd3b5
Revert doc-comment reformatting in unified/swift-syntax-rs/build.rs
Copilot Jul 14, 2026
03aed00
Clarify Swift toolchain hermeticity comment in MODULE.bazel
Copilot Jul 14, 2026
b43b4ac
Simplify apple_support extension comments in MODULE.bazel
Copilot Jul 14, 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
34 changes: 18 additions & 16 deletions MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ bazel_dep(name = "googletest", version = "1.17.0.bcr.2")
bazel_dep(name = "rules_rust", version = "0.69.0")
bazel_dep(name = "rules_swift", version = "4.0.0-rc4")
bazel_dep(name = "swift-syntax", version = "602.0.0.bcr.2")

# Needed so we can `use_repo` `local_config_xcode` and
# `local_config_apple_cc_toolchains` below (referenced by the per-target
# Xcode-config transition in `unified/swift-syntax-rs/xcode_transition.bzl`).
bazel_dep(name = "apple_support", version = "2.6.1")
bazel_dep(name = "zstd", version = "1.5.7.bcr.1")

bazel_dep(name = "buildifier_prebuilt", version = "6.4.0", dev_dependency = True)
Expand Down Expand Up @@ -221,22 +226,11 @@ use_repo(
"swift-resource-dir-macos",
)

# Hermetic Swift toolchain (from swift.org) for building the `swift-syntax`
# based Rust wrapper in `unified/swift-syntax-rs`. This is the official
# `rules_swift` standalone-toolchain extension and is independent of the
# patched prebuilt toolchain wired up via `swift_deps` above.
#
# Bazel cannot auto-select between Linux distributions, so the toolchain is
# registered explicitly for the distribution our CI runs on (ubuntu24.04,
# x86_64). Add further platforms here if CI grows to cover them.
#
# We register the `exec` toolchain (normal host compilation, targeting
# linux/x86_64) rather than the `embedded` one, which targets `os:none`
# (Embedded Swift) and would not match a normal host build.
#
# The Swift version is read from `unified/swift-syntax-rs/.swift-version`, which
# is the single source of truth shared with the local `cargo` build (via
# swiftly / any Swift install) and `swift/Package.swift`.
# Swift toolchain for building `unified/swift-syntax-rs`. On Linux we register
# a hermetic swift.org toolchain as the exec toolchain; on macOS `rules_swift`
# auto-registers `xcode_swift_toolchain` (host Xcode + OS-provided Swift
# runtime), which is not hermetic. Version comes from
# `unified/swift-syntax-rs/.swift-version`.
swift = use_extension("@rules_swift//swift:extensions.bzl", "swift")
swift.toolchain(
name = "swift_toolchain",
Expand All @@ -252,6 +246,14 @@ register_toolchains(
"@swift_toolchain//:swift_toolchain_exec_ubuntu24.04",
)

# `apple_support`'s xcode_config and CC toolchains, needed by the Xcode
# transition in `unified/swift-syntax-rs/xcode_transition.bzl`.
xcode_configure = use_extension("@apple_support//xcode:xcode_configure.bzl", "xcode_configure_extension")
use_repo(xcode_configure, "local_config_xcode")

apple_cc_configure = use_extension("@apple_support//crosstool:setup.bzl", "apple_cc_configure_extension")
use_repo(apple_cc_configure, "local_config_apple_cc_toolchains")

node = use_extension("@rules_nodejs//nodejs:extensions.bzl", "node")
node.toolchain(
name = "nodejs",
Expand Down
53 changes: 33 additions & 20 deletions unified/swift-syntax-rs/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -1,38 +1,44 @@
load("@rules_rust//rust:defs.bzl", "rust_binary", "rust_library", "rust_test")
load("@rules_swift//swift:swift.bzl", "swift_library")
load(":xcode_transition.bzl", "xcode_transition_swift_library")

package(default_visibility = ["//visibility:public"])

# The pinned Swift version, shared with the local `cargo` build and
# `swift/Package.swift`. Referenced by the `swift.toolchain` extension in
# //:MODULE.bazel via `swift_version_file`.
# Pinned Swift version, shared with `cargo` and `swift/Package.swift`.
# Referenced by `swift.toolchain(swift_version_file = ...)` in //:MODULE.bazel.
exports_files([".swift-version"])

# The Swift FFI shim: wraps swift-syntax and exposes a small C ABI
# (`ssr_parse_json` / `ssr_string_free`). Built with the hermetic Swift
# toolchain registered in //:MODULE.bazel; provides a `CcInfo` that the Rust
# targets below link against.
swift_library(
# Targets in this package require a Swift toolchain (Linux or macOS).
# `select()` gives us OR-of-OSes; other platforms get marked incompatible
# so `bazel build/test //...` skips them cleanly.
_SWIFT_SUPPORTED_PLATFORMS = select({
"@platforms//os:linux": [],
"@platforms//os:macos": [],
"//conditions:default": ["@platforms//:incompatible"],
})

# Swift FFI shim: wraps swift-syntax and exposes a small C ABI. The Rust
# targets below link against its `CcInfo`.
xcode_transition_swift_library(
name = "swift_syntax_ffi",
srcs = ["swift/Sources/SwiftSyntaxFFI/SwiftSyntaxFFI.swift"],
module_name = "SwiftSyntaxFFI",
target_compatible_with = _SWIFT_SUPPORTED_PLATFORMS,
deps = [
"@swift-syntax//:SwiftParser",
"@swift-syntax//:SwiftSyntax",
],
)

# Safe Rust bindings on top of the C ABI. `build.rs` is intentionally *not*
# wired up here (no `cargo_build_script`): under Bazel the Swift side is
# provided by `:swift_syntax_ffi`, whereas `build.rs` only exists to build the
# Swift shim in the local `cargo` workflow.
# Safe Rust bindings on top of the C ABI. Under Bazel the Swift side comes
# from `:swift_syntax_ffi`; `build.rs` is only used by the `cargo` workflow.
rust_library(
name = "swift_syntax_rs",
srcs = glob(
["src/**/*.rs"],
exclude = ["src/main.rs"],
),
edition = "2024",
target_compatible_with = _SWIFT_SUPPORTED_PLATFORMS,
deps = [
":swift_syntax_ffi",
],
Expand All @@ -41,20 +47,27 @@ rust_library(
rust_binary(
name = "swift-syntax-parse",
srcs = ["src/main.rs"],
# The Swift toolchain propagates a runfiles-relative RPATH to its runtime
# `.so`s via `swift_syntax_ffi`'s `CcInfo`, but (unlike `swift_binary`)
# `rust_binary` does not copy those libraries into runfiles. Add the
# toolchain files as `data` so the runtime is found at execution time.
# (rules_swift does not yet support statically linking the runtime.)
data = ["@swift_toolchain_ubuntu24.04//:files"],
# `rust_binary` doesn't copy the Swift runtime into runfiles the way
# `swift_binary` does. On Linux, ship the standalone toolchain's runtime;
# on macOS the OS provides it at `/usr/lib/swift` (rpath'd by
# `xcode_swift_toolchain`).
data = select({
"@platforms//os:macos": [],
"@platforms//os:linux": ["@swift_toolchain_ubuntu24.04//:files"],
}),
edition = "2024",
target_compatible_with = _SWIFT_SUPPORTED_PLATFORMS,
deps = [":swift_syntax_rs"],
)

rust_test(
name = "swift_syntax_rs_test",
size = "small",
crate = ":swift_syntax_rs",
data = ["@swift_toolchain_ubuntu24.04//:files"],
data = select({
"@platforms//os:macos": [],
"@platforms//os:linux": ["@swift_toolchain_ubuntu24.04//:files"],
}),
edition = "2024",
target_compatible_with = _SWIFT_SUPPORTED_PLATFORMS,
)
14 changes: 10 additions & 4 deletions unified/swift-syntax-rs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,10 +144,16 @@ Requirements:
- **`clang`** must be installed on the runner. `rules_swift` requires the Bazel
CC toolchain to use clang; the repo's `.bazelrc` already sets
`--repo_env=CC=clang`, so no extra flags are needed.
- The registered Swift toolchain currently targets **ubuntu24.04 / x86_64**
only (Bazel cannot auto-select between Linux distributions). Add more
platforms in `MODULE.bazel` (`swift.toolchain` + `register_toolchains`) if CI
grows to cover them.
- The registered Swift toolchains cover **ubuntu24.04 / x86_64** and
**macOS / `xcode`** (Apple Silicon and Intel). Bazel selects the toolchain
matching the host. Targets are marked `target_compatible_with` these two
OSes, so on Windows Bazel skips them cleanly.
- **macOS only:** the Swift toolchain comes from the host Xcode installation
(`rules_swift` auto-registers `xcode_swift_toolchain`), which also needs
Xcode's CC toolchain and xcode_config; these are applied to the Swift
target via an incoming-edge Starlark transition (see
[`xcode_transition.bzl`](xcode_transition.bzl)), so other targets on macOS
keep using Bazel's default CC toolchain.

The Swift compiler version is read from [`.swift-version`](.swift-version) by
both the Bazel toolchain (`swift.toolchain(swift_version_file = …)`) and the
Expand Down
2 changes: 1 addition & 1 deletion unified/swift-syntax-rs/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ fn swift_runtime_dir() -> Option<PathBuf> {
let close = value_start.find('"')?;
let resource_path = &value_start[..close];

Some(PathBuf::from(resource_path).join("linux"))
Some(PathBuf::from(resource_path).join(if cfg!(target_os = "macos") { "macosx" } else { "linux" }))
}

/// The `swift` driver to invoke: `$SWIFT` if set, otherwise `swift` from `PATH`.
Expand Down
5 changes: 5 additions & 0 deletions unified/swift-syntax-rs/swift/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ import PackageDescription

let package = Package(
name: "SwiftSyntaxFFI",
platforms: [
// swift-syntax 602 requires macOS 10.15; declare it explicitly
// rather than relying on the swift-tools-version default (10.13).
.macOS(.v10_15),
],
products: [
// Dynamic library so the produced .so records its dependency on the
// Swift runtime libraries, keeping the Rust link step simple.
Expand Down
94 changes: 94 additions & 0 deletions unified/swift-syntax-rs/xcode_transition.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
"""Per-target Xcode configuration for `//unified/swift-syntax-rs` on macOS.

`rules_swift`'s auto-registered `xcode_swift_toolchain` reads
`cc_toolchain.target_gnu_system_name`, which is literally "local" on
Bazel's built-in `local_config_cc`. To make it usable we need:

- `--xcode_version_config=@local_config_xcode//:host_xcodes` — selects
`apple_support`'s xcode_config (matches `rules_swift`'s `system_sdk` keys).
- `--extra_toolchains=@local_config_apple_cc_toolchains//:all` — forces
`apple_support`'s CC toolchain ahead of `local_config_cc`.

Applied via an incoming-edge Starlark transition on the `swift_library`
target only (via the `xcode_transition_swift_library` macro), so downstream
Rust targets and the rest of the repo stay on the default CC toolchain and
the `@local_config_*` repos are not materialized otherwise. No-op off macOS.
"""

load("@rules_cc//cc/common:cc_info.bzl", "CcInfo")
load("@rules_swift//swift:swift.bzl", "swift_library")
load("//misc/bazel:os.bzl", "os_select")

_XCODE_VERSION_CONFIG = "//command_line_option:xcode_version_config"
_EXTRA_TOOLCHAINS = "//command_line_option:extra_toolchains"

def _transition_impl(settings, attr):
if attr.os != "macos":
# Preserve inputs so the configuration is identical to the incoming one.
return {
_XCODE_VERSION_CONFIG: settings[_XCODE_VERSION_CONFIG],
_EXTRA_TOOLCHAINS: settings[_EXTRA_TOOLCHAINS],
}
return {
_XCODE_VERSION_CONFIG: "@local_config_xcode//:host_xcodes",
_EXTRA_TOOLCHAINS: (
list(settings[_EXTRA_TOOLCHAINS]) +
["@local_config_apple_cc_toolchains//:all"]
),
}

_xcode_transition = transition(
implementation = _transition_impl,
inputs = [_XCODE_VERSION_CONFIG, _EXTRA_TOOLCHAINS],
outputs = [_XCODE_VERSION_CONFIG, _EXTRA_TOOLCHAINS],
)

def _wrapper_impl(ctx):
src = ctx.attr.actual[0]
# Forward the providers a downstream `rust_*` target reads from `deps`:
# `DefaultInfo`, `CcInfo` (linking info), and `OutputGroupInfo`.
providers = [src[DefaultInfo]]
for p in (CcInfo, OutputGroupInfo):
if p in src:
providers.append(src[p])
return providers

_xcode_transition_swift_library_rule = rule(
implementation = _wrapper_impl,
attrs = {
"actual": attr.label(
mandatory = True,
cfg = _xcode_transition,
providers = [CcInfo],
),
"os": attr.string(mandatory = True),
"_allowlist_function_transition": attr.label(
default = "@bazel_tools//tools/allowlists/function_transition_allowlist",
),
},
)

def xcode_transition_swift_library(name, visibility = None, tags = None, target_compatible_with = None, **kwargs):
"""`swift_library` wrapped in the macOS Xcode-config transition.

Emits a private inner `_impl_<name>` `swift_library` (tagged `manual`)
and a public `name` that applies the transition on macOS and forwards
the inner target's providers. Downstream `rust_*` targets depend on
`name` as usual; only the `swift_library` sub-graph flips toolchain.
"""
inner_name = "_impl_%s" % name
swift_library(
name = inner_name,
visibility = ["//visibility:private"],
tags = (tags or []) + ["manual"],
target_compatible_with = target_compatible_with,
**kwargs
)
_xcode_transition_swift_library_rule(
name = name,
visibility = visibility,
tags = tags,
target_compatible_with = target_compatible_with,
actual = ":" + inner_name,
os = os_select(linux = "linux", macos = "macos", default = "other"),
)