From a8edbcf1dc275841ac800b80035736f45a242458 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 13 Jul 2026 14:12:30 +0000 Subject: [PATCH 01/33] Initial plan From 29dcff8ff38fb0ce33c809abb39a85cef17f4b23 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 13 Jul 2026 14:18:38 +0000 Subject: [PATCH 02/33] Add macOS (xcode) support to swift-syntax-rs build --- MODULE.bazel | 17 ++++++++++++----- unified/swift-syntax-rs/BUILD.bazel | 20 ++++++++++++++------ unified/swift-syntax-rs/README.md | 10 ++++++---- unified/swift-syntax-rs/build.rs | 2 +- 4 files changed, 33 insertions(+), 16 deletions(-) diff --git a/MODULE.bazel b/MODULE.bazel index aa1f6555e0ca..babe3803e2c1 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -227,12 +227,17 @@ use_repo( # 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. +# registered explicitly for the distributions/platforms our CI runs on: +# - ubuntu24.04 / x86_64 (Linux CI) +# - xcode (macOS CI — covers both Apple Silicon and Intel) # -# 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. +# NOTE: The macOS toolchain is distributed as a `.pkg` archive that can only +# be extracted on a macOS host. Do not build the `xcode` toolchain on Linux. +# +# We register the `exec` toolchains (normal host compilation) rather than the +# `embedded` ones, which target `os:none` (Embedded Swift) and would not +# match a normal host build. Bazel selects the toolchain matching the host +# platform automatically. # # 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 @@ -246,10 +251,12 @@ use_repo( swift, "swift_toolchain", "swift_toolchain_ubuntu24.04", + "swift_toolchain_xcode", ) register_toolchains( "@swift_toolchain//:swift_toolchain_exec_ubuntu24.04", + "@swift_toolchain//:swift_toolchain_exec_xcode", ) node = use_extension("@rules_nodejs//nodejs:extensions.bzl", "node") diff --git a/unified/swift-syntax-rs/BUILD.bazel b/unified/swift-syntax-rs/BUILD.bazel index 9db40d4db30c..c187cc19faf1 100644 --- a/unified/swift-syntax-rs/BUILD.bazel +++ b/unified/swift-syntax-rs/BUILD.bazel @@ -42,11 +42,16 @@ 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"], + # `.so`s/`.dylib`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.) + # Select the correct per-platform toolchain repo: `xcode` on macOS, + # `ubuntu24.04` on Linux. + data = select({ + "@platforms//os:macos": ["@swift_toolchain_xcode//:files"], + "//conditions:default": ["@swift_toolchain_ubuntu24.04//:files"], + }), edition = "2024", deps = [":swift_syntax_rs"], ) @@ -55,6 +60,9 @@ rust_test( name = "swift_syntax_rs_test", size = "small", crate = ":swift_syntax_rs", - data = ["@swift_toolchain_ubuntu24.04//:files"], + data = select({ + "@platforms//os:macos": ["@swift_toolchain_xcode//:files"], + "//conditions:default": ["@swift_toolchain_ubuntu24.04//:files"], + }), edition = "2024", ) diff --git a/unified/swift-syntax-rs/README.md b/unified/swift-syntax-rs/README.md index a2d66495a541..85b8cf8186eb 100644 --- a/unified/swift-syntax-rs/README.md +++ b/unified/swift-syntax-rs/README.md @@ -144,10 +144,12 @@ 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`** (both Apple Silicon and Intel). Bazel automatically + selects the toolchain matching the host platform. +- **macOS only:** the macOS toolchain is distributed as a `.pkg` archive that + can only be fetched and extracted on a macOS host. Building the `xcode` + toolchain on Linux is not supported. The Swift compiler version is read from [`.swift-version`](.swift-version) by both the Bazel toolchain (`swift.toolchain(swift_version_file = …)`) and the diff --git a/unified/swift-syntax-rs/build.rs b/unified/swift-syntax-rs/build.rs index 0adb140140dc..3cd2e2858742 100644 --- a/unified/swift-syntax-rs/build.rs +++ b/unified/swift-syntax-rs/build.rs @@ -73,7 +73,7 @@ fn swift_runtime_dir() -> Option { 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`. From 13d2a1263f5049b4181038fb117e490e839a2746 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 13 Jul 2026 14:26:47 +0000 Subject: [PATCH 03/33] swift-syntax-rs: make BUILD.bazel targets Windows-incompatible; explicit linux/macos selects --- unified/swift-syntax-rs/BUILD.bazel | 21 +++++++++++++++++++-- unified/swift-syntax-rs/README.md | 5 ++++- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/unified/swift-syntax-rs/BUILD.bazel b/unified/swift-syntax-rs/BUILD.bazel index c187cc19faf1..5fac86e4caf7 100644 --- a/unified/swift-syntax-rs/BUILD.bazel +++ b/unified/swift-syntax-rs/BUILD.bazel @@ -8,6 +8,19 @@ package(default_visibility = ["//visibility:public"]) # //:MODULE.bazel via `swift_version_file`. exports_files([".swift-version"]) +# Constraint expressing "this target only builds on platforms where we have +# a Swift toolchain registered in //:MODULE.bazel" (i.e. Linux or macOS — +# not Windows). `target_compatible_with` uses AND semantics on a plain list, +# so we express the OR via a `select()` that maps supported OSes to the +# empty (satisfiable) constraint list, and everything else to the sentinel +# `@platforms//:incompatible`, which causes Bazel to skip the target on +# `bazel build/test //...` rather than fail. +_SWIFT_SUPPORTED_PLATFORMS = select({ + "@platforms//os:linux": [], + "@platforms//os:macos": [], + "//conditions:default": ["@platforms//:incompatible"], +}) + # 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 @@ -16,6 +29,7 @@ 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", @@ -33,6 +47,7 @@ rust_library( exclude = ["src/main.rs"], ), edition = "2024", + target_compatible_with = _SWIFT_SUPPORTED_PLATFORMS, deps = [ ":swift_syntax_ffi", ], @@ -50,9 +65,10 @@ rust_binary( # `ubuntu24.04` on Linux. data = select({ "@platforms//os:macos": ["@swift_toolchain_xcode//:files"], - "//conditions:default": ["@swift_toolchain_ubuntu24.04//:files"], + "@platforms//os:linux": ["@swift_toolchain_ubuntu24.04//:files"], }), edition = "2024", + target_compatible_with = _SWIFT_SUPPORTED_PLATFORMS, deps = [":swift_syntax_rs"], ) @@ -62,7 +78,8 @@ rust_test( crate = ":swift_syntax_rs", data = select({ "@platforms//os:macos": ["@swift_toolchain_xcode//:files"], - "//conditions:default": ["@swift_toolchain_ubuntu24.04//:files"], + "@platforms//os:linux": ["@swift_toolchain_ubuntu24.04//:files"], }), edition = "2024", + target_compatible_with = _SWIFT_SUPPORTED_PLATFORMS, ) diff --git a/unified/swift-syntax-rs/README.md b/unified/swift-syntax-rs/README.md index 85b8cf8186eb..48ef4ca3e42d 100644 --- a/unified/swift-syntax-rs/README.md +++ b/unified/swift-syntax-rs/README.md @@ -146,7 +146,10 @@ Requirements: `--repo_env=CC=clang`, so no extra flags are needed. - The registered Swift toolchains cover **ubuntu24.04 / x86_64** and **macOS / `xcode`** (both Apple Silicon and Intel). Bazel automatically - selects the toolchain matching the host platform. + selects the toolchain matching the host platform. The Bazel targets are + marked `target_compatible_with` these two OSes only, so on Windows (or any + other unsupported host) Bazel skips them cleanly under `bazel build/test + //...` rather than trying to build a Swift-less target. - **macOS only:** the macOS toolchain is distributed as a `.pkg` archive that can only be fetched and extracted on a macOS host. Building the `xcode` toolchain on Linux is not supported. From 0662060d694cdad1e77f54a2db3e0a34e2f453c3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 13 Jul 2026 14:38:31 +0000 Subject: [PATCH 04/33] Set macOS minimum deployment target for Swift builds in .bazelrc --- .bazelrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.bazelrc b/.bazelrc index 8687f4406cb9..7fcff085debe 100644 --- a/.bazelrc +++ b/.bazelrc @@ -11,6 +11,15 @@ build --compilation_mode opt common --override_module=semmle_code=%workspace%/misc/bazel/semmle_code_stub build --repo_env=CC=clang --repo_env=CXX=clang++ + +# Set a macOS deployment target for both target and exec configurations so +# that the hermetic Swift toolchain used by //unified/swift-syntax-rs (and +# its `swift-syntax` dependency) compiles. Without this, `swiftc` is +# invoked without a deployment target and fails with +# "Swift requires a minimum deployment target of macOS 10.9.0" on recent +# macOS versions. 10.15 matches swift-syntax's declared minimum platform. +build:macos --macos_minimum_os=10.15 +build:macos --host_macos_minimum_os=10.15 # Disable Android SDK auto-detection (we don't use it, and rules_android has Bazel 9 compatibility issues) build --repo_env=ANDROID_HOME= From 4ba85eb65b14005f6958644cecc84c58490862c1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 13 Jul 2026 14:42:59 +0000 Subject: [PATCH 05/33] Also set MACOSX_DEPLOYMENT_TARGET in action_env for exec config --- .bazelrc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.bazelrc b/.bazelrc index 7fcff085debe..929aa60326c4 100644 --- a/.bazelrc +++ b/.bazelrc @@ -18,8 +18,14 @@ build --repo_env=CC=clang --repo_env=CXX=clang++ # invoked without a deployment target and fails with # "Swift requires a minimum deployment target of macOS 10.9.0" on recent # macOS versions. 10.15 matches swift-syntax's declared minimum platform. +# `--macos_minimum_os` covers the target config; the exec/host config +# (used for e.g. Swift macro compilation) needs `MACOSX_DEPLOYMENT_TARGET` +# on the action env, since `--host_macos_minimum_os` doesn't reach the +# standalone Swift toolchain in rules_swift 4.x. build:macos --macos_minimum_os=10.15 build:macos --host_macos_minimum_os=10.15 +build:macos --action_env=MACOSX_DEPLOYMENT_TARGET=10.15 +build:macos --host_action_env=MACOSX_DEPLOYMENT_TARGET=10.15 # Disable Android SDK auto-detection (we don't use it, and rules_android has Bazel 9 compatibility issues) build --repo_env=ANDROID_HOME= From 377ee496a2fb7dbadf1b41abbe7ae1caefa399e4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 13 Jul 2026 14:49:50 +0000 Subject: [PATCH 06/33] Scope CC=clang to Linux; use --macos_minimum_os=10.14 on macOS --- .bazelrc | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/.bazelrc b/.bazelrc index 929aa60326c4..6007abd3cd46 100644 --- a/.bazelrc +++ b/.bazelrc @@ -10,22 +10,19 @@ build --compilation_mode opt # that we can build things that do not rely on that common --override_module=semmle_code=%workspace%/misc/bazel/semmle_code_stub -build --repo_env=CC=clang --repo_env=CXX=clang++ - -# Set a macOS deployment target for both target and exec configurations so -# that the hermetic Swift toolchain used by //unified/swift-syntax-rs (and -# its `swift-syntax` dependency) compiles. Without this, `swiftc` is -# invoked without a deployment target and fails with -# "Swift requires a minimum deployment target of macOS 10.9.0" on recent -# macOS versions. 10.15 matches swift-syntax's declared minimum platform. -# `--macos_minimum_os` covers the target config; the exec/host config -# (used for e.g. Swift macro compilation) needs `MACOSX_DEPLOYMENT_TARGET` -# on the action env, since `--host_macos_minimum_os` doesn't reach the -# standalone Swift toolchain in rules_swift 4.x. -build:macos --macos_minimum_os=10.15 -build:macos --host_macos_minimum_os=10.15 -build:macos --action_env=MACOSX_DEPLOYMENT_TARGET=10.15 -build:macos --host_action_env=MACOSX_DEPLOYMENT_TARGET=10.15 +build:linux --repo_env=CC=clang --repo_env=CXX=clang++ + +# On macOS, do NOT force `CC=clang`: that steers Bazel to its generic +# `local_config_cc` toolchain, which reports an unversioned target triple +# (`arm64-apple-macosx`). The rules_swift 4.x standalone Swift toolchain +# feeds that triple straight to `swiftc -target`, and swiftc then fails with +# "Swift requires a minimum deployment target of macOS 10.9.0". Letting +# Bazel auto-detect the Xcode CC toolchain on macOS gives it a properly +# versioned triple (derived from `--macos_minimum_os` below). Xcode's +# `clang` is used either way, so `rules_swift`'s "requires clang" check +# still passes. +build:macos --macos_minimum_os=10.14 +build:macos --host_macos_minimum_os=10.14 # Disable Android SDK auto-detection (we don't use it, and rules_android has Bazel 9 compatibility issues) build --repo_env=ANDROID_HOME= From aebf3070f6966c9d2508058c04a4c75a1f0541a8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 13 Jul 2026 14:53:33 +0000 Subject: [PATCH 07/33] Set macOS deployment target back to 10.15 --- .bazelrc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.bazelrc b/.bazelrc index 6007abd3cd46..1d1d3c7acc37 100644 --- a/.bazelrc +++ b/.bazelrc @@ -21,8 +21,8 @@ build:linux --repo_env=CC=clang --repo_env=CXX=clang++ # versioned triple (derived from `--macos_minimum_os` below). Xcode's # `clang` is used either way, so `rules_swift`'s "requires clang" check # still passes. -build:macos --macos_minimum_os=10.14 -build:macos --host_macos_minimum_os=10.14 +build:macos --macos_minimum_os=10.15 +build:macos --host_macos_minimum_os=10.15 # Disable Android SDK auto-detection (we don't use it, and rules_android has Bazel 9 compatibility issues) build --repo_env=ANDROID_HOME= From 4596c693147dba413725cc7066d2da42e8dae0d5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 13 Jul 2026 14:59:29 +0000 Subject: [PATCH 08/33] Scope macOS deployment-target fix to Swift compiles via --swiftcopt=-target --- .bazelrc | 37 ++++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/.bazelrc b/.bazelrc index 1d1d3c7acc37..3225239adc2e 100644 --- a/.bazelrc +++ b/.bazelrc @@ -10,19 +10,30 @@ build --compilation_mode opt # that we can build things that do not rely on that common --override_module=semmle_code=%workspace%/misc/bazel/semmle_code_stub -build:linux --repo_env=CC=clang --repo_env=CXX=clang++ - -# On macOS, do NOT force `CC=clang`: that steers Bazel to its generic -# `local_config_cc` toolchain, which reports an unversioned target triple -# (`arm64-apple-macosx`). The rules_swift 4.x standalone Swift toolchain -# feeds that triple straight to `swiftc -target`, and swiftc then fails with -# "Swift requires a minimum deployment target of macOS 10.9.0". Letting -# Bazel auto-detect the Xcode CC toolchain on macOS gives it a properly -# versioned triple (derived from `--macos_minimum_os` below). Xcode's -# `clang` is used either way, so `rules_swift`'s "requires clang" check -# still passes. -build:macos --macos_minimum_os=10.15 -build:macos --host_macos_minimum_os=10.15 +build --repo_env=CC=clang --repo_env=CXX=clang++ + +# Force a versioned macOS target triple on Swift compilations only. +# +# Without `apple_support` in this module (see MODULE.bazel), Bazel uses its +# generic `local_config_cc` toolchain on macOS, whose `target_gnu_system_name` +# is unversioned (e.g. `arm64-apple-macosx`). Both rules_swift toolchains +# (standalone and xcode) build swiftc's `-target` argument from +# `ctx.var["CC_TARGET_TRIPLE"] or cc_toolchain.target_gnu_system_name`, so +# swiftc receives `-target arm64-apple-macosx` and fails with +# "Swift requires a minimum deployment target of macOS 10.9.0". +# +# `--macos_minimum_os` alone does not fix this (it doesn't feed either +# source). We append a versioned `-target` via `--swiftcopt` / +# `--host_swiftcopt`; swiftc treats the last `-target` as authoritative. +# These flags only affect Swift compile actions, so the scope is limited +# to //unified/swift-syntax-rs (the sole Swift consumer in this repo). +# +# 10.15 matches swift-syntax's declared minimum deployment target. +# NOTE: this pins the CPU to arm64 (Apple Silicon), which is fine because +# macOS 26 is Apple-Silicon-only. If Intel macOS Bazel builds are ever +# needed, override in local.bazelrc. +build:macos --swiftcopt=-target --swiftcopt=arm64-apple-macosx10.15 +build:macos --host_swiftcopt=-target --host_swiftcopt=arm64-apple-macosx10.15 # Disable Android SDK auto-detection (we don't use it, and rules_android has Bazel 9 compatibility issues) build --repo_env=ANDROID_HOME= From 381a4a1d43578066dc6ec00e059410b09d8c7fd4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 13 Jul 2026 15:05:53 +0000 Subject: [PATCH 09/33] Register --swiftcopt flag_alias in .bazelrc for macOS Swift builds --- .bazelrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.bazelrc b/.bazelrc index 3225239adc2e..22ba36c73ecd 100644 --- a/.bazelrc +++ b/.bazelrc @@ -12,6 +12,15 @@ common --override_module=semmle_code=%workspace%/misc/bazel/semmle_code_stub build --repo_env=CC=clang --repo_env=CXX=clang++ +# Register the `--swiftcopt` / `--host_swiftcopt` command-line shortcuts. +# +# rules_swift defines these as `--flag_alias` entries in its own `.bazelrc`, +# which Bazel does *not* load for consumer modules. Without registering them +# here, any use of `--swiftcopt=...` (including via `build:macos` below) fails +# with "Unrecognized option: --swiftcopt". +common --flag_alias=swiftcopt=@rules_swift//swift:copt +common --flag_alias=host_swiftcopt=@rules_swift//swift:exec_copt + # Force a versioned macOS target triple on Swift compilations only. # # Without `apple_support` in this module (see MODULE.bazel), Bazel uses its From e452647295105295f6c725c3b148fe9d28511f73 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 13 Jul 2026 15:15:36 +0000 Subject: [PATCH 10/33] Disable swift-autolink-extract on macOS to fix Swift build --- .bazelrc | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/.bazelrc b/.bazelrc index 22ba36c73ecd..1c77edf1d04d 100644 --- a/.bazelrc +++ b/.bazelrc @@ -43,6 +43,25 @@ common --flag_alias=host_swiftcopt=@rules_swift//swift:exec_copt # needed, override in local.bazelrc. build:macos --swiftcopt=-target --swiftcopt=arm64-apple-macosx10.15 build:macos --host_swiftcopt=-target --host_swiftcopt=arm64-apple-macosx10.15 + +# Disable `swift-autolink-extract` on macOS. +# +# rules_swift 4.0.0-rc4's standalone toolchain template +# (`swift/internal/extensions/BUILD.bazel.tpl`) unconditionally hardcodes +# `swift.use_autolink_extract` into the exec `swift_toolchain(...)` features +# list — even for macOS. That schedules a `SwiftAutolinkExtract` action for +# every `swift_library` (e.g. `@swift-syntax//:SwiftSyntax601`). But +# `swift-autolink-extract` is a Linux/ELF-only workflow: on macOS the Apple +# linker handles autolinking natively via `LC_LINKER_OPTION`, and running the +# tool from a swift.org standalone toolchain against Mach-O `.o` files fails +# with an opaque "worker failed" error. +# +# Negating the feature on the command line flips +# `SWIFT_FEATURE_USE_AUTOLINK_EXTRACT in ctx.features` to `False` in +# `swift_toolchain.bzl`, so the autolink-extract tool config (and therefore +# the action) is never registered on macOS. The Linux exec toolchain is +# unaffected. +build:macos --features=-swift.use_autolink_extract # Disable Android SDK auto-detection (we don't use it, and rules_android has Bazel 9 compatibility issues) build --repo_env=ANDROID_HOME= From 967e298f55c7ed7e9e77652ce3b21463c2fa9091 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 13 Jul 2026 15:26:57 +0000 Subject: [PATCH 11/33] Patch rules_swift to skip Linux-only linkopts on macOS --- MODULE.bazel | 13 +++ misc/bazel/patches/rules_swift/BUILD.bazel | 4 + .../patches/rules_swift/macos-linkopts.patch | 88 +++++++++++++++++++ 3 files changed, 105 insertions(+) create mode 100644 misc/bazel/patches/rules_swift/BUILD.bazel create mode 100644 misc/bazel/patches/rules_swift/macos-linkopts.patch diff --git a/MODULE.bazel b/MODULE.bazel index babe3803e2c1..c604ebb889ba 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -33,6 +33,19 @@ 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") + +# rules_swift 4.0.0-rc4's standalone toolchain extension unconditionally +# emits Linux/ELF-only linker flags (swiftrt.o, -lstdc++, -lrt, -ldl, +# -static-libgcc) for its macOS toolchain, which breaks any binary linked +# via `swift_library` on macOS. Apply a local patch that skips those flags +# when the toolchain OS is macos. See the patch header and +# `misc/bazel/patches/rules_swift/macos-linkopts.patch` for details. +single_version_override( + module_name = "rules_swift", + patch_strip = 1, + patches = ["//misc/bazel/patches/rules_swift:macos-linkopts.patch"], + version = "4.0.0-rc4", +) bazel_dep(name = "zstd", version = "1.5.7.bcr.1") bazel_dep(name = "buildifier_prebuilt", version = "6.4.0", dev_dependency = True) diff --git a/misc/bazel/patches/rules_swift/BUILD.bazel b/misc/bazel/patches/rules_swift/BUILD.bazel new file mode 100644 index 000000000000..fb6c357f995c --- /dev/null +++ b/misc/bazel/patches/rules_swift/BUILD.bazel @@ -0,0 +1,4 @@ +exports_files( + glob(["*.patch"]), + visibility = ["//visibility:public"], +) diff --git a/misc/bazel/patches/rules_swift/macos-linkopts.patch b/misc/bazel/patches/rules_swift/macos-linkopts.patch new file mode 100644 index 000000000000..d043cba1d700 --- /dev/null +++ b/misc/bazel/patches/rules_swift/macos-linkopts.patch @@ -0,0 +1,88 @@ +Skip Linux-only linkopts when generating macOS Swift toolchain linker inputs. + +rules_swift 4.0.0-rc4's standalone toolchain extension registers a +`swift_toolchain(...)` for macOS whose implicit linker inputs are produced by +`_swift_unix_linkopts_cc_info`. That function unconditionally emits +Linux/ELF-only flags -- `swiftrt.o`, `-lstdc++`, `-lrt`, `-ldl`, +`-static-libgcc` -- plus a Linux-shaped runtime path +(`{arch}/swiftrt.o` with `arch = "aarch64"`, but the macOS toolchain lays +out `usr/lib/swift/macos/` without `swiftrt.o` at all and uses `arm64` for +Apple Silicon). + +On macOS the Apple linker handles Swift runtime initialization automatically +via `LC_LINKER_OPTION` autolinking and `__DATA,__mod_init_func`, so +`swiftrt.o` is neither present nor needed. The other flags are also +Linux-specific. The only linkopts that carry over are the toolchain library +search path and rpath so downstream binaries can locate +`libswiftCore.dylib` etc. + +Without this patch, linking any `swift_library`-dependent Rust binary (e.g. +`//unified/swift-syntax-rs:swift-syntax-parse`) on macOS fails with: + + clang: error: no such file or directory: + 'external/rules_swift+.../usr/lib/swift/macos/aarch64/swiftrt.o' + +This should be fixed upstream: rules_swift's standalone toolchain extension +needs a macOS-aware `_swift_unix_linkopts_cc_info`. Until then, we patch the +module locally so macOS consumers can build. + +--- a/swift/toolchains/swift_toolchain.bzl ++++ b/swift/toolchains/swift_toolchain.bzl +@@ -411,25 +411,38 @@ + toolchain_root = toolchain_root, + ) + +- runtime_object_path = "{platform_lib_dir}/{cpu}/swiftrt.o".format( +- cpu = cpu, +- platform_lib_dir = platform_lib_dir, +- ) +- +- linkopts = [ +- "-pie", +- "-L{}".format(platform_lib_dir), +- "-Wl,-rpath,{}".format(platform_lib_dir), +- "-lm", +- "-lstdc++", +- "-lrt", +- "-ldl", +- runtime_object_path, +- "-static-libgcc", +- ] + [ +- "-Wl,-rpath,{}".format(rpath) +- for rpath in additional_rpaths +- ] ++ if os == "macos": ++ # On macOS, Apple's linker handles Swift runtime initialization ++ # automatically via LC_LINKER_OPTION autolinking and ++ # __DATA,__mod_init_func; swiftrt.o is neither present in the ++ # toolchain layout nor needed. The Linux-specific system libraries ++ # (-lrt, -ldl, -lstdc++, -static-libgcc) also don't exist here. ++ linkopts = [ ++ "-L{}".format(platform_lib_dir), ++ "-Wl,-rpath,{}".format(platform_lib_dir), ++ ] + [ ++ "-Wl,-rpath,{}".format(rpath) ++ for rpath in additional_rpaths ++ ] ++ else: ++ runtime_object_path = "{platform_lib_dir}/{cpu}/swiftrt.o".format( ++ cpu = cpu, ++ platform_lib_dir = platform_lib_dir, ++ ) ++ linkopts = [ ++ "-pie", ++ "-L{}".format(platform_lib_dir), ++ "-Wl,-rpath,{}".format(platform_lib_dir), ++ "-lm", ++ "-lstdc++", ++ "-lrt", ++ "-ldl", ++ runtime_object_path, ++ "-static-libgcc", ++ ] + [ ++ "-Wl,-rpath,{}".format(rpath) ++ for rpath in additional_rpaths ++ ] + + return CcInfo( + linking_context = cc_common.create_linking_context( From 0122729a098d5ed4ab7a1602f79dc36193801161 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 13 Jul 2026 16:09:03 +0000 Subject: [PATCH 12/33] rules_swift patch: point macOS platform_lib_dir at usr/lib/swift/macosx Swift.org macOS toolchains store their runtime libraries under usr/lib/swift/macosx/ (Apple's SDK name), not usr/lib/swift/macos/ (the Bazel @platforms//os:macos name). rules_swift 4.0.0-rc4's _swift_unix_linkopts_cc_info uses the latter, causing -L to point at a non-existent directory and ld to fail with undefined swiftCompatibility{56,Concurrency,Packs} symbols on macOS. Rewrite platform_lib_dir to `usr/lib/swift/macosx` on the macOS branch of the existing patch so the -L / -Wl,-rpath resolve to a real dir that already gets materialized into the sandbox via swift_tools' `additional_inputs = glob(["usr/lib/swift/**", ...])`. --- .../patches/rules_swift/macos-linkopts.patch | 84 ++++++++++++++----- 1 file changed, 62 insertions(+), 22 deletions(-) diff --git a/misc/bazel/patches/rules_swift/macos-linkopts.patch b/misc/bazel/patches/rules_swift/macos-linkopts.patch index d043cba1d700..c1bad304b274 100644 --- a/misc/bazel/patches/rules_swift/macos-linkopts.patch +++ b/misc/bazel/patches/rules_swift/macos-linkopts.patch @@ -1,34 +1,64 @@ -Skip Linux-only linkopts when generating macOS Swift toolchain linker inputs. +Emit macOS-appropriate linkopts when generating macOS Swift toolchain +linker inputs. rules_swift 4.0.0-rc4's standalone toolchain extension registers a -`swift_toolchain(...)` for macOS whose implicit linker inputs are produced by -`_swift_unix_linkopts_cc_info`. That function unconditionally emits -Linux/ELF-only flags -- `swiftrt.o`, `-lstdc++`, `-lrt`, `-ldl`, -`-static-libgcc` -- plus a Linux-shaped runtime path -(`{arch}/swiftrt.o` with `arch = "aarch64"`, but the macOS toolchain lays -out `usr/lib/swift/macos/` without `swiftrt.o` at all and uses `arm64` for -Apple Silicon). +`swift_toolchain(...)` for macOS whose implicit linker inputs are produced +by `_swift_unix_linkopts_cc_info`. That function has two problems on macOS: -On macOS the Apple linker handles Swift runtime initialization automatically -via `LC_LINKER_OPTION` autolinking and `__DATA,__mod_init_func`, so -`swiftrt.o` is neither present nor needed. The other flags are also -Linux-specific. The only linkopts that carry over are the toolchain library -search path and rpath so downstream binaries can locate -`libswiftCore.dylib` etc. +1. It unconditionally emits Linux/ELF-only flags -- `swiftrt.o`, + `-lstdc++`, `-lrt`, `-ldl`, `-static-libgcc` -- plus a Linux-shaped + runtime path (`{arch}/swiftrt.o` with `arch = "aarch64"`, but the + macOS toolchain lays out `usr/lib/swift/macosx/` without `swiftrt.o` + at all and uses `arm64` for Apple Silicon). -Without this patch, linking any `swift_library`-dependent Rust binary (e.g. -`//unified/swift-syntax-rs:swift-syntax-parse`) on macOS fails with: +2. It builds the Swift runtime library path as + `{toolchain_root}/lib/swift/{os}` using the `os` value passed in from + `_swift_toolchain_impl`, which is `"macos"` (the Bazel + `@platforms//os:macos` name). But a Swift.org macOS toolchain -- the + one rules_swift materializes via `swift_tools`' `additional_inputs = + glob(["usr/lib/swift/**", ...])` -- actually stores its libraries + under `usr/lib/swift/`**`macosx`**`/` (Apple's SDK name), not + `usr/lib/swift/macos/`. `-L .../usr/lib/swift/macos` therefore points + at a non-existent directory, so `libswiftCompatibility56.a`, + `libswiftCompatibilityConcurrency.a`, + `libswiftCompatibilityPacks.a` (auto-linked via `LC_LINKER_OPTION` + records in swiftc-emitted `.o` files) can't be resolved, and their + symbols end up undefined. + +On macOS the Apple linker handles Swift runtime initialization +automatically via `LC_LINKER_OPTION` autolinking and +`__DATA,__mod_init_func`, so `swiftrt.o` is neither present nor needed. +The Linux-specific system libraries (`-lrt`, `-ldl`, `-lstdc++`, +`-static-libgcc`) also don't exist. The only linkopts that carry over +are the toolchain library search path and rpath -- rewritten to point +at `macosx` -- so downstream binaries can locate `libswiftCore.dylib` +and the compatibility static archives. + +Without this patch, linking any `swift_library`-dependent Rust binary +(e.g. `//unified/swift-syntax-rs:swift-syntax-parse`) on macOS fails +with either: clang: error: no such file or directory: 'external/rules_swift+.../usr/lib/swift/macos/aarch64/swiftrt.o' -This should be fixed upstream: rules_swift's standalone toolchain extension -needs a macOS-aware `_swift_unix_linkopts_cc_info`. Until then, we patch the -module locally so macOS consumers can build. +(problem 1, pre-patch), or: + + ld: warning: search path + 'external/rules_swift+.../usr/lib/swift/macos' not found + ld: warning: Could not find or use auto-linked library + 'swiftCompatibility56': library 'swiftCompatibility56' not found + Undefined symbols for architecture arm64: ... + ld: symbol(s) not found for architecture arm64 + +(problem 2, if only problem 1 is fixed). + +This should be fixed upstream: rules_swift's standalone toolchain +extension needs a macOS-aware `_swift_unix_linkopts_cc_info`. Until +then, we patch the module locally so macOS consumers can build. --- a/swift/toolchains/swift_toolchain.bzl +++ b/swift/toolchains/swift_toolchain.bzl -@@ -411,25 +411,38 @@ +@@ -411,25 +411,48 @@ toolchain_root = toolchain_root, ) @@ -57,9 +87,19 @@ module locally so macOS consumers can build. + # __DATA,__mod_init_func; swiftrt.o is neither present in the + # toolchain layout nor needed. The Linux-specific system libraries + # (-lrt, -ldl, -lstdc++, -static-libgcc) also don't exist here. ++ # ++ # Also, a Swift.org macOS toolchain stores its libraries under ++ # `usr/lib/swift/macosx/` (Apple's SDK name), not the Bazel ++ # `os = "macos"` name that `_swift_toolchain_impl` passes in. Rewrite ++ # `platform_lib_dir` to point at `macosx` so `-L` and `-Wl,-rpath` ++ # resolve to a real directory (and thus `libswiftCompatibility*.a`, ++ # `libswiftCore.dylib`, etc. can be found by the linker). ++ macos_platform_lib_dir = "{toolchain_root}/lib/swift/macosx".format( ++ toolchain_root = toolchain_root, ++ ) + linkopts = [ -+ "-L{}".format(platform_lib_dir), -+ "-Wl,-rpath,{}".format(platform_lib_dir), ++ "-L{}".format(macos_platform_lib_dir), ++ "-Wl,-rpath,{}".format(macos_platform_lib_dir), + ] + [ + "-Wl,-rpath,{}".format(rpath) + for rpath in additional_rpaths From 66071ddedd2c594ebbb193d7d1c5598898b5f9e9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 13 Jul 2026 17:24:00 +0000 Subject: [PATCH 13/33] Use xcode_swift_toolchain on macOS instead of patched Unix toolchain --- .bazelrc | 18 --- MODULE.bazel | 42 +++--- misc/bazel/patches/rules_swift/BUILD.bazel | 4 - .../patches/rules_swift/macos-linkopts.patch | 128 ------------------ unified/swift-syntax-rs/BUILD.bazel | 13 +- 5 files changed, 27 insertions(+), 178 deletions(-) delete mode 100644 misc/bazel/patches/rules_swift/BUILD.bazel delete mode 100644 misc/bazel/patches/rules_swift/macos-linkopts.patch diff --git a/.bazelrc b/.bazelrc index 1c77edf1d04d..71f8957a329f 100644 --- a/.bazelrc +++ b/.bazelrc @@ -44,24 +44,6 @@ common --flag_alias=host_swiftcopt=@rules_swift//swift:exec_copt build:macos --swiftcopt=-target --swiftcopt=arm64-apple-macosx10.15 build:macos --host_swiftcopt=-target --host_swiftcopt=arm64-apple-macosx10.15 -# Disable `swift-autolink-extract` on macOS. -# -# rules_swift 4.0.0-rc4's standalone toolchain template -# (`swift/internal/extensions/BUILD.bazel.tpl`) unconditionally hardcodes -# `swift.use_autolink_extract` into the exec `swift_toolchain(...)` features -# list — even for macOS. That schedules a `SwiftAutolinkExtract` action for -# every `swift_library` (e.g. `@swift-syntax//:SwiftSyntax601`). But -# `swift-autolink-extract` is a Linux/ELF-only workflow: on macOS the Apple -# linker handles autolinking natively via `LC_LINKER_OPTION`, and running the -# tool from a swift.org standalone toolchain against Mach-O `.o` files fails -# with an opaque "worker failed" error. -# -# Negating the feature on the command line flips -# `SWIFT_FEATURE_USE_AUTOLINK_EXTRACT in ctx.features` to `False` in -# `swift_toolchain.bzl`, so the autolink-extract tool config (and therefore -# the action) is never registered on macOS. The Linux exec toolchain is -# unaffected. -build:macos --features=-swift.use_autolink_extract # Disable Android SDK auto-detection (we don't use it, and rules_android has Bazel 9 compatibility issues) build --repo_env=ANDROID_HOME= diff --git a/MODULE.bazel b/MODULE.bazel index c604ebb889ba..f13809d53848 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -34,18 +34,6 @@ 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") -# rules_swift 4.0.0-rc4's standalone toolchain extension unconditionally -# emits Linux/ELF-only linker flags (swiftrt.o, -lstdc++, -lrt, -ldl, -# -static-libgcc) for its macOS toolchain, which breaks any binary linked -# via `swift_library` on macOS. Apply a local patch that skips those flags -# when the toolchain OS is macos. See the patch header and -# `misc/bazel/patches/rules_swift/macos-linkopts.patch` for details. -single_version_override( - module_name = "rules_swift", - patch_strip = 1, - patches = ["//misc/bazel/patches/rules_swift:macos-linkopts.patch"], - version = "4.0.0-rc4", -) bazel_dep(name = "zstd", version = "1.5.7.bcr.1") bazel_dep(name = "buildifier_prebuilt", version = "6.4.0", dev_dependency = True) @@ -239,16 +227,28 @@ use_repo( # `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 distributions/platforms our CI runs on: -# - ubuntu24.04 / x86_64 (Linux CI) -# - xcode (macOS CI — covers both Apple Silicon and Intel) +# We register the `exec` toolchain for **Linux only**. On macOS we do NOT +# use this standalone toolchain: it is the Unix-shaped `swift_toolchain` +# rule (`swift/toolchains/swift_toolchain.bzl`) which emits Linux/ELF +# linkopts (`swiftrt.o`, `-lstdc++`, `-lrt`, `-ldl`, `-static-libgcc`) and +# a Linux-shaped `-L{root}/lib/swift/{os}` search path — none of which make +# sense on macOS. Instead, on macOS we rely on rules_swift's auto-registered +# `@rules_swift//swift/toolchains:xcode-toolchain-*` toolchains (backed by +# `xcode_swift_toolchain.bzl`, which has its own macOS-aware +# `_swift_linkopts_cc_info` that uses the Apple linker's autolinking, +# points at `Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx`, +# and rpaths the OS-provided `/usr/lib/swift`). Those toolchains are +# auto-registered by rules_swift's own MODULE.bazel via +# `register_toolchains("//swift/toolchains:all")` and use the Xcode +# selected on the host, so macOS contributors need a working Xcode +# (or Command Line Tools) install. # -# NOTE: The macOS toolchain is distributed as a `.pkg` archive that can only -# be extracted on a macOS host. Do not build the `xcode` toolchain on Linux. +# NOTE: The macOS standalone toolchain is distributed as a `.pkg` archive +# that can only be extracted on a macOS host. Do not build the `xcode` +# standalone toolchain on Linux. # -# We register the `exec` toolchains (normal host compilation) rather than the -# `embedded` ones, which target `os:none` (Embedded Swift) and would not +# We register the `exec` toolchain (normal host compilation) rather than the +# `embedded` one, which targets `os:none` (Embedded Swift) and would not # match a normal host build. Bazel selects the toolchain matching the host # platform automatically. # @@ -264,12 +264,10 @@ use_repo( swift, "swift_toolchain", "swift_toolchain_ubuntu24.04", - "swift_toolchain_xcode", ) register_toolchains( "@swift_toolchain//:swift_toolchain_exec_ubuntu24.04", - "@swift_toolchain//:swift_toolchain_exec_xcode", ) node = use_extension("@rules_nodejs//nodejs:extensions.bzl", "node") diff --git a/misc/bazel/patches/rules_swift/BUILD.bazel b/misc/bazel/patches/rules_swift/BUILD.bazel deleted file mode 100644 index fb6c357f995c..000000000000 --- a/misc/bazel/patches/rules_swift/BUILD.bazel +++ /dev/null @@ -1,4 +0,0 @@ -exports_files( - glob(["*.patch"]), - visibility = ["//visibility:public"], -) diff --git a/misc/bazel/patches/rules_swift/macos-linkopts.patch b/misc/bazel/patches/rules_swift/macos-linkopts.patch deleted file mode 100644 index c1bad304b274..000000000000 --- a/misc/bazel/patches/rules_swift/macos-linkopts.patch +++ /dev/null @@ -1,128 +0,0 @@ -Emit macOS-appropriate linkopts when generating macOS Swift toolchain -linker inputs. - -rules_swift 4.0.0-rc4's standalone toolchain extension registers a -`swift_toolchain(...)` for macOS whose implicit linker inputs are produced -by `_swift_unix_linkopts_cc_info`. That function has two problems on macOS: - -1. It unconditionally emits Linux/ELF-only flags -- `swiftrt.o`, - `-lstdc++`, `-lrt`, `-ldl`, `-static-libgcc` -- plus a Linux-shaped - runtime path (`{arch}/swiftrt.o` with `arch = "aarch64"`, but the - macOS toolchain lays out `usr/lib/swift/macosx/` without `swiftrt.o` - at all and uses `arm64` for Apple Silicon). - -2. It builds the Swift runtime library path as - `{toolchain_root}/lib/swift/{os}` using the `os` value passed in from - `_swift_toolchain_impl`, which is `"macos"` (the Bazel - `@platforms//os:macos` name). But a Swift.org macOS toolchain -- the - one rules_swift materializes via `swift_tools`' `additional_inputs = - glob(["usr/lib/swift/**", ...])` -- actually stores its libraries - under `usr/lib/swift/`**`macosx`**`/` (Apple's SDK name), not - `usr/lib/swift/macos/`. `-L .../usr/lib/swift/macos` therefore points - at a non-existent directory, so `libswiftCompatibility56.a`, - `libswiftCompatibilityConcurrency.a`, - `libswiftCompatibilityPacks.a` (auto-linked via `LC_LINKER_OPTION` - records in swiftc-emitted `.o` files) can't be resolved, and their - symbols end up undefined. - -On macOS the Apple linker handles Swift runtime initialization -automatically via `LC_LINKER_OPTION` autolinking and -`__DATA,__mod_init_func`, so `swiftrt.o` is neither present nor needed. -The Linux-specific system libraries (`-lrt`, `-ldl`, `-lstdc++`, -`-static-libgcc`) also don't exist. The only linkopts that carry over -are the toolchain library search path and rpath -- rewritten to point -at `macosx` -- so downstream binaries can locate `libswiftCore.dylib` -and the compatibility static archives. - -Without this patch, linking any `swift_library`-dependent Rust binary -(e.g. `//unified/swift-syntax-rs:swift-syntax-parse`) on macOS fails -with either: - - clang: error: no such file or directory: - 'external/rules_swift+.../usr/lib/swift/macos/aarch64/swiftrt.o' - -(problem 1, pre-patch), or: - - ld: warning: search path - 'external/rules_swift+.../usr/lib/swift/macos' not found - ld: warning: Could not find or use auto-linked library - 'swiftCompatibility56': library 'swiftCompatibility56' not found - Undefined symbols for architecture arm64: ... - ld: symbol(s) not found for architecture arm64 - -(problem 2, if only problem 1 is fixed). - -This should be fixed upstream: rules_swift's standalone toolchain -extension needs a macOS-aware `_swift_unix_linkopts_cc_info`. Until -then, we patch the module locally so macOS consumers can build. - ---- a/swift/toolchains/swift_toolchain.bzl -+++ b/swift/toolchains/swift_toolchain.bzl -@@ -411,25 +411,48 @@ - toolchain_root = toolchain_root, - ) - -- runtime_object_path = "{platform_lib_dir}/{cpu}/swiftrt.o".format( -- cpu = cpu, -- platform_lib_dir = platform_lib_dir, -- ) -- -- linkopts = [ -- "-pie", -- "-L{}".format(platform_lib_dir), -- "-Wl,-rpath,{}".format(platform_lib_dir), -- "-lm", -- "-lstdc++", -- "-lrt", -- "-ldl", -- runtime_object_path, -- "-static-libgcc", -- ] + [ -- "-Wl,-rpath,{}".format(rpath) -- for rpath in additional_rpaths -- ] -+ if os == "macos": -+ # On macOS, Apple's linker handles Swift runtime initialization -+ # automatically via LC_LINKER_OPTION autolinking and -+ # __DATA,__mod_init_func; swiftrt.o is neither present in the -+ # toolchain layout nor needed. The Linux-specific system libraries -+ # (-lrt, -ldl, -lstdc++, -static-libgcc) also don't exist here. -+ # -+ # Also, a Swift.org macOS toolchain stores its libraries under -+ # `usr/lib/swift/macosx/` (Apple's SDK name), not the Bazel -+ # `os = "macos"` name that `_swift_toolchain_impl` passes in. Rewrite -+ # `platform_lib_dir` to point at `macosx` so `-L` and `-Wl,-rpath` -+ # resolve to a real directory (and thus `libswiftCompatibility*.a`, -+ # `libswiftCore.dylib`, etc. can be found by the linker). -+ macos_platform_lib_dir = "{toolchain_root}/lib/swift/macosx".format( -+ toolchain_root = toolchain_root, -+ ) -+ linkopts = [ -+ "-L{}".format(macos_platform_lib_dir), -+ "-Wl,-rpath,{}".format(macos_platform_lib_dir), -+ ] + [ -+ "-Wl,-rpath,{}".format(rpath) -+ for rpath in additional_rpaths -+ ] -+ else: -+ runtime_object_path = "{platform_lib_dir}/{cpu}/swiftrt.o".format( -+ cpu = cpu, -+ platform_lib_dir = platform_lib_dir, -+ ) -+ linkopts = [ -+ "-pie", -+ "-L{}".format(platform_lib_dir), -+ "-Wl,-rpath,{}".format(platform_lib_dir), -+ "-lm", -+ "-lstdc++", -+ "-lrt", -+ "-ldl", -+ runtime_object_path, -+ "-static-libgcc", -+ ] + [ -+ "-Wl,-rpath,{}".format(rpath) -+ for rpath in additional_rpaths -+ ] - - return CcInfo( - linking_context = cc_common.create_linking_context( diff --git a/unified/swift-syntax-rs/BUILD.bazel b/unified/swift-syntax-rs/BUILD.bazel index 5fac86e4caf7..f1831b42fe56 100644 --- a/unified/swift-syntax-rs/BUILD.bazel +++ b/unified/swift-syntax-rs/BUILD.bazel @@ -59,12 +59,13 @@ rust_binary( # The Swift toolchain propagates a runfiles-relative RPATH to its runtime # `.so`s/`.dylib`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.) - # Select the correct per-platform toolchain repo: `xcode` on macOS, - # `ubuntu24.04` on Linux. + # On Linux the standalone swift.org toolchain provides the runtime, so we + # add its files as `data` so the runtime is found at execution time. + # On macOS the runtime lives in the OS at `/usr/lib/swift` (which + # `xcode_swift_toolchain` rpaths automatically), so no data is needed. + # (rules_swift does not yet support statically linking the runtime.) data = select({ - "@platforms//os:macos": ["@swift_toolchain_xcode//:files"], + "@platforms//os:macos": [], "@platforms//os:linux": ["@swift_toolchain_ubuntu24.04//:files"], }), edition = "2024", @@ -77,7 +78,7 @@ rust_test( size = "small", crate = ":swift_syntax_rs", data = select({ - "@platforms//os:macos": ["@swift_toolchain_xcode//:files"], + "@platforms//os:macos": [], "@platforms//os:linux": ["@swift_toolchain_ubuntu24.04//:files"], }), edition = "2024", From 7c8469378685c95505d91c05435f6c70cffbbb37 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 13 Jul 2026 17:42:56 +0000 Subject: [PATCH 14/33] Wire up apple_support xcode_config so @system_sdk selects resolve on macOS --- .bazelrc | 14 ++++++++++++++ MODULE.bazel | 23 +++++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/.bazelrc b/.bazelrc index 71f8957a329f..00f2004d55b7 100644 --- a/.bazelrc +++ b/.bazelrc @@ -44,6 +44,20 @@ common --flag_alias=host_swiftcopt=@rules_swift//swift:exec_copt build:macos --swiftcopt=-target --swiftcopt=arm64-apple-macosx10.15 build:macos --host_swiftcopt=-target --host_swiftcopt=arm64-apple-macosx10.15 +# Point Bazel at `apple_support`'s xcode_config on macOS. +# +# `xcode_swift_toolchain` pulls in `@system_sdk//:{all_modules,implicit_modules}`, +# whose `select({":xcode_": ...})` keys use the exact version string +# from `@bazel_tools//tools/osx:xcode_locator.m` (e.g. `xcode_26_6_0_17F113`). +# Bazel's built-in `@bazel_tools//tools/osx:host_xcodes` produces a shorter +# version without the build number, so none of the config_settings match and +# the select fails. +# +# `apple_support`'s `@local_config_xcode//:host_xcodes` uses the SAME +# `xcode_locator.m`, so its version string agrees with the select keys. +# Wired up in `MODULE.bazel` via the `xcode_configure` extension. +build:macos --xcode_version_config=@local_config_xcode//:host_xcodes + # Disable Android SDK auto-detection (we don't use it, and rules_android has Bazel 9 compatibility issues) build --repo_env=ANDROID_HOME= diff --git a/MODULE.bazel b/MODULE.bazel index f13809d53848..2aa9702da589 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -33,6 +33,12 @@ 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") +# `apple_support` is a transitive dep of `rules_swift`, but we depend on it +# directly so we can `use_repo` `local_config_xcode` (see below). This lets +# `xcode_swift_toolchain`'s `system_sdk` selects — which are keyed on the +# exact Xcode version string from `xcode_locator.m` (e.g. +# `xcode_26_6_0_17F113`) — resolve on macOS. +bazel_dep(name = "apple_support", version = "2.6.1") bazel_dep(name = "zstd", version = "1.5.7.bcr.1") @@ -270,6 +276,23 @@ register_toolchains( "@swift_toolchain//:swift_toolchain_exec_ubuntu24.04", ) +# On macOS, `xcode_swift_toolchain` (auto-registered by rules_swift at +# `@rules_swift//swift/toolchains:xcode-toolchain-*`) pulls in +# `@system_sdk//:{all_modules,implicit_modules}`, whose targets are +# `alias(..., select({":xcode_": ...}))` keyed on the exact +# Xcode version string produced by `@bazel_tools//tools/osx:xcode_locator.m` +# (e.g. `xcode_26_6_0_17F113`). Bazel's built-in `@bazel_tools//tools/osx:host_xcodes` +# reports a shorter version without the build number, so no `config_setting` +# matches and the select fails with +# "configurable attribute doesn't match this configuration". +# +# `apple_support`'s `@local_config_xcode//:host_xcodes` uses the SAME +# `xcode_locator.m` as rules_swift's `system_sdk` extension, so its version +# strings agree with the config_setting keys. Wire it up here and select it +# on macOS via `--xcode_version_config` in `.bazelrc`. +xcode_configure = use_extension("@apple_support//xcode:xcode_configure.bzl", "xcode_configure_extension") +use_repo(xcode_configure, "local_config_xcode") + node = use_extension("@rules_nodejs//nodejs:extensions.bzl", "node") node.toolchain( name = "nodejs", From f4c41e4fd170009014758f3d1b68a57111b0d3d4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 13 Jul 2026 17:55:31 +0000 Subject: [PATCH 15/33] bazel: give apple_support cc toolchains priority on macOS --- .bazelrc | 22 ++++++++++++++++++++++ MODULE.bazel | 22 ++++++++++++++++++---- 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/.bazelrc b/.bazelrc index 00f2004d55b7..604b92b93fdd 100644 --- a/.bazelrc +++ b/.bazelrc @@ -58,6 +58,28 @@ build:macos --host_swiftcopt=-target --host_swiftcopt=arm64-apple-macosx10.15 # Wired up in `MODULE.bazel` via the `xcode_configure` extension. build:macos --xcode_version_config=@local_config_xcode//:host_xcodes +# Give `apple_support`'s CC toolchains priority over Bazel's built-in +# `@rules_cc//...:local_config_cc` on macOS. +# +# `apple_support` registers `@local_config_apple_cc_toolchains//:all` from +# its own `MODULE.bazel`, but module-registered toolchains are ordered by +# the module DAG and Bazel's built-in `local_config_cc` (pulled in by +# `rules_cc`) wins resolution first. That toolchain reports a +# `target_gnu_system_name` of literally `"local"`, and +# `rules_swift`'s `xcode_swift_toolchain` blows up in +# `target_triples.parse(ctx.var.get("CC_TARGET_TRIPLE") or +# cc_toolchain.target_gnu_system_name)` with: +# +# Invalid target triple: local, this likely means you're using the wrong +# CC toolchain, make sure you include apple_support in your project +# +# Root-level `--extra_toolchains` beats all module-registered toolchains, +# so this forces `@local_config_apple_cc_toolchains//:cc-toolchain-darwin_*` +# to be selected for both the target and exec platforms. The apple_cc +# toolchains only match Apple platform constraints, so this is a no-op +# for any non-Apple exec/target platform. +build:macos --extra_toolchains=@local_config_apple_cc_toolchains//:all + # Disable Android SDK auto-detection (we don't use it, and rules_android has Bazel 9 compatibility issues) build --repo_env=ANDROID_HOME= diff --git a/MODULE.bazel b/MODULE.bazel index 2aa9702da589..f00b33343553 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -34,10 +34,24 @@ 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") # `apple_support` is a transitive dep of `rules_swift`, but we depend on it -# directly so we can `use_repo` `local_config_xcode` (see below). This lets -# `xcode_swift_toolchain`'s `system_sdk` selects — which are keyed on the -# exact Xcode version string from `xcode_locator.m` (e.g. -# `xcode_26_6_0_17F113`) — resolve on macOS. +# directly for two reasons on macOS: +# +# 1. We `use_repo` `local_config_xcode` (see below) so +# `xcode_swift_toolchain`'s `system_sdk` selects — which are keyed on +# the exact Xcode version string from `xcode_locator.m` (e.g. +# `xcode_26_6_0_17F113`) — resolve. +# +# 2. It registers `@local_config_apple_cc_toolchains//:all`, whose +# `cc-toolchain-darwin_*` toolchains report a proper Apple target +# triple (`arm64-apple-macosx` / `x86_64-apple-macosx`) via +# `target_gnu_system_name`. Bazel's built-in `local_config_cc` +# toolchain reports the literal string `"local"`, and +# `xcode_swift_toolchain` fails analysis in +# `target_triples.parse(ctx.var.get("CC_TARGET_TRIPLE") or +# cc_toolchain.target_gnu_system_name)` with +# "Invalid target triple: local". Since module-registered toolchains +# are ordered by the module DAG and `local_config_cc` wins by default, +# we override the priority via `--extra_toolchains` in `.bazelrc`. bazel_dep(name = "apple_support", version = "2.6.1") bazel_dep(name = "zstd", version = "1.5.7.bcr.1") From dca60ea74d317d239f8397f34b5d25223d291dea Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 13 Jul 2026 17:58:59 +0000 Subject: [PATCH 16/33] bazel: use_repo(apple_cc_configure, "local_config_apple_cc_toolchains") --- MODULE.bazel | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/MODULE.bazel b/MODULE.bazel index f00b33343553..19e2137404f3 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -307,6 +307,15 @@ register_toolchains( xcode_configure = use_extension("@apple_support//xcode:xcode_configure.bzl", "xcode_configure_extension") use_repo(xcode_configure, "local_config_xcode") +# `apple_support` also registers `@local_config_apple_cc_toolchains//:all` +# from its own `MODULE.bazel`, but the CC toolchains it generates lose +# toolchain-resolution ordering to Bazel's built-in `local_config_cc`. +# We override that priority in `.bazelrc` via +# `build:macos --extra_toolchains=@local_config_apple_cc_toolchains//:all`, +# which requires the repo to be visible from the root module. +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", From 86bcb720c9fd7d87b6edce6592670265e9ce8223 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 13 Jul 2026 18:08:06 +0000 Subject: [PATCH 17/33] bazel: drop swiftcopt -target workaround (obsoleted by apple_cc toolchain) --- .bazelrc | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/.bazelrc b/.bazelrc index 604b92b93fdd..e1023e500b09 100644 --- a/.bazelrc +++ b/.bazelrc @@ -21,29 +21,6 @@ build --repo_env=CC=clang --repo_env=CXX=clang++ common --flag_alias=swiftcopt=@rules_swift//swift:copt common --flag_alias=host_swiftcopt=@rules_swift//swift:exec_copt -# Force a versioned macOS target triple on Swift compilations only. -# -# Without `apple_support` in this module (see MODULE.bazel), Bazel uses its -# generic `local_config_cc` toolchain on macOS, whose `target_gnu_system_name` -# is unversioned (e.g. `arm64-apple-macosx`). Both rules_swift toolchains -# (standalone and xcode) build swiftc's `-target` argument from -# `ctx.var["CC_TARGET_TRIPLE"] or cc_toolchain.target_gnu_system_name`, so -# swiftc receives `-target arm64-apple-macosx` and fails with -# "Swift requires a minimum deployment target of macOS 10.9.0". -# -# `--macos_minimum_os` alone does not fix this (it doesn't feed either -# source). We append a versioned `-target` via `--swiftcopt` / -# `--host_swiftcopt`; swiftc treats the last `-target` as authoritative. -# These flags only affect Swift compile actions, so the scope is limited -# to //unified/swift-syntax-rs (the sole Swift consumer in this repo). -# -# 10.15 matches swift-syntax's declared minimum deployment target. -# NOTE: this pins the CPU to arm64 (Apple Silicon), which is fine because -# macOS 26 is Apple-Silicon-only. If Intel macOS Bazel builds are ever -# needed, override in local.bazelrc. -build:macos --swiftcopt=-target --swiftcopt=arm64-apple-macosx10.15 -build:macos --host_swiftcopt=-target --host_swiftcopt=arm64-apple-macosx10.15 - # Point Bazel at `apple_support`'s xcode_config on macOS. # # `xcode_swift_toolchain` pulls in `@system_sdk//:{all_modules,implicit_modules}`, From de5c1f7f43de9ad672f1723b49e31a77ec6d0fae Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 13 Jul 2026 18:14:44 +0000 Subject: [PATCH 18/33] bazel: drop --swiftcopt/--host_swiftcopt flag aliases (no longer used) --- .bazelrc | 9 --------- 1 file changed, 9 deletions(-) diff --git a/.bazelrc b/.bazelrc index e1023e500b09..dd51eb5653ca 100644 --- a/.bazelrc +++ b/.bazelrc @@ -12,15 +12,6 @@ common --override_module=semmle_code=%workspace%/misc/bazel/semmle_code_stub build --repo_env=CC=clang --repo_env=CXX=clang++ -# Register the `--swiftcopt` / `--host_swiftcopt` command-line shortcuts. -# -# rules_swift defines these as `--flag_alias` entries in its own `.bazelrc`, -# which Bazel does *not* load for consumer modules. Without registering them -# here, any use of `--swiftcopt=...` (including via `build:macos` below) fails -# with "Unrecognized option: --swiftcopt". -common --flag_alias=swiftcopt=@rules_swift//swift:copt -common --flag_alias=host_swiftcopt=@rules_swift//swift:exec_copt - # Point Bazel at `apple_support`'s xcode_config on macOS. # # `xcode_swift_toolchain` pulls in `@system_sdk//:{all_modules,implicit_modules}`, From a29be0b6143140b5d6c74589e97b1ca107c7ba34 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 13 Jul 2026 18:27:13 +0000 Subject: [PATCH 19/33] clean up wordy comments across macOS bring-up changes --- .bazelrc | 38 ++------ MODULE.bazel | 88 ++++--------------- .../extractor/src/languages/swift/adapter.rs | 85 +++++++----------- .../extractor/src/languages/swift/swift.rs | 19 ++-- unified/swift-syntax-rs/BUILD.bazel | 39 +++----- unified/swift-syntax-rs/README.md | 13 ++- unified/swift-syntax-rs/build.rs | 24 ++--- .../SwiftSyntaxFFI/SwiftSyntaxFFI.swift | 66 +++++--------- 8 files changed, 110 insertions(+), 262 deletions(-) diff --git a/.bazelrc b/.bazelrc index dd51eb5653ca..8007caeacd18 100644 --- a/.bazelrc +++ b/.bazelrc @@ -12,40 +12,14 @@ common --override_module=semmle_code=%workspace%/misc/bazel/semmle_code_stub build --repo_env=CC=clang --repo_env=CXX=clang++ -# Point Bazel at `apple_support`'s xcode_config on macOS. -# -# `xcode_swift_toolchain` pulls in `@system_sdk//:{all_modules,implicit_modules}`, -# whose `select({":xcode_": ...})` keys use the exact version string -# from `@bazel_tools//tools/osx:xcode_locator.m` (e.g. `xcode_26_6_0_17F113`). -# Bazel's built-in `@bazel_tools//tools/osx:host_xcodes` produces a shorter -# version without the build number, so none of the config_settings match and -# the select fails. -# -# `apple_support`'s `@local_config_xcode//:host_xcodes` uses the SAME -# `xcode_locator.m`, so its version string agrees with the select keys. -# Wired up in `MODULE.bazel` via the `xcode_configure` extension. +# Use apple_support's xcode_config: its version strings match the ones +# `rules_swift`'s `system_sdk` selects are keyed on. build:macos --xcode_version_config=@local_config_xcode//:host_xcodes -# Give `apple_support`'s CC toolchains priority over Bazel's built-in -# `@rules_cc//...:local_config_cc` on macOS. -# -# `apple_support` registers `@local_config_apple_cc_toolchains//:all` from -# its own `MODULE.bazel`, but module-registered toolchains are ordered by -# the module DAG and Bazel's built-in `local_config_cc` (pulled in by -# `rules_cc`) wins resolution first. That toolchain reports a -# `target_gnu_system_name` of literally `"local"`, and -# `rules_swift`'s `xcode_swift_toolchain` blows up in -# `target_triples.parse(ctx.var.get("CC_TARGET_TRIPLE") or -# cc_toolchain.target_gnu_system_name)` with: -# -# Invalid target triple: local, this likely means you're using the wrong -# CC toolchain, make sure you include apple_support in your project -# -# Root-level `--extra_toolchains` beats all module-registered toolchains, -# so this forces `@local_config_apple_cc_toolchains//:cc-toolchain-darwin_*` -# to be selected for both the target and exec platforms. The apple_cc -# toolchains only match Apple platform constraints, so this is a no-op -# for any non-Apple exec/target platform. +# Force apple_support's CC toolchains over Bazel's built-in `local_config_cc` +# (whose `target_gnu_system_name` is literally "local", which breaks +# `xcode_swift_toolchain`'s triple parsing). Root-level `--extra_toolchains` +# beats module-registered toolchains; only matches Apple platforms. build:macos --extra_toolchains=@local_config_apple_cc_toolchains//:all # Disable Android SDK auto-detection (we don't use it, and rules_android has Bazel 9 compatibility issues) diff --git a/MODULE.bazel b/MODULE.bazel index 19e2137404f3..cd7968a5a0c5 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -33,25 +33,9 @@ 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") -# `apple_support` is a transitive dep of `rules_swift`, but we depend on it -# directly for two reasons on macOS: -# -# 1. We `use_repo` `local_config_xcode` (see below) so -# `xcode_swift_toolchain`'s `system_sdk` selects — which are keyed on -# the exact Xcode version string from `xcode_locator.m` (e.g. -# `xcode_26_6_0_17F113`) — resolve. -# -# 2. It registers `@local_config_apple_cc_toolchains//:all`, whose -# `cc-toolchain-darwin_*` toolchains report a proper Apple target -# triple (`arm64-apple-macosx` / `x86_64-apple-macosx`) via -# `target_gnu_system_name`. Bazel's built-in `local_config_cc` -# toolchain reports the literal string `"local"`, and -# `xcode_swift_toolchain` fails analysis in -# `target_triples.parse(ctx.var.get("CC_TARGET_TRIPLE") or -# cc_toolchain.target_gnu_system_name)` with -# "Invalid target triple: local". Since module-registered toolchains -# are ordered by the module DAG and `local_config_cc` wins by default, -# we override the priority via `--extra_toolchains` in `.bazelrc`. +# Direct dep on `apple_support` (transitively pulled in by `rules_swift`) so we +# can `use_repo` `local_config_xcode` and `local_config_apple_cc_toolchains` +# below, needed to make `xcode_swift_toolchain` work on macOS. See `.bazelrc`. bazel_dep(name = "apple_support", version = "2.6.1") bazel_dep(name = "zstd", version = "1.5.7.bcr.1") @@ -242,39 +226,12 @@ 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. -# -# We register the `exec` toolchain for **Linux only**. On macOS we do NOT -# use this standalone toolchain: it is the Unix-shaped `swift_toolchain` -# rule (`swift/toolchains/swift_toolchain.bzl`) which emits Linux/ELF -# linkopts (`swiftrt.o`, `-lstdc++`, `-lrt`, `-ldl`, `-static-libgcc`) and -# a Linux-shaped `-L{root}/lib/swift/{os}` search path — none of which make -# sense on macOS. Instead, on macOS we rely on rules_swift's auto-registered -# `@rules_swift//swift/toolchains:xcode-toolchain-*` toolchains (backed by -# `xcode_swift_toolchain.bzl`, which has its own macOS-aware -# `_swift_linkopts_cc_info` that uses the Apple linker's autolinking, -# points at `Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx`, -# and rpaths the OS-provided `/usr/lib/swift`). Those toolchains are -# auto-registered by rules_swift's own MODULE.bazel via -# `register_toolchains("//swift/toolchains:all")` and use the Xcode -# selected on the host, so macOS contributors need a working Xcode -# (or Command Line Tools) install. -# -# NOTE: The macOS standalone toolchain is distributed as a `.pkg` archive -# that can only be extracted on a macOS host. Do not build the `xcode` -# standalone toolchain on Linux. -# -# We register the `exec` toolchain (normal host compilation) rather than the -# `embedded` one, which targets `os:none` (Embedded Swift) and would not -# match a normal host build. Bazel selects the toolchain matching the host -# platform automatically. -# -# 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`. +# Hermetic Swift toolchain (swift.org) for building `unified/swift-syntax-rs`. +# Only registered as an `exec` toolchain on Linux; on macOS we rely on +# `rules_swift`'s auto-registered `xcode_swift_toolchain`, which uses the +# host Xcode and links against the OS-provided Swift runtime in `/usr/lib/swift`. +# Version is read from `unified/swift-syntax-rs/.swift-version`, shared with +# the local `cargo` build and `swift/Package.swift`. swift = use_extension("@rules_swift//swift:extensions.bzl", "swift") swift.toolchain( name = "swift_toolchain", @@ -290,29 +247,16 @@ register_toolchains( "@swift_toolchain//:swift_toolchain_exec_ubuntu24.04", ) -# On macOS, `xcode_swift_toolchain` (auto-registered by rules_swift at -# `@rules_swift//swift/toolchains:xcode-toolchain-*`) pulls in -# `@system_sdk//:{all_modules,implicit_modules}`, whose targets are -# `alias(..., select({":xcode_": ...}))` keyed on the exact -# Xcode version string produced by `@bazel_tools//tools/osx:xcode_locator.m` -# (e.g. `xcode_26_6_0_17F113`). Bazel's built-in `@bazel_tools//tools/osx:host_xcodes` -# reports a shorter version without the build number, so no `config_setting` -# matches and the select fails with -# "configurable attribute doesn't match this configuration". -# -# `apple_support`'s `@local_config_xcode//:host_xcodes` uses the SAME -# `xcode_locator.m` as rules_swift's `system_sdk` extension, so its version -# strings agree with the config_setting keys. Wire it up here and select it -# on macOS via `--xcode_version_config` in `.bazelrc`. +# `apple_support`'s xcode_config, selected via `--xcode_version_config` in +# `.bazelrc`. Needed because rules_swift's `system_sdk` selects use Xcode +# version strings from `apple_support`'s `xcode_locator.m`, which Bazel's +# built-in `host_xcodes` does not match. xcode_configure = use_extension("@apple_support//xcode:xcode_configure.bzl", "xcode_configure_extension") use_repo(xcode_configure, "local_config_xcode") -# `apple_support` also registers `@local_config_apple_cc_toolchains//:all` -# from its own `MODULE.bazel`, but the CC toolchains it generates lose -# toolchain-resolution ordering to Bazel's built-in `local_config_cc`. -# We override that priority in `.bazelrc` via -# `build:macos --extra_toolchains=@local_config_apple_cc_toolchains//:all`, -# which requires the repo to be visible from the root module. +# `apple_support`'s CC toolchains, forced ahead of Bazel's built-in +# `local_config_cc` via `--extra_toolchains` in `.bazelrc`. Must be visible +# from the root module for the flag to resolve. apple_cc_configure = use_extension("@apple_support//crosstool:setup.bzl", "apple_cc_configure_extension") use_repo(apple_cc_configure, "local_config_apple_cc_toolchains") diff --git a/unified/extractor/src/languages/swift/adapter.rs b/unified/extractor/src/languages/swift/adapter.rs index 37d5aac00d2d..c28e14cf2f0d 100644 --- a/unified/extractor/src/languages/swift/adapter.rs +++ b/unified/extractor/src/languages/swift/adapter.rs @@ -1,26 +1,19 @@ -//! Converts the swift-syntax JSON syntax tree into a [`yeast::Ast`], the -//! in-memory format the CodeQL desugaring rules operate on. +//! Converts the swift-syntax JSON syntax tree into a [`yeast::Ast`]. //! -//! The JSON tree is produced by the `swift-syntax-rs` crate's Swift FFI shim -//! (`parse_to_json`). This module is pure Rust (only `yeast` + `serde_json`), -//! so the extractor consumes swift-syntax output without pulling in the Swift -//! toolchain (the JSON is produced out-of-process). +//! The JSON is produced out-of-process by `swift-syntax-rs`'s FFI shim, so +//! this module is pure Rust (no Swift toolchain needed). //! -//! The mapping mirrors tree-sitter's node model, which is what yeast (and the -//! extractor's rewrite rules) expect: +//! The mapping mirrors tree-sitter's node model: //! -//! * **Layout nodes** (e.g. `functionDecl`) and **varying tokens** (identifiers, -//! literals, operators — the ones whose text is not determined by their kind) -//! become **named** nodes, keyed by their kind name. -//! * **Fixed tokens** (keywords and punctuation, whose text is fully determined -//! by their kind) become **anonymous** nodes, keyed by their text — exactly -//! how tree-sitter models anonymous tokens (e.g. `"func"`, `"->"`). -//! * Collection nodes are already elided to JSON arrays upstream, so a -//! list-valued field maps directly to that field holding several children. +//! * **Layout nodes** (e.g. `functionDecl`) and **varying tokens** +//! (identifiers, literals, operators — text not determined by kind) become +//! **named** nodes, keyed by their kind name. +//! * **Fixed tokens** (keywords, punctuation) become **anonymous** nodes, +//! keyed by their text (like tree-sitter's anonymous tokens). +//! * Collection nodes are already elided to JSON arrays upstream. //! -//! Note: this preserves swift-syntax's own kind/field names. Aligning those -//! names with the tree-sitter-swift schema (so the rewrite rules in -//! [`super::swift`] fire) is done incrementally in the rules. +//! Kind/field names are preserved from swift-syntax; aligning them with the +//! tree-sitter-swift schema is done incrementally in the rewrite rules. use std::collections::BTreeMap; @@ -29,32 +22,29 @@ use yeast::schema::Schema; use yeast::{Ast, Id, NodeContent, Point, Range}; /// A comment (or `unexpectedText`) recovered from the syntax tree's trivia. -/// -/// These are collected into a side channel rather than embedded in the -/// [`yeast::Ast`], mirroring how the extractor treats tree-sitter `extra` -/// nodes: they carry a location and text but are not attached to a parent. +/// Collected into a side channel rather than embedded in the [`yeast::Ast`], +/// mirroring how the extractor treats tree-sitter `extra` nodes. #[derive(Debug, Clone, PartialEq, Eq)] pub struct TriviaToken { - /// The trivia kind (e.g. `lineComment`, `blockComment`, `docLineComment`, - /// `docBlockComment`, `unexpectedText`). + /// The trivia kind (e.g. `lineComment`, `docLineComment`, `unexpectedText`). pub kind: String, - /// The verbatim source text of the piece (e.g. `// comment`). + /// The verbatim source text (e.g. `// comment`). pub text: String, /// The source range the piece occupies. pub range: Range, } -/// The result of adapting a swift-syntax JSON tree: the [`yeast::Ast`] plus the -/// comment/`unexpectedText` trivia harvested from it (in source order). +/// The result of adapting a swift-syntax JSON tree: the [`yeast::Ast`] plus +/// the comment/`unexpectedText` trivia harvested from it (in source order). pub struct AdaptedTree { pub ast: Ast, pub trivia: Vec, } /// swift-syntax `TokenKind` cases whose text is *not* determined by the kind -/// (i.e. `TokenKind.defaultText == nil`). These carry varying information and -/// are modelled as named leaf nodes; every other token is a fixed -/// keyword/punctuation token modelled as an anonymous token keyed by its text. +/// (i.e. `TokenKind.defaultText == nil`). Modelled as named leaf nodes; every +/// other token is a fixed keyword/punctuation, modelled as an anonymous token +/// keyed by its text. const VARYING_TOKEN_KINDS: &[&str] = &[ "identifier", "integerLiteral", @@ -116,7 +106,7 @@ fn classify(node: &Value) -> Result { .unwrap_or("") .to_string(); - // The case name is the part before any `(payload)` in the debug rendering. + // Case name is the part before any `(payload)` in the debug rendering. let case_name = token_kind.split('(').next().unwrap_or(token_kind); if VARYING_TOKEN_KINDS.contains(&case_name) { @@ -142,8 +132,7 @@ fn classify(node: &Value) -> Result { } } -/// Iterate over a node object's structural (field, value) pairs in a stable -/// order, skipping metadata keys. +/// Iterate over a node's structural (field, value) pairs, skipping metadata. fn field_entries(node: &Value) -> Vec<(&str, &Value)> { node.as_object() .map(|map| { @@ -155,8 +144,8 @@ fn field_entries(node: &Value) -> Vec<(&str, &Value)> { .unwrap_or_default() } -/// The child node objects held by a field value, which is either a single node -/// object or an array of them (an elided collection). +/// Child nodes held by a field value: either a single node or an elided +/// collection (JSON array). fn children_of(value: &Value) -> Vec<&Value> { match value { Value::Array(items) => items.iter().collect(), @@ -165,12 +154,8 @@ fn children_of(value: &Value) -> Vec<&Value> { } /// Recursively build `node` (and its descendants) into `ast`, returning its id. -/// -/// This is a single traversal: each node's kind and field names are registered -/// in the schema on the fly, immediately before the node is created. Children -/// are built first so a parent's field lists reference existing ids. Any -/// comment/`unexpectedText` trivia carried by a token is harvested into -/// `trivia` during the same pass rather than embedded in the tree. +/// Kinds and field names are registered in the schema on the fly. Comment / +/// `unexpectedText` trivia is harvested into `trivia` in the same pass. fn build(node: &Value, ast: &mut Ast, trivia: &mut Vec) -> Result { let info = classify(node)?; collect_trivia(node, trivia); @@ -200,9 +185,8 @@ fn build(node: &Value, ast: &mut Ast, trivia: &mut Vec) -> Result) { for key in ["leadingTrivia", "trailingTrivia"] { let Some(Value::Array(pieces)) = node.get(key) else { @@ -231,11 +215,9 @@ fn collect_trivia(node: &Value, out: &mut Vec) { /// Parse a node's `range` into a [`yeast::Range`]. /// -/// The JSON carries, for `start` and `end`, a 0-based UTF-8 file byte `offset`, -/// a 1-based `line`, and a 1-based UTF-8 byte `column`. yeast (like tree-sitter) -/// uses byte offsets with 0-based rows/columns and an exclusive end, so the -/// line/column are shifted down by one. swift-syntax's end position is already -/// exclusive, so the byte offsets map across directly. +/// JSON offsets are 0-based UTF-8 byte offsets; `line`/`column` are 1-based +/// UTF-8. yeast uses byte offsets with 0-based rows/columns (exclusive end), +/// so line/column are shifted down by one. fn parse_range(node: &Value) -> Option { let range = node.get("range")?; let point = |key: &str| -> Option<(usize, Point)> { @@ -259,8 +241,7 @@ fn parse_range(node: &Value) -> Option { } /// Convert a swift-syntax JSON tree (as produced by [`crate::parse_to_json`]) -/// into a [`yeast::Ast`] plus the comment/`unexpectedText` trivia harvested -/// from it. Both are produced in a single traversal. +/// into a [`yeast::Ast`] plus harvested trivia, in a single traversal. pub fn json_to_ast(json: &str) -> Result { let root: Value = serde_json::from_str(json).map_err(|e| format!("invalid JSON: {e}"))?; diff --git a/unified/extractor/src/languages/swift/swift.rs b/unified/extractor/src/languages/swift/swift.rs index 13f1a6beadf0..640d79e58605 100644 --- a/unified/extractor/src/languages/swift/swift.rs +++ b/unified/extractor/src/languages/swift/swift.rs @@ -135,19 +135,12 @@ fn translation_rules() -> Vec> { // Declarations may be wrapped in local/global wrapper nodes. rule!((global_declaration _ @inner) => stmt { inner }), rule!((local_declaration _ @inner) => stmt { inner }), - // ---- swift-syntax front-end (minimal hook-up) ---- - // These rules target the swift-syntax AST (camelCase kind names), - // produced by the sibling `adapter` module. They coexist with the - // tree-sitter rules (snake_case names): rules are dispatched by exact - // kind name, and the two name spaces never collide, so these are inert - // on the tree-sitter path. Only the minimal top-level mapping lives here - // to demonstrate the pipeline end-to-end; the full translation is added - // separately. Unmatched swift-syntax nodes fall through to the - // `unsupported_node` fallback at the end. - // - // `sourceFile` holds its top-level statements in an (elided) - // `statements` collection; each element is a `codeBlockItem` wrapping - // the real node. + // ---- swift-syntax front-end ---- + // Rules targeting the swift-syntax AST (camelCase kind names, built + // by the sibling `adapter` module). Kind namespaces don't overlap + // with the tree-sitter (snake_case) rules, so these are inert on + // the tree-sitter path. Unmatched nodes fall through to the + // `unsupported_node` fallback below. rule!( (sourceFile statements: _* @items) => diff --git a/unified/swift-syntax-rs/BUILD.bazel b/unified/swift-syntax-rs/BUILD.bazel index f1831b42fe56..ce4e35a749ca 100644 --- a/unified/swift-syntax-rs/BUILD.bazel +++ b/unified/swift-syntax-rs/BUILD.bazel @@ -3,28 +3,21 @@ load("@rules_swift//swift:swift.bzl", "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"]) -# Constraint expressing "this target only builds on platforms where we have -# a Swift toolchain registered in //:MODULE.bazel" (i.e. Linux or macOS — -# not Windows). `target_compatible_with` uses AND semantics on a plain list, -# so we express the OR via a `select()` that maps supported OSes to the -# empty (satisfiable) constraint list, and everything else to the sentinel -# `@platforms//:incompatible`, which causes Bazel to skip the target on -# `bazel build/test //...` rather than fail. +# 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"], }) -# 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 FFI shim: wraps swift-syntax and exposes a small C ABI. The Rust +# targets below link against its `CcInfo`. swift_library( name = "swift_syntax_ffi", srcs = ["swift/Sources/SwiftSyntaxFFI/SwiftSyntaxFFI.swift"], @@ -36,10 +29,8 @@ swift_library( ], ) -# 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( @@ -56,14 +47,10 @@ 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/`.dylib`s via `swift_syntax_ffi`'s `CcInfo`, but (unlike - # `swift_binary`) `rust_binary` does not copy those libraries into runfiles. - # On Linux the standalone swift.org toolchain provides the runtime, so we - # add its files as `data` so the runtime is found at execution time. - # On macOS the runtime lives in the OS at `/usr/lib/swift` (which - # `xcode_swift_toolchain` rpaths automatically), so no data is needed. - # (rules_swift does not yet support statically linking the runtime.) + # `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"], diff --git a/unified/swift-syntax-rs/README.md b/unified/swift-syntax-rs/README.md index 48ef4ca3e42d..60cf9809d8e1 100644 --- a/unified/swift-syntax-rs/README.md +++ b/unified/swift-syntax-rs/README.md @@ -145,14 +145,11 @@ Requirements: CC toolchain to use clang; the repo's `.bazelrc` already sets `--repo_env=CC=clang`, so no extra flags are needed. - The registered Swift toolchains cover **ubuntu24.04 / x86_64** and - **macOS / `xcode`** (both Apple Silicon and Intel). Bazel automatically - selects the toolchain matching the host platform. The Bazel targets are - marked `target_compatible_with` these two OSes only, so on Windows (or any - other unsupported host) Bazel skips them cleanly under `bazel build/test - //...` rather than trying to build a Swift-less target. -- **macOS only:** the macOS toolchain is distributed as a `.pkg` archive that - can only be fetched and extracted on a macOS host. Building the `xcode` - toolchain on Linux is not supported. + **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 macOS toolchain is a `.pkg` archive that can only be + fetched and extracted on a macOS host. The Swift compiler version is read from [`.swift-version`](.swift-version) by both the Bazel toolchain (`swift.toolchain(swift_version_file = …)`) and the diff --git a/unified/swift-syntax-rs/build.rs b/unified/swift-syntax-rs/build.rs index 3cd2e2858742..b97ca9b600d5 100644 --- a/unified/swift-syntax-rs/build.rs +++ b/unified/swift-syntax-rs/build.rs @@ -49,8 +49,8 @@ fn main() { } } -/// Query the active Swift toolchain for the directory containing its runtime -/// shared libraries (e.g. `libswiftCore.so`). +/// Directory containing the active Swift toolchain's runtime libraries +/// (e.g. `libswiftCore.so`). fn swift_runtime_dir() -> Option { let output = Command::new(swiftc_bin()) .arg("-print-target-info") @@ -61,8 +61,7 @@ fn swift_runtime_dir() -> Option { } let info = String::from_utf8_lossy(&output.stdout); - // Extract the value of `"runtimeResourcePath": "..."` without pulling in a - // JSON dependency. + // Extract `"runtimeResourcePath": "..."` without pulling in a JSON dep. let key = "\"runtimeResourcePath\""; let start = info.find(key)?; let rest = &info[start + key.len()..]; @@ -76,25 +75,20 @@ fn swift_runtime_dir() -> Option { 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`. -/// This keeps the build tool-agnostic — any Swift install works; no particular -/// version manager is required. +/// `swift` driver: `$SWIFT` if set, else `swift` from `PATH`. fn swift_bin() -> String { env::var("SWIFT").unwrap_or_else(|_| "swift".to_string()) } -/// The `swiftc` compiler to invoke: `$SWIFTC` if set, otherwise `swiftc` from -/// `PATH`. +/// `swiftc` compiler: `$SWIFTC` if set, else `swiftc` from `PATH`. fn swiftc_bin() -> String { env::var("SWIFTC").unwrap_or_else(|_| "swiftc".to_string()) } -/// Some environments (notably GitHub Codespaces) inject -/// `GIT_CONFIG_KEY_0=safe.bareRepository` / `GIT_CONFIG_VALUE_0=explicit`, which -/// breaks the cached bare git repositories `swift build` uses. When exactly that -/// key is present, relax it to `all` for the `swift build` subprocess only -/// (rather than unconditionally, which could clobber an unrelated -/// `GIT_CONFIG_VALUE_0`). +/// Workaround for GitHub Codespaces, which sets +/// `GIT_CONFIG_KEY_0=safe.bareRepository` / `GIT_CONFIG_VALUE_0=explicit` and +/// breaks the cached bare git repos `swift build` uses. Relax to `all` only +/// for this subprocess, and only when that exact key is set. fn apply_bare_repository_workaround(command: &mut Command) { if env::var("GIT_CONFIG_KEY_0").as_deref() == Ok("safe.bareRepository") { command.env("GIT_CONFIG_VALUE_0", "all"); diff --git a/unified/swift-syntax-rs/swift/Sources/SwiftSyntaxFFI/SwiftSyntaxFFI.swift b/unified/swift-syntax-rs/swift/Sources/SwiftSyntaxFFI/SwiftSyntaxFFI.swift index 6305b1610d06..3420ba26b838 100644 --- a/unified/swift-syntax-rs/swift/Sources/SwiftSyntaxFFI/SwiftSyntaxFFI.swift +++ b/unified/swift-syntax-rs/swift/Sources/SwiftSyntaxFFI/SwiftSyntaxFFI.swift @@ -1,8 +1,8 @@ import Foundation import SwiftParser -// `@_spi(RawSyntax)` exposes the `childName(_:)` helper that maps a child's -// key path to its field name in the parent layout; used to emit named fields. +// `@_spi(RawSyntax)` exposes `childName(_:)`, which maps a child's key path +// to its field name in the parent layout. @_spi(RawSyntax) import SwiftSyntax #if canImport(Glibc) @@ -11,8 +11,7 @@ import SwiftParser import Darwin #endif -/// Convert an absolute position into an `{ offset, line, column }` dictionary. -/// +/// Convert an absolute position into `{ offset, line, column }`. /// `offset` is a UTF-8 byte offset; `line`/`column` are 1-based. private func location( _ position: AbsolutePosition, @@ -26,11 +25,9 @@ private func location( ] } -/// Trivia kinds worth preserving in the serialized tree. Comments carry -/// developer intent (including doc comments), and `unexpectedText` flags source -/// the parser had to skip. Whitespace and multi-line-string escape markers are -/// dropped: node ranges already encode positions, so they would only bloat the -/// output. +/// Trivia kinds worth preserving: comments (including doc comments) and +/// `unexpectedText` (source the parser skipped). Whitespace is dropped since +/// node ranges already encode positions. private let keptTriviaKinds: Set = [ "lineComment", "blockComment", @@ -39,13 +36,10 @@ private let keptTriviaKinds: Set = [ "unexpectedText", ] -/// Serialize a trivia collection into an array of `{ kind, text, range }` -/// pieces, keeping only the kinds in `keptTriviaKinds`. -/// -/// `start` is the absolute position of the first piece (a token's leading -/// trivia starts at `token.position`; its trailing trivia at -/// `token.endPositionBeforeTrailingTrivia`). Each piece's range is derived by -/// accumulating piece lengths, so kept pieces carry an exact source location. +/// Serialize a trivia collection into `{ kind, text, range }` pieces, keeping +/// only kinds in `keptTriviaKinds`. `start` is the absolute position of the +/// first piece (leading trivia at `token.position`; trailing trivia at +/// `token.endPositionBeforeTrailingTrivia`). private func serializeTrivia( _ trivia: Trivia, startingAt start: AbsolutePosition, @@ -55,9 +49,8 @@ private func serializeTrivia( var offset = start.utf8Offset for piece in trivia.pieces { let length = piece.sourceLength.utf8Length - // The label of an enum case mirror is the case name (e.g. "spaces", - // "lineComment"), which gives us a stable kind without an exhaustive - // switch over every `TriviaPiece` case. + // Enum-case mirror label gives us a stable kind (e.g. "lineComment") + // without an exhaustive switch over every TriviaPiece case. let kind = Mirror(reflecting: piece).children.first?.label ?? "\(piece)" if keptTriviaKinds.contains(kind) { result.append([ @@ -77,19 +70,12 @@ private func serializeTrivia( /// Recursively convert a SwiftSyntax node into a JSON-serializable value. /// /// * Tokens carry `kind`, `tokenKind`, `text`, and `range`, plus -/// `leadingTrivia`/`trailingTrivia` — but only when non-empty (after -/// filtering, most tokens have no trivia, so the keys are simply absent). -/// * Layout nodes (e.g. `functionDecl`) carry `kind` and source `range`, and -/// additionally embed their children directly as members keyed by the -/// child's name in the parent (e.g. `name`, `signature`, `body`); absent -/// optional children are omitted. Field names never collide with -/// `kind`/`range`. -/// * Collection nodes (e.g. `codeBlockItemList`) are *elided*: they become a -/// plain array of their serialized elements, taking the place of the -/// collection node itself. A list-valued layout field (e.g. `parameters`) is -/// therefore simply a JSON array. This drops the collection node's own -/// `kind`/`range`, which are unnamed and largely recoverable from the -/// elements. +/// `leadingTrivia`/`trailingTrivia` when non-empty. +/// * Layout nodes (e.g. `functionDecl`) carry `kind` and `range`, and +/// embed their children as members keyed by the field name in the parent +/// (e.g. `name`, `body`); absent optional children are omitted. +/// * Collection nodes (e.g. `codeBlockItemList`) are elided to a plain array +/// of their serialized elements — their own `kind`/`range` are dropped. private func serialize( _ node: Syntax, _ converter: SourceLocationConverter @@ -100,7 +86,7 @@ private func serialize( } } - // Source range covering the node's content, excluding surrounding trivia. + // Node's content range, excluding surrounding trivia. let range: [String: Any] = [ "start": location(node.positionAfterSkippingLeadingTrivia, converter), "end": location(node.endPositionBeforeTrailingTrivia, converter), @@ -113,9 +99,6 @@ private func serialize( "text": token.text, "range": range, ] - // Only emit trivia when present; after filtering, most tokens have none. - // Leading trivia starts at the token's own position; trailing trivia - // starts just after the token's content. let leading = serializeTrivia( token.leadingTrivia, startingAt: token.position, converter) if !leading.isEmpty { @@ -137,9 +120,6 @@ private func serialize( ] var unnamed = 0 for child in node.children(viewMode: .sourceAccurate) { - // Layout children are named; embed each under its field name in the - // parent (the same mechanism SwiftSyntax uses for its debug dump). A - // child that is a collection serializes to an array (see above). if let keyPath = child.keyPathInParent, let name = childName(keyPath) { result[name] = serialize(child, converter) } else { @@ -151,11 +131,9 @@ private func serialize( return result } -/// Parse the given NUL-terminated Swift source string and return a -/// heap-allocated, NUL-terminated JSON representation of the syntax tree. -/// -/// The returned pointer is owned by the caller and MUST be released with -/// `ssr_string_free`. Returns `nil` on failure. +/// Parse a NUL-terminated Swift source string and return a heap-allocated, +/// NUL-terminated JSON representation of the syntax tree. Caller must release +/// the returned pointer with `ssr_string_free`. Returns `nil` on failure. @_cdecl("ssr_parse_json") public func ssr_parse_json(_ source: UnsafePointer?) -> UnsafeMutablePointer? { guard let source = source else { return nil } From 32dcc0f418fc083ef6d3b33d4d7775b3ed61de88 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 13 Jul 2026 18:53:15 +0000 Subject: [PATCH 20/33] bazel: scope Xcode-coupled flags to Swift builds via --config=swift_macos --- .bazelrc | 24 +++++++++++++++--------- .github/workflows/swift.yml | 10 ++++++++-- MODULE.bazel | 15 +++++++++------ unified/swift-syntax-rs/README.md | 5 +++++ 4 files changed, 37 insertions(+), 17 deletions(-) diff --git a/.bazelrc b/.bazelrc index 8007caeacd18..13919796e5c9 100644 --- a/.bazelrc +++ b/.bazelrc @@ -12,15 +12,21 @@ common --override_module=semmle_code=%workspace%/misc/bazel/semmle_code_stub build --repo_env=CC=clang --repo_env=CXX=clang++ -# Use apple_support's xcode_config: its version strings match the ones -# `rules_swift`'s `system_sdk` selects are keyed on. -build:macos --xcode_version_config=@local_config_xcode//:host_xcodes - -# Force apple_support's CC toolchains over Bazel's built-in `local_config_cc` -# (whose `target_gnu_system_name` is literally "local", which breaks -# `xcode_swift_toolchain`'s triple parsing). Root-level `--extra_toolchains` -# beats module-registered toolchains; only matches Apple platforms. -build:macos --extra_toolchains=@local_config_apple_cc_toolchains//:all +# macOS Xcode-coupled flags, scoped to Swift builds only so that non-Swift +# builds (C/C++/Rust/...) work with just the Command Line Tools. Enable +# with `--config=swift_macos` (see `unified/swift-syntax-rs/README.md`; CI +# for the `swift/` package passes it in `.github/workflows/swift.yml`). +# +# - `--xcode_version_config` selects `apple_support`'s xcode_config, whose +# version strings match the ones `rules_swift`'s `system_sdk` selects are +# keyed on (Bazel's built-in `host_xcodes` does not match). +# - `--extra_toolchains` forces `apple_support`'s CC toolchain ahead of +# Bazel's built-in `local_config_cc` (whose `target_gnu_system_name` is +# literally "local", which breaks `xcode_swift_toolchain`'s triple +# parsing). Root-level `--extra_toolchains` beats module-registered +# toolchains; only matches Apple platforms. +build:swift_macos --xcode_version_config=@local_config_xcode//:host_xcodes +build:swift_macos --extra_toolchains=@local_config_apple_cc_toolchains//:all # Disable Android SDK auto-detection (we don't use it, and rules_android has Bazel 9 compatibility issues) build --repo_env=ANDROID_HOME= diff --git a/.github/workflows/swift.yml b/.github/workflows/swift.yml index 4a5613f988e5..ad2cb0d7fc9d 100644 --- a/.github/workflows/swift.yml +++ b/.github/workflows/swift.yml @@ -44,12 +44,18 @@ jobs: sudo apt-get install -y uuid-dev zlib1g-dev - name: Build Swift extractor shell: bash + env: + # On macOS, opt into the Xcode-coupled toolchain wiring only for + # Swift builds. Empty on Linux (hermetic swift.org toolchain). + BAZEL_SWIFT_CONFIG: ${{ runner.os == 'macOS' && '--config=swift_macos' || '' }} run: | - bazel run :install + bazel run $BAZEL_SWIFT_CONFIG :install - name: Run Swift tests shell: bash + env: + BAZEL_SWIFT_CONFIG: ${{ runner.os == 'macOS' && '--config=swift_macos' || '' }} run: | - bazel test ... --test_tag_filters=-override --test_output=errors + bazel test $BAZEL_SWIFT_CONFIG ... --test_tag_filters=-override --test_output=errors clang-format: runs-on: ubuntu-latest steps: diff --git a/MODULE.bazel b/MODULE.bazel index cd7968a5a0c5..86a59a3eae7c 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -35,7 +35,8 @@ bazel_dep(name = "rules_swift", version = "4.0.0-rc4") bazel_dep(name = "swift-syntax", version = "602.0.0.bcr.2") # Direct dep on `apple_support` (transitively pulled in by `rules_swift`) so we # can `use_repo` `local_config_xcode` and `local_config_apple_cc_toolchains` -# below, needed to make `xcode_swift_toolchain` work on macOS. See `.bazelrc`. +# below, needed by the `swift_macos` bazelrc config that makes +# `xcode_swift_toolchain` work on macOS. See `.bazelrc`. bazel_dep(name = "apple_support", version = "2.6.1") bazel_dep(name = "zstd", version = "1.5.7.bcr.1") @@ -248,15 +249,17 @@ register_toolchains( ) # `apple_support`'s xcode_config, selected via `--xcode_version_config` in -# `.bazelrc`. Needed because rules_swift's `system_sdk` selects use Xcode -# version strings from `apple_support`'s `xcode_locator.m`, which Bazel's -# built-in `host_xcodes` does not match. +# the `swift_macos` bazelrc config (see `.bazelrc`). Needed because +# rules_swift's `system_sdk` selects use Xcode version strings from +# `apple_support`'s `xcode_locator.m`, which Bazel's built-in `host_xcodes` +# does not match. xcode_configure = use_extension("@apple_support//xcode:xcode_configure.bzl", "xcode_configure_extension") use_repo(xcode_configure, "local_config_xcode") # `apple_support`'s CC toolchains, forced ahead of Bazel's built-in -# `local_config_cc` via `--extra_toolchains` in `.bazelrc`. Must be visible -# from the root module for the flag to resolve. +# `local_config_cc` via `--extra_toolchains` in the `swift_macos` bazelrc +# config (see `.bazelrc`). Must be visible from the root module for the flag +# to resolve. apple_cc_configure = use_extension("@apple_support//crosstool:setup.bzl", "apple_cc_configure_extension") use_repo(apple_cc_configure, "local_config_apple_cc_toolchains") diff --git a/unified/swift-syntax-rs/README.md b/unified/swift-syntax-rs/README.md index 60cf9809d8e1..97d2c7a9e634 100644 --- a/unified/swift-syntax-rs/README.md +++ b/unified/swift-syntax-rs/README.md @@ -139,6 +139,11 @@ bazel test //unified/swift-syntax-rs:swift_syntax_rs_test bazel run //unified/swift-syntax-rs:swift-syntax-parse < some.swift ``` +On **macOS**, add `--config=swift_macos` to any of the above so that +`rules_swift`'s `xcode_swift_toolchain` gets the Xcode-coupled CC toolchain +and xcode_config it needs. The flag is opt-in so that non-Swift builds on +macOS don't require a full Xcode.app install. + Requirements: - **`clang`** must be installed on the runner. `rules_swift` requires the Bazel From 45a42b0393d2eec971e0a8dbf021ed697d9d24bf Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 13 Jul 2026 22:31:19 +0000 Subject: [PATCH 21/33] Revert swift.yml change: swift/ builds C++, not Swift code --- .bazelrc | 3 +-- .github/workflows/swift.yml | 10 ++-------- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/.bazelrc b/.bazelrc index 13919796e5c9..55a9b72c3a14 100644 --- a/.bazelrc +++ b/.bazelrc @@ -14,8 +14,7 @@ build --repo_env=CC=clang --repo_env=CXX=clang++ # macOS Xcode-coupled flags, scoped to Swift builds only so that non-Swift # builds (C/C++/Rust/...) work with just the Command Line Tools. Enable -# with `--config=swift_macos` (see `unified/swift-syntax-rs/README.md`; CI -# for the `swift/` package passes it in `.github/workflows/swift.yml`). +# with `--config=swift_macos` (see `unified/swift-syntax-rs/README.md`). # # - `--xcode_version_config` selects `apple_support`'s xcode_config, whose # version strings match the ones `rules_swift`'s `system_sdk` selects are diff --git a/.github/workflows/swift.yml b/.github/workflows/swift.yml index ad2cb0d7fc9d..4a5613f988e5 100644 --- a/.github/workflows/swift.yml +++ b/.github/workflows/swift.yml @@ -44,18 +44,12 @@ jobs: sudo apt-get install -y uuid-dev zlib1g-dev - name: Build Swift extractor shell: bash - env: - # On macOS, opt into the Xcode-coupled toolchain wiring only for - # Swift builds. Empty on Linux (hermetic swift.org toolchain). - BAZEL_SWIFT_CONFIG: ${{ runner.os == 'macOS' && '--config=swift_macos' || '' }} run: | - bazel run $BAZEL_SWIFT_CONFIG :install + bazel run :install - name: Run Swift tests shell: bash - env: - BAZEL_SWIFT_CONFIG: ${{ runner.os == 'macOS' && '--config=swift_macos' || '' }} run: | - bazel test $BAZEL_SWIFT_CONFIG ... --test_tag_filters=-override --test_output=errors + bazel test ... --test_tag_filters=-override --test_output=errors clang-format: runs-on: ubuntu-latest steps: From 26ec22130abdd091fa18b00648b46d2505a0737f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 13 Jul 2026 22:40:00 +0000 Subject: [PATCH 22/33] bazel: auto-enable swift_macos on macOS so Swift builds work zero-config --- .bazelrc | 9 ++++++--- unified/swift-syntax-rs/README.md | 5 ----- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/.bazelrc b/.bazelrc index 55a9b72c3a14..ca1dcadbdc1b 100644 --- a/.bazelrc +++ b/.bazelrc @@ -12,9 +12,11 @@ common --override_module=semmle_code=%workspace%/misc/bazel/semmle_code_stub build --repo_env=CC=clang --repo_env=CXX=clang++ -# macOS Xcode-coupled flags, scoped to Swift builds only so that non-Swift -# builds (C/C++/Rust/...) work with just the Command Line Tools. Enable -# with `--config=swift_macos` (see `unified/swift-syntax-rs/README.md`). +# macOS Xcode-coupled flags, grouped under `--config=swift_macos` so they're +# easy to see, disable, or omit for non-Xcode setups. Auto-enabled for +# macOS below via `common:macos --config=swift_macos` because `rules_swift` +# auto-registers `xcode_swift_toolchain` on macOS, and its analysis fails +# without these flags whenever a Swift target is in the graph. # # - `--xcode_version_config` selects `apple_support`'s xcode_config, whose # version strings match the ones `rules_swift`'s `system_sdk` selects are @@ -26,6 +28,7 @@ build --repo_env=CC=clang --repo_env=CXX=clang++ # toolchains; only matches Apple platforms. build:swift_macos --xcode_version_config=@local_config_xcode//:host_xcodes build:swift_macos --extra_toolchains=@local_config_apple_cc_toolchains//:all +common:macos --config=swift_macos # Disable Android SDK auto-detection (we don't use it, and rules_android has Bazel 9 compatibility issues) build --repo_env=ANDROID_HOME= diff --git a/unified/swift-syntax-rs/README.md b/unified/swift-syntax-rs/README.md index 97d2c7a9e634..60cf9809d8e1 100644 --- a/unified/swift-syntax-rs/README.md +++ b/unified/swift-syntax-rs/README.md @@ -139,11 +139,6 @@ bazel test //unified/swift-syntax-rs:swift_syntax_rs_test bazel run //unified/swift-syntax-rs:swift-syntax-parse < some.swift ``` -On **macOS**, add `--config=swift_macos` to any of the above so that -`rules_swift`'s `xcode_swift_toolchain` gets the Xcode-coupled CC toolchain -and xcode_config it needs. The flag is opt-in so that non-Swift builds on -macOS don't require a full Xcode.app install. - Requirements: - **`clang`** must be installed on the runner. `rules_swift` requires the Bazel From 720f41af9f949c21c909058722b2107ed401cad1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 14 Jul 2026 06:46:11 +0000 Subject: [PATCH 23/33] bazel: scope Xcode flags to //unified/swift-syntax-rs via transition --- .bazelrc | 13 +- unified/swift-syntax-rs/BUILD.bazel | 15 +- unified/swift-syntax-rs/README.md | 8 +- unified/swift-syntax-rs/xcode_transition.bzl | 146 +++++++++++++++++++ 4 files changed, 172 insertions(+), 10 deletions(-) create mode 100644 unified/swift-syntax-rs/xcode_transition.bzl diff --git a/.bazelrc b/.bazelrc index ca1dcadbdc1b..3d439dc6979b 100644 --- a/.bazelrc +++ b/.bazelrc @@ -12,11 +12,13 @@ common --override_module=semmle_code=%workspace%/misc/bazel/semmle_code_stub build --repo_env=CC=clang --repo_env=CXX=clang++ -# macOS Xcode-coupled flags, grouped under `--config=swift_macos` so they're -# easy to see, disable, or omit for non-Xcode setups. Auto-enabled for -# macOS below via `common:macos --config=swift_macos` because `rules_swift` -# auto-registers `xcode_swift_toolchain` on macOS, and its analysis fails -# without these flags whenever a Swift target is in the graph. +# macOS Xcode-coupled flags, kept as an opt-in `--config=swift_macos` for +# manual debugging. The equivalent flags are applied automatically — but +# only to `//unified/swift-syntax-rs` targets — via an incoming-edge +# Starlark transition (see `unified/swift-syntax-rs/xcode_transition.bzl`), +# so other macOS builds continue to use Bazel's built-in `local_config_cc` +# and never materialize the `@local_config_xcode` / +# `@local_config_apple_cc_toolchains` repos. # # - `--xcode_version_config` selects `apple_support`'s xcode_config, whose # version strings match the ones `rules_swift`'s `system_sdk` selects are @@ -28,7 +30,6 @@ build --repo_env=CC=clang --repo_env=CXX=clang++ # toolchains; only matches Apple platforms. build:swift_macos --xcode_version_config=@local_config_xcode//:host_xcodes build:swift_macos --extra_toolchains=@local_config_apple_cc_toolchains//:all -common:macos --config=swift_macos # Disable Android SDK auto-detection (we don't use it, and rules_android has Bazel 9 compatibility issues) build --repo_env=ANDROID_HOME= diff --git a/unified/swift-syntax-rs/BUILD.bazel b/unified/swift-syntax-rs/BUILD.bazel index ce4e35a749ca..7533a20fcfed 100644 --- a/unified/swift-syntax-rs/BUILD.bazel +++ b/unified/swift-syntax-rs/BUILD.bazel @@ -1,5 +1,6 @@ -load("@rules_rust//rust:defs.bzl", "rust_binary", "rust_library", "rust_test") +load("@rules_rust//rust:defs.bzl", "rust_library") load("@rules_swift//swift:swift.bzl", "swift_library") +load(":xcode_transition.bzl", "xcode_transition_rust_binary", "xcode_transition_rust_test") package(default_visibility = ["//visibility:public"]) @@ -18,10 +19,17 @@ _SWIFT_SUPPORTED_PLATFORMS = select({ # Swift FFI shim: wraps swift-syntax and exposes a small C ABI. The Rust # targets below link against its `CcInfo`. +# +# Tagged `manual` so it is only built through the `xcode_transition_*` +# wrappers below (which apply the Xcode-config transition on macOS). +# Building it directly on macOS via `bazel build //...` would try to +# analyze `xcode_swift_toolchain` under Bazel's default CC toolchain and +# fail; going via a wrapper avoids that. swift_library( name = "swift_syntax_ffi", srcs = ["swift/Sources/SwiftSyntaxFFI/SwiftSyntaxFFI.swift"], module_name = "SwiftSyntaxFFI", + tags = ["manual"], target_compatible_with = _SWIFT_SUPPORTED_PLATFORMS, deps = [ "@swift-syntax//:SwiftParser", @@ -38,13 +46,14 @@ rust_library( exclude = ["src/main.rs"], ), edition = "2024", + tags = ["manual"], target_compatible_with = _SWIFT_SUPPORTED_PLATFORMS, deps = [ ":swift_syntax_ffi", ], ) -rust_binary( +xcode_transition_rust_binary( name = "swift-syntax-parse", srcs = ["src/main.rs"], # `rust_binary` doesn't copy the Swift runtime into runfiles the way @@ -60,7 +69,7 @@ rust_binary( deps = [":swift_syntax_rs"], ) -rust_test( +xcode_transition_rust_test( name = "swift_syntax_rs_test", size = "small", crate = ":swift_syntax_rs", diff --git a/unified/swift-syntax-rs/README.md b/unified/swift-syntax-rs/README.md index 60cf9809d8e1..610ff8b296b5 100644 --- a/unified/swift-syntax-rs/README.md +++ b/unified/swift-syntax-rs/README.md @@ -149,7 +149,13 @@ Requirements: matching the host. Targets are marked `target_compatible_with` these two OSes, so on Windows Bazel skips them cleanly. - **macOS only:** the macOS toolchain is a `.pkg` archive that can only be - fetched and extracted on a macOS host. + fetched and extracted on a macOS host. On macOS, `rules_swift` also needs + Xcode's CC toolchain and xcode_config; the targets above apply the + necessary `--xcode_version_config` and `--extra_toolchains` flags via an + incoming-edge Starlark transition + ([`xcode_transition.bzl`](xcode_transition.bzl)), so no manual `--config=…` + is needed and other targets on macOS keep using Bazel's default CC + toolchain unchanged. The Swift compiler version is read from [`.swift-version`](.swift-version) by both the Bazel toolchain (`swift.toolchain(swift_version_file = …)`) and the diff --git a/unified/swift-syntax-rs/xcode_transition.bzl b/unified/swift-syntax-rs/xcode_transition.bzl new file mode 100644 index 000000000000..9164ffefdea3 --- /dev/null +++ b/unified/swift-syntax-rs/xcode_transition.bzl @@ -0,0 +1,146 @@ +"""Per-target Xcode configuration for `//unified/swift-syntax-rs` on macOS. + +On macOS, `rules_swift` auto-registers `xcode_swift_toolchain`, whose +analysis reads `cc_toolchain.target_gnu_system_name` and fails on Bazel's +built-in `local_config_cc` (triple is literally "local"). Working around +this needs two flags: + +- `--xcode_version_config=@local_config_xcode//:host_xcodes` — selects + `apple_support`'s xcode_config, whose version strings match the ones + `rules_swift`'s `system_sdk` selects are keyed on. +- `--extra_toolchains=@local_config_apple_cc_toolchains//:all` — forces + `apple_support`'s CC toolchain ahead of `local_config_cc`. + +We apply them via an incoming-edge Starlark transition on the public +`//unified/swift-syntax-rs` binary/test wrappers rather than globally in +`.bazelrc`. That keeps every other target on macOS on Bazel's default CC +toolchain (`local_config_cc`) and avoids materializing the +`@local_config_xcode` / `@local_config_apple_cc_toolchains` repos unless +something under `//unified/swift-syntax-rs` is actually being built. + +The transition is a no-op on non-macOS platforms. +""" + +load("@rules_rust//rust:defs.bzl", "rust_binary", "rust_test") +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 input values so the transitioned configuration is + # identical to the incoming one (no reconfiguration penalty). + 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): + # `ctx.attr.actual` is a list because of the incoming transition. + src = ctx.attr.actual[0] + src_default = src[DefaultInfo] + src_exe = src_default.files_to_run.executable + src_runfiles = src_default.default_runfiles + + # Copy (not symlink) the executable so that runfiles lookups via + # `argv[0]` resolve under this wrapper's runfiles tree. This mirrors + # the pattern used by `//swift:rules.bzl` `_cc_transition_impl`. + out = ctx.actions.declare_file(ctx.label.name) + ctx.actions.run_shell( + inputs = [src_exe], + outputs = [out], + command = "cp {src} {dst}".format(src = src_exe.path, dst = out.path), + ) + + # Rewrite runfiles so the wrapped executable is replaced by the copy. + files = src_runfiles.files.to_list() + if src_exe in files: + files.remove(src_exe) + files.append(out) + runfiles = ctx.runfiles(files = files) + + return [DefaultInfo( + executable = out, + files = depset([out]), + runfiles = runfiles, + )] + +_xcode_transition_binary_rule = rule( + implementation = _wrapper_impl, + attrs = { + "actual": attr.label( + mandatory = True, + cfg = _xcode_transition, + executable = True, + ), + "os": attr.string(mandatory = True), + "_allowlist_function_transition": attr.label( + default = "@bazel_tools//tools/allowlists/function_transition_allowlist", + ), + }, + executable = True, +) + +_xcode_transition_test_rule = rule( + implementation = _wrapper_impl, + attrs = { + "actual": attr.label( + mandatory = True, + cfg = _xcode_transition, + executable = True, + ), + "os": attr.string(mandatory = True), + "_allowlist_function_transition": attr.label( + default = "@bazel_tools//tools/allowlists/function_transition_allowlist", + ), + }, + test = True, +) + +def _wrap(inner_rule, wrapper_rule, name, visibility = None, tags = None, target_compatible_with = None, **kwargs): + """Wrap a Rust target with the Xcode-config transition. + + Splits `name` into a private inner target `_impl_` (built with + `inner_rule` and hidden from wildcards via `tags = ["manual"]`) and a + public `name` (built with `wrapper_rule`, which applies the Xcode + transition on macOS). The `_impl_` prefix — rather than an `_impl` + suffix — preserves the caller's original suffix, which Bazel requires + for test rules (target names must end in `_test`).""" + inner_name = "_impl_%s" % name + inner_rule( + name = inner_name, + visibility = ["//visibility:private"], + tags = (tags or []) + ["manual"], + target_compatible_with = target_compatible_with, + **kwargs + ) + wrapper_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"), + ) + +def xcode_transition_rust_binary(name, **kwargs): + """`rust_binary` wrapped in the macOS Xcode-config transition.""" + _wrap(rust_binary, _xcode_transition_binary_rule, name = name, **kwargs) + +def xcode_transition_rust_test(name, **kwargs): + """`rust_test` wrapped in the macOS Xcode-config transition.""" + _wrap(rust_test, _xcode_transition_test_rule, name = name, **kwargs) From a7dcd565d1cf8870ab1a3e1b2f3ec6e29c220016 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 14 Jul 2026 06:47:37 +0000 Subject: [PATCH 24/33] MODULE.bazel: update apple_support/local_config_* comments per review --- MODULE.bazel | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/MODULE.bazel b/MODULE.bazel index 86a59a3eae7c..7e94eb3045fe 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -33,12 +33,14 @@ 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") + # Direct dep on `apple_support` (transitively pulled in by `rules_swift`) so we # can `use_repo` `local_config_xcode` and `local_config_apple_cc_toolchains` -# below, needed by the `swift_macos` bazelrc config that makes -# `xcode_swift_toolchain` work on macOS. See `.bazelrc`. +# below. These repos are referenced by the per-target Xcode-config transition +# in `unified/swift-syntax-rs/xcode_transition.bzl` (primary mechanism) and by +# the opt-in `swift_macos` bazelrc config (manual escape hatch). See +# `.bazelrc`. 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) @@ -248,18 +250,20 @@ register_toolchains( "@swift_toolchain//:swift_toolchain_exec_ubuntu24.04", ) -# `apple_support`'s xcode_config, selected via `--xcode_version_config` in -# the `swift_macos` bazelrc config (see `.bazelrc`). Needed because -# rules_swift's `system_sdk` selects use Xcode version strings from -# `apple_support`'s `xcode_locator.m`, which Bazel's built-in `host_xcodes` -# does not match. +# `apple_support`'s xcode_config, selected via `--xcode_version_config` by +# the per-target Xcode-config transition in +# `unified/swift-syntax-rs/xcode_transition.bzl` (and by the opt-in +# `swift_macos` bazelrc config; see `.bazelrc`). Needed because rules_swift's +# `system_sdk` selects use Xcode version strings from `apple_support`'s +# `xcode_locator.m`, which Bazel's built-in `host_xcodes` does not match. xcode_configure = use_extension("@apple_support//xcode:xcode_configure.bzl", "xcode_configure_extension") use_repo(xcode_configure, "local_config_xcode") # `apple_support`'s CC toolchains, forced ahead of Bazel's built-in -# `local_config_cc` via `--extra_toolchains` in the `swift_macos` bazelrc -# config (see `.bazelrc`). Must be visible from the root module for the flag -# to resolve. +# `local_config_cc` via `--extra_toolchains` by the per-target Xcode-config +# transition in `unified/swift-syntax-rs/xcode_transition.bzl` (and by the +# opt-in `swift_macos` bazelrc config; see `.bazelrc`). Must be visible from +# the root module for those references to resolve. apple_cc_configure = use_extension("@apple_support//crosstool:setup.bzl", "apple_cc_configure_extension") use_repo(apple_cc_configure, "local_config_apple_cc_toolchains") From 5bee639c022bda178dfa71d86f75719d5e23d6da Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 14 Jul 2026 12:51:23 +0000 Subject: [PATCH 25/33] bazel: apply Xcode transition to swift_library only, drop Rust wrappers --- unified/swift-syntax-rs/BUILD.bazel | 25 ++-- unified/swift-syntax-rs/xcode_transition.bzl | 121 +++++++------------ 2 files changed, 57 insertions(+), 89 deletions(-) diff --git a/unified/swift-syntax-rs/BUILD.bazel b/unified/swift-syntax-rs/BUILD.bazel index 7533a20fcfed..12a7c74edbe8 100644 --- a/unified/swift-syntax-rs/BUILD.bazel +++ b/unified/swift-syntax-rs/BUILD.bazel @@ -1,6 +1,5 @@ -load("@rules_rust//rust:defs.bzl", "rust_library") -load("@rules_swift//swift:swift.bzl", "swift_library") -load(":xcode_transition.bzl", "xcode_transition_rust_binary", "xcode_transition_rust_test") +load("@rules_rust//rust:defs.bzl", "rust_binary", "rust_library", "rust_test") +load(":xcode_transition.bzl", "xcode_transition_swift_library") package(default_visibility = ["//visibility:public"]) @@ -20,16 +19,17 @@ _SWIFT_SUPPORTED_PLATFORMS = select({ # Swift FFI shim: wraps swift-syntax and exposes a small C ABI. The Rust # targets below link against its `CcInfo`. # -# Tagged `manual` so it is only built through the `xcode_transition_*` -# wrappers below (which apply the Xcode-config transition on macOS). -# Building it directly on macOS via `bazel build //...` would try to -# analyze `xcode_swift_toolchain` under Bazel's default CC toolchain and -# fail; going via a wrapper avoids that. -swift_library( +# On macOS, `rules_swift` auto-registers `xcode_swift_toolchain`, whose +# analysis fails against Bazel's default `local_config_cc`. To avoid +# forcing every macOS target in the repo onto the Apple CC toolchain, the +# Xcode-config flags are applied only along the incoming edge into this +# `swift_library` via `xcode_transition_swift_library` (see +# `xcode_transition.bzl`). Downstream Rust targets stay in the default +# configuration. +xcode_transition_swift_library( name = "swift_syntax_ffi", srcs = ["swift/Sources/SwiftSyntaxFFI/SwiftSyntaxFFI.swift"], module_name = "SwiftSyntaxFFI", - tags = ["manual"], target_compatible_with = _SWIFT_SUPPORTED_PLATFORMS, deps = [ "@swift-syntax//:SwiftParser", @@ -46,14 +46,13 @@ rust_library( exclude = ["src/main.rs"], ), edition = "2024", - tags = ["manual"], target_compatible_with = _SWIFT_SUPPORTED_PLATFORMS, deps = [ ":swift_syntax_ffi", ], ) -xcode_transition_rust_binary( +rust_binary( name = "swift-syntax-parse", srcs = ["src/main.rs"], # `rust_binary` doesn't copy the Swift runtime into runfiles the way @@ -69,7 +68,7 @@ xcode_transition_rust_binary( deps = [":swift_syntax_rs"], ) -xcode_transition_rust_test( +rust_test( name = "swift_syntax_rs_test", size = "small", crate = ":swift_syntax_rs", diff --git a/unified/swift-syntax-rs/xcode_transition.bzl b/unified/swift-syntax-rs/xcode_transition.bzl index 9164ffefdea3..dd5d492ad43a 100644 --- a/unified/swift-syntax-rs/xcode_transition.bzl +++ b/unified/swift-syntax-rs/xcode_transition.bzl @@ -1,9 +1,10 @@ -"""Per-target Xcode configuration for `//unified/swift-syntax-rs` on macOS. +"""Per-target Xcode configuration for the `swift_library` under +`//unified/swift-syntax-rs` on macOS. On macOS, `rules_swift` auto-registers `xcode_swift_toolchain`, whose analysis reads `cc_toolchain.target_gnu_system_name` and fails on Bazel's -built-in `local_config_cc` (triple is literally "local"). Working around -this needs two flags: +built-in `local_config_cc` (whose triple is literally "local"). Working +around this needs two flags: - `--xcode_version_config=@local_config_xcode//:host_xcodes` — selects `apple_support`'s xcode_config, whose version strings match the ones @@ -11,17 +12,24 @@ this needs two flags: - `--extra_toolchains=@local_config_apple_cc_toolchains//:all` — forces `apple_support`'s CC toolchain ahead of `local_config_cc`. -We apply them via an incoming-edge Starlark transition on the public -`//unified/swift-syntax-rs` binary/test wrappers rather than globally in -`.bazelrc`. That keeps every other target on macOS on Bazel's default CC -toolchain (`local_config_cc`) and avoids materializing the -`@local_config_xcode` / `@local_config_apple_cc_toolchains` repos unless -something under `//unified/swift-syntax-rs` is actually being built. +We apply them via an incoming-edge Starlark transition on the +`swift_library` target itself (through the `xcode_transition_swift_library` +macro below), rather than globally in `.bazelrc`. That keeps every other +target on macOS on Bazel's default CC toolchain (`local_config_cc`) and +avoids materializing the `@local_config_xcode` / +`@local_config_apple_cc_toolchains` repos unless something under +`//unified/swift-syntax-rs` is actually being built. + +The transition is placed on `swift_library` — not on the downstream Rust +targets — so the Apple CC toolchain is only used for analyzing the Swift +side. The Rust compilation and any other transitive C/C++ deps stay on +Bazel's default CC toolchain, matching how they build elsewhere in the +repository. The transition is a no-op on non-macOS platforms. """ -load("@rules_rust//rust:defs.bzl", "rust_binary", "rust_test") +load("@rules_swift//swift:swift.bzl", "swift_library") load("//misc/bazel:os.bzl", "os_select") _XCODE_VERSION_CONFIG = "//command_line_option:xcode_version_config" @@ -50,85 +58,54 @@ _xcode_transition = transition( ) def _wrapper_impl(ctx): - # `ctx.attr.actual` is a list because of the incoming transition. src = ctx.attr.actual[0] - src_default = src[DefaultInfo] - src_exe = src_default.files_to_run.executable - src_runfiles = src_default.default_runfiles - - # Copy (not symlink) the executable so that runfiles lookups via - # `argv[0]` resolve under this wrapper's runfiles tree. This mirrors - # the pattern used by `//swift:rules.bzl` `_cc_transition_impl`. - out = ctx.actions.declare_file(ctx.label.name) - ctx.actions.run_shell( - inputs = [src_exe], - outputs = [out], - command = "cp {src} {dst}".format(src = src_exe.path, dst = out.path), - ) - - # Rewrite runfiles so the wrapped executable is replaced by the copy. - files = src_runfiles.files.to_list() - if src_exe in files: - files.remove(src_exe) - files.append(out) - runfiles = ctx.runfiles(files = files) - - return [DefaultInfo( - executable = out, - files = depset([out]), - runfiles = runfiles, - )] - -_xcode_transition_binary_rule = rule( - implementation = _wrapper_impl, - attrs = { - "actual": attr.label( - mandatory = True, - cfg = _xcode_transition, - executable = True, - ), - "os": attr.string(mandatory = True), - "_allowlist_function_transition": attr.label( - default = "@bazel_tools//tools/allowlists/function_transition_allowlist", - ), - }, - executable = True, -) - -_xcode_transition_test_rule = rule( + providers = [ + # Only forward the providers a downstream `rust_*` target reads + # from a `deps` entry. `CcInfo` carries the linking info; forward + # `OutputGroupInfo` too for `bazel build --output_groups=...`. + 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, - executable = True, + providers = [CcInfo], ), "os": attr.string(mandatory = True), "_allowlist_function_transition": attr.label( default = "@bazel_tools//tools/allowlists/function_transition_allowlist", ), }, - test = True, ) -def _wrap(inner_rule, wrapper_rule, name, visibility = None, tags = None, target_compatible_with = None, **kwargs): - """Wrap a Rust target with the Xcode-config transition. - - Splits `name` into a private inner target `_impl_` (built with - `inner_rule` and hidden from wildcards via `tags = ["manual"]`) and a - public `name` (built with `wrapper_rule`, which applies the Xcode - transition on macOS). The `_impl_` prefix — rather than an `_impl` - suffix — preserves the caller's original suffix, which Bazel requires - for test rules (target names must end in `_test`).""" +def xcode_transition_swift_library(name, visibility = None, tags = None, target_compatible_with = None, **kwargs): + """`swift_library` wrapped in the macOS Xcode-config transition. + + Splits `name` into a private inner target `_impl_` (a plain + `swift_library`, hidden from wildcards via `tags = ["manual"]`) and a + public `name` that applies the incoming Xcode-config transition on + macOS and forwards the inner target's `CcInfo` (and other relevant + providers) to downstream consumers. Downstream `rust_*` targets can + depend on `name` as an ordinary `deps` entry — they stay in the + default configuration; only the `swift_library` sub-graph flips to + the Apple CC toolchain, and only on macOS. + """ inner_name = "_impl_%s" % name - inner_rule( + swift_library( name = inner_name, visibility = ["//visibility:private"], tags = (tags or []) + ["manual"], target_compatible_with = target_compatible_with, **kwargs ) - wrapper_rule( + _xcode_transition_swift_library_rule( name = name, visibility = visibility, tags = tags, @@ -136,11 +113,3 @@ def _wrap(inner_rule, wrapper_rule, name, visibility = None, tags = None, target actual = ":" + inner_name, os = os_select(linux = "linux", macos = "macos", default = "other"), ) - -def xcode_transition_rust_binary(name, **kwargs): - """`rust_binary` wrapped in the macOS Xcode-config transition.""" - _wrap(rust_binary, _xcode_transition_binary_rule, name = name, **kwargs) - -def xcode_transition_rust_test(name, **kwargs): - """`rust_test` wrapped in the macOS Xcode-config transition.""" - _wrap(rust_test, _xcode_transition_test_rule, name = name, **kwargs) From f604997c04da8abc1ee95f35b9f089757f14020c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 14 Jul 2026 13:07:53 +0000 Subject: [PATCH 26/33] bazel: load CcInfo from @rules_cc for Bazel 9 compatibility --- unified/swift-syntax-rs/xcode_transition.bzl | 1 + 1 file changed, 1 insertion(+) diff --git a/unified/swift-syntax-rs/xcode_transition.bzl b/unified/swift-syntax-rs/xcode_transition.bzl index dd5d492ad43a..55ce03c9902d 100644 --- a/unified/swift-syntax-rs/xcode_transition.bzl +++ b/unified/swift-syntax-rs/xcode_transition.bzl @@ -29,6 +29,7 @@ repository. The transition is a no-op on non-macOS platforms. """ +load("@rules_cc//cc/common:cc_info.bzl", "CcInfo") load("@rules_swift//swift:swift.bzl", "swift_library") load("//misc/bazel:os.bzl", "os_select") From 240267e644ee743fd3991adf3602c3fef1b04d49 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 14 Jul 2026 13:28:39 +0000 Subject: [PATCH 27/33] swift-syntax-rs: declare macOS 10.15 platform for cargo build --- unified/swift-syntax-rs/swift/Package.swift | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/unified/swift-syntax-rs/swift/Package.swift b/unified/swift-syntax-rs/swift/Package.swift index 1b0111364ce7..fcd9fcc07bca 100644 --- a/unified/swift-syntax-rs/swift/Package.swift +++ b/unified/swift-syntax-rs/swift/Package.swift @@ -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. From 284dcb35a657b633e11786a508108e075916ef19 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 14 Jul 2026 13:34:17 +0000 Subject: [PATCH 28/33] Remove opt-in swift_macos config from .bazelrc --- .bazelrc | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/.bazelrc b/.bazelrc index 3d439dc6979b..450c1e5cc7bd 100644 --- a/.bazelrc +++ b/.bazelrc @@ -12,25 +12,6 @@ common --override_module=semmle_code=%workspace%/misc/bazel/semmle_code_stub build --repo_env=CC=clang --repo_env=CXX=clang++ -# macOS Xcode-coupled flags, kept as an opt-in `--config=swift_macos` for -# manual debugging. The equivalent flags are applied automatically — but -# only to `//unified/swift-syntax-rs` targets — via an incoming-edge -# Starlark transition (see `unified/swift-syntax-rs/xcode_transition.bzl`), -# so other macOS builds continue to use Bazel's built-in `local_config_cc` -# and never materialize the `@local_config_xcode` / -# `@local_config_apple_cc_toolchains` repos. -# -# - `--xcode_version_config` selects `apple_support`'s xcode_config, whose -# version strings match the ones `rules_swift`'s `system_sdk` selects are -# keyed on (Bazel's built-in `host_xcodes` does not match). -# - `--extra_toolchains` forces `apple_support`'s CC toolchain ahead of -# Bazel's built-in `local_config_cc` (whose `target_gnu_system_name` is -# literally "local", which breaks `xcode_swift_toolchain`'s triple -# parsing). Root-level `--extra_toolchains` beats module-registered -# toolchains; only matches Apple platforms. -build:swift_macos --xcode_version_config=@local_config_xcode//:host_xcodes -build:swift_macos --extra_toolchains=@local_config_apple_cc_toolchains//:all - # Disable Android SDK auto-detection (we don't use it, and rules_android has Bazel 9 compatibility issues) build --repo_env=ANDROID_HOME= From f8a9b0d35b859a53572cbce15d64621da5315488 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 14 Jul 2026 13:46:07 +0000 Subject: [PATCH 29/33] clean up added comments and revert cleanup edits to adapter.rs/swift.rs/SwiftSyntaxFFI.swift --- .bazelrc | 1 - MODULE.bazel | 31 +++---- .../extractor/src/languages/swift/adapter.rs | 85 ++++++++++++------- .../extractor/src/languages/swift/swift.rs | 19 +++-- unified/swift-syntax-rs/README.md | 10 +-- .../SwiftSyntaxFFI/SwiftSyntaxFFI.swift | 66 +++++++++----- unified/swift-syntax-rs/xcode_transition.bzl | 56 ++++-------- 7 files changed, 141 insertions(+), 127 deletions(-) diff --git a/.bazelrc b/.bazelrc index 450c1e5cc7bd..8687f4406cb9 100644 --- a/.bazelrc +++ b/.bazelrc @@ -11,7 +11,6 @@ build --compilation_mode opt common --override_module=semmle_code=%workspace%/misc/bazel/semmle_code_stub build --repo_env=CC=clang --repo_env=CXX=clang++ - # Disable Android SDK auto-detection (we don't use it, and rules_android has Bazel 9 compatibility issues) build --repo_env=ANDROID_HOME= diff --git a/MODULE.bazel b/MODULE.bazel index 7e94eb3045fe..1e4b325319b0 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -34,12 +34,9 @@ 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") -# Direct dep on `apple_support` (transitively pulled in by `rules_swift`) so we -# can `use_repo` `local_config_xcode` and `local_config_apple_cc_toolchains` -# below. These repos are referenced by the per-target Xcode-config transition -# in `unified/swift-syntax-rs/xcode_transition.bzl` (primary mechanism) and by -# the opt-in `swift_macos` bazelrc config (manual escape hatch). See -# `.bazelrc`. +# 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") @@ -230,11 +227,9 @@ use_repo( ) # Hermetic Swift toolchain (swift.org) for building `unified/swift-syntax-rs`. -# Only registered as an `exec` toolchain on Linux; on macOS we rely on -# `rules_swift`'s auto-registered `xcode_swift_toolchain`, which uses the -# host Xcode and links against the OS-provided Swift runtime in `/usr/lib/swift`. -# Version is read from `unified/swift-syntax-rs/.swift-version`, shared with -# the local `cargo` build and `swift/Package.swift`. +# Only registered as an exec toolchain on Linux; on macOS `rules_swift` auto- +# registers `xcode_swift_toolchain` (host Xcode + OS-provided Swift runtime). +# Version comes from `unified/swift-syntax-rs/.swift-version`. swift = use_extension("@rules_swift//swift:extensions.bzl", "swift") swift.toolchain( name = "swift_toolchain", @@ -250,20 +245,16 @@ register_toolchains( "@swift_toolchain//:swift_toolchain_exec_ubuntu24.04", ) -# `apple_support`'s xcode_config, selected via `--xcode_version_config` by -# the per-target Xcode-config transition in -# `unified/swift-syntax-rs/xcode_transition.bzl` (and by the opt-in -# `swift_macos` bazelrc config; see `.bazelrc`). Needed because rules_swift's -# `system_sdk` selects use Xcode version strings from `apple_support`'s -# `xcode_locator.m`, which Bazel's built-in `host_xcodes` does not match. +# `apple_support`'s xcode_config, selected via `--xcode_version_config` by the +# per-target Xcode-config transition in `unified/swift-syntax-rs/xcode_transition.bzl`. +# Needed because `rules_swift`'s `system_sdk` selects on version strings from +# `apple_support`'s `xcode_locator.m`, which Bazel's built-in `host_xcodes` does not match. xcode_configure = use_extension("@apple_support//xcode:xcode_configure.bzl", "xcode_configure_extension") use_repo(xcode_configure, "local_config_xcode") # `apple_support`'s CC toolchains, forced ahead of Bazel's built-in # `local_config_cc` via `--extra_toolchains` by the per-target Xcode-config -# transition in `unified/swift-syntax-rs/xcode_transition.bzl` (and by the -# opt-in `swift_macos` bazelrc config; see `.bazelrc`). Must be visible from -# the root module for those references to resolve. +# transition in `unified/swift-syntax-rs/xcode_transition.bzl`. apple_cc_configure = use_extension("@apple_support//crosstool:setup.bzl", "apple_cc_configure_extension") use_repo(apple_cc_configure, "local_config_apple_cc_toolchains") diff --git a/unified/extractor/src/languages/swift/adapter.rs b/unified/extractor/src/languages/swift/adapter.rs index c28e14cf2f0d..37d5aac00d2d 100644 --- a/unified/extractor/src/languages/swift/adapter.rs +++ b/unified/extractor/src/languages/swift/adapter.rs @@ -1,19 +1,26 @@ -//! Converts the swift-syntax JSON syntax tree into a [`yeast::Ast`]. +//! Converts the swift-syntax JSON syntax tree into a [`yeast::Ast`], the +//! in-memory format the CodeQL desugaring rules operate on. //! -//! The JSON is produced out-of-process by `swift-syntax-rs`'s FFI shim, so -//! this module is pure Rust (no Swift toolchain needed). +//! The JSON tree is produced by the `swift-syntax-rs` crate's Swift FFI shim +//! (`parse_to_json`). This module is pure Rust (only `yeast` + `serde_json`), +//! so the extractor consumes swift-syntax output without pulling in the Swift +//! toolchain (the JSON is produced out-of-process). //! -//! The mapping mirrors tree-sitter's node model: +//! The mapping mirrors tree-sitter's node model, which is what yeast (and the +//! extractor's rewrite rules) expect: //! -//! * **Layout nodes** (e.g. `functionDecl`) and **varying tokens** -//! (identifiers, literals, operators — text not determined by kind) become -//! **named** nodes, keyed by their kind name. -//! * **Fixed tokens** (keywords, punctuation) become **anonymous** nodes, -//! keyed by their text (like tree-sitter's anonymous tokens). -//! * Collection nodes are already elided to JSON arrays upstream. +//! * **Layout nodes** (e.g. `functionDecl`) and **varying tokens** (identifiers, +//! literals, operators — the ones whose text is not determined by their kind) +//! become **named** nodes, keyed by their kind name. +//! * **Fixed tokens** (keywords and punctuation, whose text is fully determined +//! by their kind) become **anonymous** nodes, keyed by their text — exactly +//! how tree-sitter models anonymous tokens (e.g. `"func"`, `"->"`). +//! * Collection nodes are already elided to JSON arrays upstream, so a +//! list-valued field maps directly to that field holding several children. //! -//! Kind/field names are preserved from swift-syntax; aligning them with the -//! tree-sitter-swift schema is done incrementally in the rewrite rules. +//! Note: this preserves swift-syntax's own kind/field names. Aligning those +//! names with the tree-sitter-swift schema (so the rewrite rules in +//! [`super::swift`] fire) is done incrementally in the rules. use std::collections::BTreeMap; @@ -22,29 +29,32 @@ use yeast::schema::Schema; use yeast::{Ast, Id, NodeContent, Point, Range}; /// A comment (or `unexpectedText`) recovered from the syntax tree's trivia. -/// Collected into a side channel rather than embedded in the [`yeast::Ast`], -/// mirroring how the extractor treats tree-sitter `extra` nodes. +/// +/// These are collected into a side channel rather than embedded in the +/// [`yeast::Ast`], mirroring how the extractor treats tree-sitter `extra` +/// nodes: they carry a location and text but are not attached to a parent. #[derive(Debug, Clone, PartialEq, Eq)] pub struct TriviaToken { - /// The trivia kind (e.g. `lineComment`, `docLineComment`, `unexpectedText`). + /// The trivia kind (e.g. `lineComment`, `blockComment`, `docLineComment`, + /// `docBlockComment`, `unexpectedText`). pub kind: String, - /// The verbatim source text (e.g. `// comment`). + /// The verbatim source text of the piece (e.g. `// comment`). pub text: String, /// The source range the piece occupies. pub range: Range, } -/// The result of adapting a swift-syntax JSON tree: the [`yeast::Ast`] plus -/// the comment/`unexpectedText` trivia harvested from it (in source order). +/// The result of adapting a swift-syntax JSON tree: the [`yeast::Ast`] plus the +/// comment/`unexpectedText` trivia harvested from it (in source order). pub struct AdaptedTree { pub ast: Ast, pub trivia: Vec, } /// swift-syntax `TokenKind` cases whose text is *not* determined by the kind -/// (i.e. `TokenKind.defaultText == nil`). Modelled as named leaf nodes; every -/// other token is a fixed keyword/punctuation, modelled as an anonymous token -/// keyed by its text. +/// (i.e. `TokenKind.defaultText == nil`). These carry varying information and +/// are modelled as named leaf nodes; every other token is a fixed +/// keyword/punctuation token modelled as an anonymous token keyed by its text. const VARYING_TOKEN_KINDS: &[&str] = &[ "identifier", "integerLiteral", @@ -106,7 +116,7 @@ fn classify(node: &Value) -> Result { .unwrap_or("") .to_string(); - // Case name is the part before any `(payload)` in the debug rendering. + // The case name is the part before any `(payload)` in the debug rendering. let case_name = token_kind.split('(').next().unwrap_or(token_kind); if VARYING_TOKEN_KINDS.contains(&case_name) { @@ -132,7 +142,8 @@ fn classify(node: &Value) -> Result { } } -/// Iterate over a node's structural (field, value) pairs, skipping metadata. +/// Iterate over a node object's structural (field, value) pairs in a stable +/// order, skipping metadata keys. fn field_entries(node: &Value) -> Vec<(&str, &Value)> { node.as_object() .map(|map| { @@ -144,8 +155,8 @@ fn field_entries(node: &Value) -> Vec<(&str, &Value)> { .unwrap_or_default() } -/// Child nodes held by a field value: either a single node or an elided -/// collection (JSON array). +/// The child node objects held by a field value, which is either a single node +/// object or an array of them (an elided collection). fn children_of(value: &Value) -> Vec<&Value> { match value { Value::Array(items) => items.iter().collect(), @@ -154,8 +165,12 @@ fn children_of(value: &Value) -> Vec<&Value> { } /// Recursively build `node` (and its descendants) into `ast`, returning its id. -/// Kinds and field names are registered in the schema on the fly. Comment / -/// `unexpectedText` trivia is harvested into `trivia` in the same pass. +/// +/// This is a single traversal: each node's kind and field names are registered +/// in the schema on the fly, immediately before the node is created. Children +/// are built first so a parent's field lists reference existing ids. Any +/// comment/`unexpectedText` trivia carried by a token is harvested into +/// `trivia` during the same pass rather than embedded in the tree. fn build(node: &Value, ast: &mut Ast, trivia: &mut Vec) -> Result { let info = classify(node)?; collect_trivia(node, trivia); @@ -185,8 +200,9 @@ fn build(node: &Value, ast: &mut Ast, trivia: &mut Vec) -> Result) { for key in ["leadingTrivia", "trailingTrivia"] { let Some(Value::Array(pieces)) = node.get(key) else { @@ -215,9 +231,11 @@ fn collect_trivia(node: &Value, out: &mut Vec) { /// Parse a node's `range` into a [`yeast::Range`]. /// -/// JSON offsets are 0-based UTF-8 byte offsets; `line`/`column` are 1-based -/// UTF-8. yeast uses byte offsets with 0-based rows/columns (exclusive end), -/// so line/column are shifted down by one. +/// The JSON carries, for `start` and `end`, a 0-based UTF-8 file byte `offset`, +/// a 1-based `line`, and a 1-based UTF-8 byte `column`. yeast (like tree-sitter) +/// uses byte offsets with 0-based rows/columns and an exclusive end, so the +/// line/column are shifted down by one. swift-syntax's end position is already +/// exclusive, so the byte offsets map across directly. fn parse_range(node: &Value) -> Option { let range = node.get("range")?; let point = |key: &str| -> Option<(usize, Point)> { @@ -241,7 +259,8 @@ fn parse_range(node: &Value) -> Option { } /// Convert a swift-syntax JSON tree (as produced by [`crate::parse_to_json`]) -/// into a [`yeast::Ast`] plus harvested trivia, in a single traversal. +/// into a [`yeast::Ast`] plus the comment/`unexpectedText` trivia harvested +/// from it. Both are produced in a single traversal. pub fn json_to_ast(json: &str) -> Result { let root: Value = serde_json::from_str(json).map_err(|e| format!("invalid JSON: {e}"))?; diff --git a/unified/extractor/src/languages/swift/swift.rs b/unified/extractor/src/languages/swift/swift.rs index 640d79e58605..13f1a6beadf0 100644 --- a/unified/extractor/src/languages/swift/swift.rs +++ b/unified/extractor/src/languages/swift/swift.rs @@ -135,12 +135,19 @@ fn translation_rules() -> Vec> { // Declarations may be wrapped in local/global wrapper nodes. rule!((global_declaration _ @inner) => stmt { inner }), rule!((local_declaration _ @inner) => stmt { inner }), - // ---- swift-syntax front-end ---- - // Rules targeting the swift-syntax AST (camelCase kind names, built - // by the sibling `adapter` module). Kind namespaces don't overlap - // with the tree-sitter (snake_case) rules, so these are inert on - // the tree-sitter path. Unmatched nodes fall through to the - // `unsupported_node` fallback below. + // ---- swift-syntax front-end (minimal hook-up) ---- + // These rules target the swift-syntax AST (camelCase kind names), + // produced by the sibling `adapter` module. They coexist with the + // tree-sitter rules (snake_case names): rules are dispatched by exact + // kind name, and the two name spaces never collide, so these are inert + // on the tree-sitter path. Only the minimal top-level mapping lives here + // to demonstrate the pipeline end-to-end; the full translation is added + // separately. Unmatched swift-syntax nodes fall through to the + // `unsupported_node` fallback at the end. + // + // `sourceFile` holds its top-level statements in an (elided) + // `statements` collection; each element is a `codeBlockItem` wrapping + // the real node. rule!( (sourceFile statements: _* @items) => diff --git a/unified/swift-syntax-rs/README.md b/unified/swift-syntax-rs/README.md index 610ff8b296b5..9512d06e2056 100644 --- a/unified/swift-syntax-rs/README.md +++ b/unified/swift-syntax-rs/README.md @@ -150,12 +150,10 @@ Requirements: OSes, so on Windows Bazel skips them cleanly. - **macOS only:** the macOS toolchain is a `.pkg` archive that can only be fetched and extracted on a macOS host. On macOS, `rules_swift` also needs - Xcode's CC toolchain and xcode_config; the targets above apply the - necessary `--xcode_version_config` and `--extra_toolchains` flags via an - incoming-edge Starlark transition - ([`xcode_transition.bzl`](xcode_transition.bzl)), so no manual `--config=…` - is needed and other targets on macOS keep using Bazel's default CC - toolchain unchanged. + 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 diff --git a/unified/swift-syntax-rs/swift/Sources/SwiftSyntaxFFI/SwiftSyntaxFFI.swift b/unified/swift-syntax-rs/swift/Sources/SwiftSyntaxFFI/SwiftSyntaxFFI.swift index 3420ba26b838..6305b1610d06 100644 --- a/unified/swift-syntax-rs/swift/Sources/SwiftSyntaxFFI/SwiftSyntaxFFI.swift +++ b/unified/swift-syntax-rs/swift/Sources/SwiftSyntaxFFI/SwiftSyntaxFFI.swift @@ -1,8 +1,8 @@ import Foundation import SwiftParser -// `@_spi(RawSyntax)` exposes `childName(_:)`, which maps a child's key path -// to its field name in the parent layout. +// `@_spi(RawSyntax)` exposes the `childName(_:)` helper that maps a child's +// key path to its field name in the parent layout; used to emit named fields. @_spi(RawSyntax) import SwiftSyntax #if canImport(Glibc) @@ -11,7 +11,8 @@ import SwiftParser import Darwin #endif -/// Convert an absolute position into `{ offset, line, column }`. +/// Convert an absolute position into an `{ offset, line, column }` dictionary. +/// /// `offset` is a UTF-8 byte offset; `line`/`column` are 1-based. private func location( _ position: AbsolutePosition, @@ -25,9 +26,11 @@ private func location( ] } -/// Trivia kinds worth preserving: comments (including doc comments) and -/// `unexpectedText` (source the parser skipped). Whitespace is dropped since -/// node ranges already encode positions. +/// Trivia kinds worth preserving in the serialized tree. Comments carry +/// developer intent (including doc comments), and `unexpectedText` flags source +/// the parser had to skip. Whitespace and multi-line-string escape markers are +/// dropped: node ranges already encode positions, so they would only bloat the +/// output. private let keptTriviaKinds: Set = [ "lineComment", "blockComment", @@ -36,10 +39,13 @@ private let keptTriviaKinds: Set = [ "unexpectedText", ] -/// Serialize a trivia collection into `{ kind, text, range }` pieces, keeping -/// only kinds in `keptTriviaKinds`. `start` is the absolute position of the -/// first piece (leading trivia at `token.position`; trailing trivia at -/// `token.endPositionBeforeTrailingTrivia`). +/// Serialize a trivia collection into an array of `{ kind, text, range }` +/// pieces, keeping only the kinds in `keptTriviaKinds`. +/// +/// `start` is the absolute position of the first piece (a token's leading +/// trivia starts at `token.position`; its trailing trivia at +/// `token.endPositionBeforeTrailingTrivia`). Each piece's range is derived by +/// accumulating piece lengths, so kept pieces carry an exact source location. private func serializeTrivia( _ trivia: Trivia, startingAt start: AbsolutePosition, @@ -49,8 +55,9 @@ private func serializeTrivia( var offset = start.utf8Offset for piece in trivia.pieces { let length = piece.sourceLength.utf8Length - // Enum-case mirror label gives us a stable kind (e.g. "lineComment") - // without an exhaustive switch over every TriviaPiece case. + // The label of an enum case mirror is the case name (e.g. "spaces", + // "lineComment"), which gives us a stable kind without an exhaustive + // switch over every `TriviaPiece` case. let kind = Mirror(reflecting: piece).children.first?.label ?? "\(piece)" if keptTriviaKinds.contains(kind) { result.append([ @@ -70,12 +77,19 @@ private func serializeTrivia( /// Recursively convert a SwiftSyntax node into a JSON-serializable value. /// /// * Tokens carry `kind`, `tokenKind`, `text`, and `range`, plus -/// `leadingTrivia`/`trailingTrivia` when non-empty. -/// * Layout nodes (e.g. `functionDecl`) carry `kind` and `range`, and -/// embed their children as members keyed by the field name in the parent -/// (e.g. `name`, `body`); absent optional children are omitted. -/// * Collection nodes (e.g. `codeBlockItemList`) are elided to a plain array -/// of their serialized elements — their own `kind`/`range` are dropped. +/// `leadingTrivia`/`trailingTrivia` — but only when non-empty (after +/// filtering, most tokens have no trivia, so the keys are simply absent). +/// * Layout nodes (e.g. `functionDecl`) carry `kind` and source `range`, and +/// additionally embed their children directly as members keyed by the +/// child's name in the parent (e.g. `name`, `signature`, `body`); absent +/// optional children are omitted. Field names never collide with +/// `kind`/`range`. +/// * Collection nodes (e.g. `codeBlockItemList`) are *elided*: they become a +/// plain array of their serialized elements, taking the place of the +/// collection node itself. A list-valued layout field (e.g. `parameters`) is +/// therefore simply a JSON array. This drops the collection node's own +/// `kind`/`range`, which are unnamed and largely recoverable from the +/// elements. private func serialize( _ node: Syntax, _ converter: SourceLocationConverter @@ -86,7 +100,7 @@ private func serialize( } } - // Node's content range, excluding surrounding trivia. + // Source range covering the node's content, excluding surrounding trivia. let range: [String: Any] = [ "start": location(node.positionAfterSkippingLeadingTrivia, converter), "end": location(node.endPositionBeforeTrailingTrivia, converter), @@ -99,6 +113,9 @@ private func serialize( "text": token.text, "range": range, ] + // Only emit trivia when present; after filtering, most tokens have none. + // Leading trivia starts at the token's own position; trailing trivia + // starts just after the token's content. let leading = serializeTrivia( token.leadingTrivia, startingAt: token.position, converter) if !leading.isEmpty { @@ -120,6 +137,9 @@ private func serialize( ] var unnamed = 0 for child in node.children(viewMode: .sourceAccurate) { + // Layout children are named; embed each under its field name in the + // parent (the same mechanism SwiftSyntax uses for its debug dump). A + // child that is a collection serializes to an array (see above). if let keyPath = child.keyPathInParent, let name = childName(keyPath) { result[name] = serialize(child, converter) } else { @@ -131,9 +151,11 @@ private func serialize( return result } -/// Parse a NUL-terminated Swift source string and return a heap-allocated, -/// NUL-terminated JSON representation of the syntax tree. Caller must release -/// the returned pointer with `ssr_string_free`. Returns `nil` on failure. +/// Parse the given NUL-terminated Swift source string and return a +/// heap-allocated, NUL-terminated JSON representation of the syntax tree. +/// +/// The returned pointer is owned by the caller and MUST be released with +/// `ssr_string_free`. Returns `nil` on failure. @_cdecl("ssr_parse_json") public func ssr_parse_json(_ source: UnsafePointer?) -> UnsafeMutablePointer? { guard let source = source else { return nil } diff --git a/unified/swift-syntax-rs/xcode_transition.bzl b/unified/swift-syntax-rs/xcode_transition.bzl index 55ce03c9902d..c6f293a0452c 100644 --- a/unified/swift-syntax-rs/xcode_transition.bzl +++ b/unified/swift-syntax-rs/xcode_transition.bzl @@ -1,32 +1,18 @@ -"""Per-target Xcode configuration for the `swift_library` under -`//unified/swift-syntax-rs` on macOS. +"""Per-target Xcode configuration for `//unified/swift-syntax-rs` on macOS. -On macOS, `rules_swift` auto-registers `xcode_swift_toolchain`, whose -analysis reads `cc_toolchain.target_gnu_system_name` and fails on Bazel's -built-in `local_config_cc` (whose triple is literally "local"). Working -around this needs two flags: +`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, whose version strings match the ones - `rules_swift`'s `system_sdk` selects are keyed on. + `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`. -We apply them via an incoming-edge Starlark transition on the -`swift_library` target itself (through the `xcode_transition_swift_library` -macro below), rather than globally in `.bazelrc`. That keeps every other -target on macOS on Bazel's default CC toolchain (`local_config_cc`) and -avoids materializing the `@local_config_xcode` / -`@local_config_apple_cc_toolchains` repos unless something under -`//unified/swift-syntax-rs` is actually being built. - -The transition is placed on `swift_library` — not on the downstream Rust -targets — so the Apple CC toolchain is only used for analyzing the Swift -side. The Rust compilation and any other transitive C/C++ deps stay on -Bazel's default CC toolchain, matching how they build elsewhere in the -repository. - -The transition is a no-op on non-macOS platforms. +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") @@ -38,8 +24,7 @@ _EXTRA_TOOLCHAINS = "//command_line_option:extra_toolchains" def _transition_impl(settings, attr): if attr.os != "macos": - # Preserve input values so the transitioned configuration is - # identical to the incoming one (no reconfiguration penalty). + # Preserve inputs so the configuration is identical to the incoming one. return { _XCODE_VERSION_CONFIG: settings[_XCODE_VERSION_CONFIG], _EXTRA_TOOLCHAINS: settings[_EXTRA_TOOLCHAINS], @@ -60,12 +45,9 @@ _xcode_transition = transition( def _wrapper_impl(ctx): src = ctx.attr.actual[0] - providers = [ - # Only forward the providers a downstream `rust_*` target reads - # from a `deps` entry. `CcInfo` carries the linking info; forward - # `OutputGroupInfo` too for `bazel build --output_groups=...`. - src[DefaultInfo], - ] + # 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]) @@ -89,14 +71,10 @@ _xcode_transition_swift_library_rule = rule( def xcode_transition_swift_library(name, visibility = None, tags = None, target_compatible_with = None, **kwargs): """`swift_library` wrapped in the macOS Xcode-config transition. - Splits `name` into a private inner target `_impl_` (a plain - `swift_library`, hidden from wildcards via `tags = ["manual"]`) and a - public `name` that applies the incoming Xcode-config transition on - macOS and forwards the inner target's `CcInfo` (and other relevant - providers) to downstream consumers. Downstream `rust_*` targets can - depend on `name` as an ordinary `deps` entry — they stay in the - default configuration; only the `swift_library` sub-graph flips to - the Apple CC toolchain, and only on macOS. + Emits a private inner `_impl_` `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( From 266eef555689ae62d4e96bc600ccf811aa9adb51 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 14 Jul 2026 13:55:58 +0000 Subject: [PATCH 30/33] Address review comments on swift-syntax-rs macOS toolchain docs --- unified/swift-syntax-rs/BUILD.bazel | 8 -------- unified/swift-syntax-rs/README.md | 4 ++-- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/unified/swift-syntax-rs/BUILD.bazel b/unified/swift-syntax-rs/BUILD.bazel index 12a7c74edbe8..be7d41eaf871 100644 --- a/unified/swift-syntax-rs/BUILD.bazel +++ b/unified/swift-syntax-rs/BUILD.bazel @@ -18,14 +18,6 @@ _SWIFT_SUPPORTED_PLATFORMS = select({ # Swift FFI shim: wraps swift-syntax and exposes a small C ABI. The Rust # targets below link against its `CcInfo`. -# -# On macOS, `rules_swift` auto-registers `xcode_swift_toolchain`, whose -# analysis fails against Bazel's default `local_config_cc`. To avoid -# forcing every macOS target in the repo onto the Apple CC toolchain, the -# Xcode-config flags are applied only along the incoming edge into this -# `swift_library` via `xcode_transition_swift_library` (see -# `xcode_transition.bzl`). Downstream Rust targets stay in the default -# configuration. xcode_transition_swift_library( name = "swift_syntax_ffi", srcs = ["swift/Sources/SwiftSyntaxFFI/SwiftSyntaxFFI.swift"], diff --git a/unified/swift-syntax-rs/README.md b/unified/swift-syntax-rs/README.md index 9512d06e2056..6a714137d8c7 100644 --- a/unified/swift-syntax-rs/README.md +++ b/unified/swift-syntax-rs/README.md @@ -148,8 +148,8 @@ Requirements: **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 macOS toolchain is a `.pkg` archive that can only be - fetched and extracted on a macOS host. On macOS, `rules_swift` also needs +- **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 From 96cd3b534c0d111f8a2d8fe4e2f81644acc354e3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 14 Jul 2026 14:01:09 +0000 Subject: [PATCH 31/33] Revert doc-comment reformatting in unified/swift-syntax-rs/build.rs --- unified/swift-syntax-rs/build.rs | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/unified/swift-syntax-rs/build.rs b/unified/swift-syntax-rs/build.rs index b97ca9b600d5..3cd2e2858742 100644 --- a/unified/swift-syntax-rs/build.rs +++ b/unified/swift-syntax-rs/build.rs @@ -49,8 +49,8 @@ fn main() { } } -/// Directory containing the active Swift toolchain's runtime libraries -/// (e.g. `libswiftCore.so`). +/// Query the active Swift toolchain for the directory containing its runtime +/// shared libraries (e.g. `libswiftCore.so`). fn swift_runtime_dir() -> Option { let output = Command::new(swiftc_bin()) .arg("-print-target-info") @@ -61,7 +61,8 @@ fn swift_runtime_dir() -> Option { } let info = String::from_utf8_lossy(&output.stdout); - // Extract `"runtimeResourcePath": "..."` without pulling in a JSON dep. + // Extract the value of `"runtimeResourcePath": "..."` without pulling in a + // JSON dependency. let key = "\"runtimeResourcePath\""; let start = info.find(key)?; let rest = &info[start + key.len()..]; @@ -75,20 +76,25 @@ fn swift_runtime_dir() -> Option { Some(PathBuf::from(resource_path).join(if cfg!(target_os = "macos") { "macosx" } else { "linux" })) } -/// `swift` driver: `$SWIFT` if set, else `swift` from `PATH`. +/// The `swift` driver to invoke: `$SWIFT` if set, otherwise `swift` from `PATH`. +/// This keeps the build tool-agnostic — any Swift install works; no particular +/// version manager is required. fn swift_bin() -> String { env::var("SWIFT").unwrap_or_else(|_| "swift".to_string()) } -/// `swiftc` compiler: `$SWIFTC` if set, else `swiftc` from `PATH`. +/// The `swiftc` compiler to invoke: `$SWIFTC` if set, otherwise `swiftc` from +/// `PATH`. fn swiftc_bin() -> String { env::var("SWIFTC").unwrap_or_else(|_| "swiftc".to_string()) } -/// Workaround for GitHub Codespaces, which sets -/// `GIT_CONFIG_KEY_0=safe.bareRepository` / `GIT_CONFIG_VALUE_0=explicit` and -/// breaks the cached bare git repos `swift build` uses. Relax to `all` only -/// for this subprocess, and only when that exact key is set. +/// Some environments (notably GitHub Codespaces) inject +/// `GIT_CONFIG_KEY_0=safe.bareRepository` / `GIT_CONFIG_VALUE_0=explicit`, which +/// breaks the cached bare git repositories `swift build` uses. When exactly that +/// key is present, relax it to `all` for the `swift build` subprocess only +/// (rather than unconditionally, which could clobber an unrelated +/// `GIT_CONFIG_VALUE_0`). fn apply_bare_repository_workaround(command: &mut Command) { if env::var("GIT_CONFIG_KEY_0").as_deref() == Ok("safe.bareRepository") { command.env("GIT_CONFIG_VALUE_0", "all"); From 03aed00dd3261978ba10da39777b546e4f3a1fae Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 14 Jul 2026 14:15:00 +0000 Subject: [PATCH 32/33] Clarify Swift toolchain hermeticity comment in MODULE.bazel --- MODULE.bazel | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/MODULE.bazel b/MODULE.bazel index 1e4b325319b0..2b12a065767e 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -226,10 +226,11 @@ use_repo( "swift-resource-dir-macos", ) -# Hermetic Swift toolchain (swift.org) for building `unified/swift-syntax-rs`. -# Only registered as an exec toolchain on Linux; on macOS `rules_swift` auto- -# registers `xcode_swift_toolchain` (host Xcode + OS-provided Swift runtime). -# Version comes from `unified/swift-syntax-rs/.swift-version`. +# 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", From b43b4aca1c3f001928ec0b3b718b01ba8a4f14f7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 14 Jul 2026 14:18:02 +0000 Subject: [PATCH 33/33] Simplify apple_support extension comments in MODULE.bazel --- MODULE.bazel | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/MODULE.bazel b/MODULE.bazel index 2b12a065767e..4c9e3316c018 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -246,16 +246,11 @@ register_toolchains( "@swift_toolchain//:swift_toolchain_exec_ubuntu24.04", ) -# `apple_support`'s xcode_config, selected via `--xcode_version_config` by the -# per-target Xcode-config transition in `unified/swift-syntax-rs/xcode_transition.bzl`. -# Needed because `rules_swift`'s `system_sdk` selects on version strings from -# `apple_support`'s `xcode_locator.m`, which Bazel's built-in `host_xcodes` does not match. +# `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_support`'s CC toolchains, forced ahead of Bazel's built-in -# `local_config_cc` via `--extra_toolchains` by the per-target Xcode-config -# transition in `unified/swift-syntax-rs/xcode_transition.bzl`. apple_cc_configure = use_extension("@apple_support//crosstool:setup.bzl", "apple_cc_configure_extension") use_repo(apple_cc_configure, "local_config_apple_cc_toolchains")