From e9083b73ec0f7f1d5b6875237eb1aab68a62c7f7 Mon Sep 17 00:00:00 2001 From: David Zbarsky Date: Mon, 22 Jun 2026 11:43:01 -0400 Subject: [PATCH] Use only hermetic Swift toolchains --- .bazelci/presubmit.yml | 32 +- .bcr/presubmit.yml | 12 +- .github/generate-notes.sh | 14 + MODULE.bazel | 23 +- README.md | 64 +-- doc/providers.md | 4 +- doc/standalone_toolchain.md | 67 ++-- swift/extensions.bzl | 24 +- swift/internal/BUILD | 15 +- swift/internal/extensions/BUILD.bazel.tpl | 39 +- .../extensions/standalone_toolchain.bzl | 40 ++ swift/internal/extensions/toolchains.bzl | 65 ++- swift/internal/repositories.bzl | 6 - swift/internal/swift_autoconfiguration.bzl | 370 ------------------ swift/providers.bzl | 3 + swift/toolchains/BUILD | 77 ---- swift/toolchains/swift_toolchain.bzl | 12 +- swift/toolchains/swift_tools.bzl | 6 + swift/toolchains/xcode_swift_toolchain.bzl | 102 ++++- test/synthesize_interface_tests.bzl | 2 +- tools/common/BUILD | 3 - tools/common/bazel_substitutions.cc | 46 +-- tools/dump_toolchains/BUILD | 9 - tools/dump_toolchains/dump_toolchains.sh | 44 --- 24 files changed, 328 insertions(+), 751 deletions(-) delete mode 100644 swift/internal/swift_autoconfiguration.bzl delete mode 100644 tools/dump_toolchains/BUILD delete mode 100755 tools/dump_toolchains/dump_toolchains.sh diff --git a/.bazelci/presubmit.yml b/.bazelci/presubmit.yml index 5626e8a51..a7e58c8a0 100644 --- a/.bazelci/presubmit.yml +++ b/.bazelci/presubmit.yml @@ -10,23 +10,11 @@ x_defaults: test_targets: - "//examples/..." - "//test/..." - linux_environment: &linux_environment - platform: ubuntu2204 - environment: - SWIFT_VERSION: "6.0.3" - SWIFT_HOME: "$HOME/swift-$SWIFT_VERSION" - PATH: "$SWIFT_HOME/usr/bin:$PATH" linux_common: &linux_common - <<: *linux_environment - build_flags: &linux_flags - # On Linux, we look for Swift toolchain binaries on the path. We may be - # able to change this when we start auto-downloading toolchains (see - # https://github.com/bazelbuild/rules_swift/issues/4). - - "--action_env=PATH" + platform: ubuntu2204 build_targets: - "//examples/..." - "-//examples/apple/..." - test_flags: *linux_flags test_targets: - "//examples/..." - "//test/..." @@ -73,28 +61,16 @@ tasks: ubuntu2204_latest: name: "Current LTS" bazel: latest - shell_commands: - - "echo --- Downloading and extracting Swift $SWIFT_VERSION to $SWIFT_HOME" - - "mkdir $SWIFT_HOME" - - "curl https://download.swift.org/swift-${SWIFT_VERSION}-release/ubuntu2204/swift-${SWIFT_VERSION}-RELEASE/swift-${SWIFT_VERSION}-RELEASE-ubuntu22.04.tar.gz | tar xvz --strip-components=1 -C $SWIFT_HOME" <<: *linux_common ubuntu2204_rolling: name: "Latest rolling" bazel: rolling - shell_commands: - - "echo --- Downloading and extracting Swift $SWIFT_VERSION to $SWIFT_HOME" - - "mkdir $SWIFT_HOME" - - "curl https://download.swift.org/swift-${SWIFT_VERSION}-release/ubuntu2204/swift-${SWIFT_VERSION}-RELEASE/swift-${SWIFT_VERSION}-RELEASE-ubuntu22.04.tar.gz | tar xvz --strip-components=1 -C $SWIFT_HOME" <<: *linux_common ubuntu2204_last_green: name: "Last Green Bazel" bazel: last_green - shell_commands: - - "echo --- Downloading and extracting Swift $SWIFT_VERSION to $SWIFT_HOME" - - "mkdir $SWIFT_HOME" - - "curl https://download.swift.org/swift-${SWIFT_VERSION}-release/ubuntu2204/swift-${SWIFT_VERSION}-RELEASE/swift-${SWIFT_VERSION}-RELEASE-ubuntu22.04.tar.gz | tar xvz --strip-components=1 -C $SWIFT_HOME" <<: *linux_common # TODO: re-enable when Windows in Bazel CI is properly configured for Swift. @@ -106,11 +82,7 @@ tasks: doc_tests: name: "Doc tests" bazel: latest - <<: *linux_environment - shell_commands: - - "echo --- Downloading and extracting Swift $SWIFT_VERSION to $SWIFT_HOME" - - "mkdir $SWIFT_HOME" - - "curl https://download.swift.org/swift-${SWIFT_VERSION}-release/ubuntu2204/swift-${SWIFT_VERSION}-RELEASE/swift-${SWIFT_VERSION}-RELEASE-ubuntu22.04.tar.gz | tar xvz --strip-components=1 -C $SWIFT_HOME" + platform: ubuntu2204 test_targets: - "doc/..." diff --git a/.bcr/presubmit.yml b/.bcr/presubmit.yml index fdab99422..919ad58c0 100644 --- a/.bcr/presubmit.yml +++ b/.bcr/presubmit.yml @@ -1,15 +1,10 @@ -shell_commands: &shell_commands -- "echo --- Downloading and extracting Swift $SWIFT_VERSION to $SWIFT_HOME" -- "mkdir $SWIFT_HOME" -- "curl https://download.swift.org/swift-${SWIFT_VERSION}-release/ubuntu2004/swift-${SWIFT_VERSION}-RELEASE/swift-${SWIFT_VERSION}-RELEASE-ubuntu20.04.tar.gz | tar xvz --strip-components=1 -C $SWIFT_HOME" - matrix: bazel: - 8.x - 9.x - rolling platform: - - ubuntu2004 + - ubuntu2204 tasks: verify_targets: name: Verify build targets @@ -17,12 +12,7 @@ tasks: platform: ${{ platform }} environment: CC: "clang" - SWIFT_VERSION: "6.0.3" - SWIFT_HOME: "$HOME/swift-$SWIFT_VERSION" - PATH: "$PATH:$SWIFT_HOME/usr/bin" - shell_commands: *shell_commands build_flags: - - "--action_env=PATH" - "--incompatible_enable_proto_toolchain_resolution" build_targets: - "@rules_swift//examples/xplatform/..." diff --git a/.github/generate-notes.sh b/.github/generate-notes.sh index 3a09ad95d..a31d6012c 100755 --- a/.github/generate-notes.sh +++ b/.github/generate-notes.sh @@ -15,5 +15,19 @@ This release is compatible with: TODO \`\`\`bzl bazel_dep(name = "rules_swift", version = "$new_version") + +swift = use_extension( + "@rules_swift//swift:extensions.bzl", + "swift", +) +swift.toolchain( + name = "swift_toolchain", + # Use "ubuntu22.04" or another supported platform on Linux. + platforms = ["xcode"], + swift_version = "6.3", +) +use_repo(swift, "swift_toolchain") + +register_toolchains("@swift_toolchain//:all") \`\`\` EOF diff --git a/MODULE.bazel b/MODULE.bazel index 30e9a8029..044da45fe 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -32,14 +32,11 @@ use_repo( "com_github_grpc_grpc_swift", "rules_swift_index_import_5_8", "rules_swift_index_import_6_1", - "rules_swift_local_config", ) apple_cc_configure = use_extension("@apple_support//crosstool:setup.bzl", "apple_cc_configure_extension") use_repo(apple_cc_configure, "local_config_apple_cc") -register_toolchains("//swift/toolchains:all") - system_sdk = use_extension("//swift:extensions.bzl", "system_sdk") system_sdk.configure_sdks( # NOTE: This doesn't apply to downstream repos @@ -82,6 +79,11 @@ use_repo(system_sdk, "system_sdk") swift = use_extension("//swift:extensions.bzl", "swift", dev_dependency = True) swift.toolchain( name = "swift_toolchain", + platforms = [ + "ubuntu22.04", + "ubuntu22.04-aarch64", + "xcode", + ], swift_version = "6.3", ) use_repo( @@ -93,20 +95,7 @@ use_repo( ) register_toolchains( - # We're using ubuntu22.04 here because that's what's used in the current rules_swift buildkite CI. - # In a perfect world, we would be able to have bazel automatically use the toolchain that just works. - # Unfortunately, we are not in this world yet. This would require either one of these two things to happen: - # * either have a constraint to select on the current distribution, which would allow us to register all - # the toolchains and have bazel automatically select the right one. But there is no such constraint today. - # * or have a swift universal Linux toolchain. Which is not the case today. - # Anyway, for now we will need to update this every time we update the CI environment. Contributors might also - # have to update this list if they're testing on a different Linux distribution. - "@swift_toolchain//:cc_toolchain_embedded_ubuntu22.04", - "@swift_toolchain//:cc_toolchain_embedded_ubuntu22.04-aarch64", - "@swift_toolchain//:cc_toolchain_embedded_xcode", - "@swift_toolchain//:swift_toolchain_embedded_ubuntu22.04", - "@swift_toolchain//:swift_toolchain_embedded_ubuntu22.04-aarch64", - "@swift_toolchain//:swift_toolchain_embedded_xcode", + "@swift_toolchain//:all", dev_dependency = True, ) diff --git a/README.md b/README.md index 4dcab4456..bf0bcb8d1 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ If you run into any problems with these rules, please ## Basic Examples -Create a simple CLI that can run on macOS, Linux, or Windows: +Create a simple CLI that can run on macOS or Linux: ```bzl load("@rules_swift//swift:swift_binary.bzl", "swift_binary") @@ -46,64 +46,40 @@ repository. ## Quick Setup -### 1. Install Swift +### 1. Configure your workspace -Before getting started, make sure that you have a Swift toolchain installed. +Copy the `MODULE.bazel` snippet from +[the releases page](https://github.com/bazelbuild/rules_swift/releases), then +select the platform that matches your build host. `rules_swift` downloads the +selected Swift release and registers it as a hermetic Bazel toolchain; it does +not discover or use a Swift installation from the host. -**Apple users:** Install [Xcode](https://developer.apple.com/xcode/downloads/). -If this is your first time installing it, make sure to open it once after -installing so that the command line tools are correctly configured. +See [Hermetic Swift toolchain](doc/standalone_toolchain.md) for the complete +setup and the supported platform names. -**Linux users:** Follow the instructions on the -[Swift download page](https://swift.org/download/) to download and install the -appropriate Swift toolchain for your platform. Take care to ensure that you have -all of Swift's dependencies installed (such as ICU, Clang, and so forth), and -also ensure that the Swift compiler is available on your system path. +### 2. Install platform dependencies -### 2. Configure your workspace +**Apple users:** Install [Xcode](https://developer.apple.com/xcode/downloads/). +`rules_swift` uses the Xcode SDKs and Apple linker, but Swift compiler actions +use the hermetic toolchain declared in `MODULE.bazel`. If this is your first +time installing Xcode, open it once so that the command line tools are +configured. -Copy the `MODULE.bazel` snippet from [the releases page](https://github.com/bazelbuild/rules_swift/releases). +**Linux users:** Install the system dependencies required by the selected +swift.org toolchain, including Clang and ICU. -### 3. Additional configuration (Linux only) +### 3. Configure Clang (Linux only) The `swift_binary` and `swift_test` rules expect to use `clang` as the driver for linking, and they query the Bazel C++ API and CROSSTOOL to determine which arguments should be passed to the linker. By default, the C++ toolchain used by Bazel is `gcc`, so Swift users on Linux need to override this by setting the -environment variable `CC=clang` when invoking Bazel. +environment variable `CC=clang` when invoking Bazel. The downloaded Swift +toolchain does not replace Bazel's C++ toolchain. This step is not necessary for macOS users because the Xcode toolchain always uses `clang`. -## Building with a Standalone Swift Toolchain - -As an alternative to the host's Swift install, `rules_swift` can download -and register a hermetic Swift toolchain from swift.org. See -[doc/standalone_toolchain.md](doc/standalone_toolchain.md) for setup -instructions. - -## Building with Custom Toolchains - -**macOS hosts:** You can build with a custom Swift toolchain (downloaded -from https://swift.org/download) instead of Xcode's default. To do so, -pass the following flag to Bazel: - -```lang-none ---action_env=TOOLCHAINS=toolchain.id -``` - -Where `toolchain.id` is the value of the `CFBundleIdentifier` key in the -toolchain's Info.plist file. - -To list the available toolchains and their bundle identifiers, you can run: - -```command -bazel run @rules_swift//tools/dump_toolchains -``` - -**Linux hosts:** At this time, Bazel uses whichever `swift` executable is -encountered first on your `PATH`. - ## Supporting debugging To make cacheable builds work correctly with debugging see diff --git a/doc/providers.md b/doc/providers.md index 238aacb6d..46548d2a7 100644 --- a/doc/providers.md +++ b/doc/providers.md @@ -310,7 +310,8 @@ that use the toolchain. ## SwiftToolsInfo
-SwiftToolsInfo(swift_driver, swift_autolink_extract, swift_symbolgraph_extract, additional_inputs)
+SwiftToolsInfo(swift_driver, swift_autolink_extract, swift_symbolgraph_extract,
+               swift_synthesize_interface, additional_inputs)
 
Propagates information about Swift toolchain tools that can be specified as @@ -326,6 +327,7 @@ dependencies, ensuring they are available in the execution environment. | swift_driver | `File`. The Swift driver executable that orchestrates compilation and linking operations. This is the main entry point for invoking the Swift compiler toolchain. | | swift_autolink_extract | `File`. The executable that extracts autolink information from object files. This tool is used to determine which libraries need to be linked based on import statements in Swift code. | | swift_symbolgraph_extract | `File`. The executable that extracts symbol graph information from Swift modules. This tool generates structured data about APIs, which can be used for documentation generation and other tooling purposes. | +| swift_synthesize_interface | `File` or `None`. The executable that synthesizes Swift interfaces. | | additional_inputs | `List` of `File`s. Additional files to add to the action input root when calling these tools. | diff --git a/doc/standalone_toolchain.md b/doc/standalone_toolchain.md index 7e96e51cf..17e4f9faa 100644 --- a/doc/standalone_toolchain.md +++ b/doc/standalone_toolchain.md @@ -1,13 +1,14 @@ -# Standalone Swift toolchain +# Hermetic Swift toolchain -`rules_swift` ships a Bzlmod module extension that downloads a standalone -Swift toolchain from [swift.org](https://swift.org/download) and registers -it with Bazel. This gives you a hermetic Swift toolchain that does not rely -on the host's pre-installed compiler (or Xcode), which is useful for -reproducible builds, CI, and cross-platform builds. +`rules_swift` uses a Bzlmod module extension to download a Swift toolchain from +[swift.org](https://swift.org/download) and expose it to Bazel toolchain +resolution. `rules_swift` does not discover a Swift compiler from `PATH`, Xcode, +or the Windows host. Every build must declare and register a hermetic Swift +toolchain in the root `MODULE.bazel`. -The extension supports both macOS (`.pkg`) and Linux (`.tar.gz`) toolchain -archives. +The extension supports macOS (`.pkg`) and Linux (`.tar.gz`) toolchain archives. +Apple builds still use the SDKs, Clang, and linker from Xcode, but all Swift +compiler actions use the downloaded Swift toolchain. ## Quick start @@ -21,37 +22,27 @@ swift = use_extension( swift.toolchain( name = "swift_toolchain", - swift_version = "6.2.4", + platforms = ["xcode"], + swift_version = "6.3", ) -use_repo( - swift, - "swift_toolchain", - # Add one entry per platform you intend to build on. The repo names are - # of the form `_`. - "swift_toolchain_xcode", - "swift_toolchain_ubuntu22.04", - "swift_toolchain_ubuntu22.04-aarch64", -) +use_repo(swift, "swift_toolchain") -register_toolchains( - "@swift_toolchain//:cc_toolchain_embedded_xcode", - "@swift_toolchain//:swift_toolchain_embedded_xcode", - "@swift_toolchain//:cc_toolchain_embedded_ubuntu22.04", - "@swift_toolchain//:swift_toolchain_embedded_ubuntu22.04", - "@swift_toolchain//:cc_toolchain_embedded_ubuntu22.04-aarch64", - "@swift_toolchain//:swift_toolchain_embedded_ubuntu22.04-aarch64", -) +register_toolchains("@swift_toolchain//:all") ``` -You only need to `use_repo` and `register_toolchains` for the platforms you -actually build on. Each platform repo name is `_`. +This example configures a macOS build host. For Ubuntu 22.04 on x86-64, use +`platforms = ["ubuntu22.04"]`; for Ubuntu 22.04 on Arm64, use +`platforms = ["ubuntu22.04-aarch64"]`. List every build-host platform used by +developers and CI. The generated `@swift_toolchain//:all` target pattern +contains only the selected platforms. ## The `swift.toolchain` tag | Attribute | Type | Description | |---|---|---| | `name` | string, required | Repository name of the generated parent toolchain repo. Per-platform repos are named `_`. | +| `platforms` | string_list | Platform archives to download and generate toolchain declarations for. Defaults to every platform available for the selected Swift version. Explicit selection is recommended. | | `swift_version` | string | The Swift release version (e.g. `6.2.4`) or a snapshot identifier (see [Snapshots](#snapshot-toolchains)). Mutually exclusive with `swift_version_file`. | | `swift_version_file` | label | A label pointing at a file containing the version string (typically a `.swift-version` file checked into the repo). Mutually exclusive with `swift_version`. | | `platform_sha256` | string_dict | Optional map of platform → SHA-256. Required for snapshots and any version not present in the bundled `SWIFT_RELEASES` table. When set, it overrides the bundled checksums. | @@ -69,8 +60,8 @@ The platform keys come from the swift.org download URLs and currently include: * `ubi9`, `ubi9-aarch64` Bazel does not currently have constraints to auto-select between Linux -distributions, so you must explicitly `register_toolchains` for the -distribution(s) your CI and developers run on. +distributions, so `platforms` must identify the distribution(s) that CI and +developers run on. The macOS (`xcode`) archive is a `.pkg` and can only be extracted on a macOS host — pulling it on Linux will fail at fetch time. @@ -83,8 +74,9 @@ For each platform, the parent repo (`@`) exposes: Swift target platform. * `swift_toolchain_embedded_` — Swift toolchain for the embedded target. -* `swift_toolchain_exec_` — Swift toolchain for use as an exec - toolchain (i.e. compiling tools that run on the build host). +* `swift_toolchain_exec_` — Swift toolchain for Linux host targets. +* `xcode-toolchain-*` and `xcode-sdk-toolchain-*` — Swift toolchains for Apple + target platforms, generated when `platforms` contains `xcode`. The per-platform repo (`@_`) holds the extracted Swift toolchain itself and is what the toolchain targets above point at. @@ -161,12 +153,9 @@ swift = use_extension( ) ``` -## Building with the standalone toolchain +## Building with the hermetic toolchain Once registered, normal `swift_binary`, `swift_library`, and `swift_test` -targets pick up the toolchain through Bazel's standard toolchain -resolution — no per-target configuration is needed. - -This is independent of the macOS `--action_env=TOOLCHAINS=…` mechanism -described in the top-level README, which selects between Xcode-managed -toolchains rather than between hermetic Bazel-managed ones. +targets pick up the toolchain through Bazel's standard toolchain resolution; +no per-target configuration is needed. `PATH`, `TOOLCHAINS`, and an Xcode Swift +compiler do not select or replace the registered hermetic Swift compiler. diff --git a/swift/extensions.bzl b/swift/extensions.bzl index be6bbc0c1..111a12315 100644 --- a/swift/extensions.bzl +++ b/swift/extensions.bzl @@ -50,8 +50,8 @@ def _standalone_toolchain_impl(module_ctx): if not root_module: fail("Could not find a root module. This should never happen.") - toolchains_build_file_content = "" for toolchain in root_module.tags.toolchain: + toolchains_build_file_content = "" if toolchain.swift_version and toolchain.swift_version_file: fail("Cannot use both swift_version and swift_version_file together. Please choose one.") @@ -68,8 +68,22 @@ def _standalone_toolchain_impl(module_ctx): SWIFT_RELEASES.keys(), )) - swift_releases = toolchain.platform_sha256.items() or SWIFT_RELEASES[swift_version].items() - for platform, sha256 in swift_releases: + swift_releases = toolchain.platform_sha256 or SWIFT_RELEASES[swift_version] + platforms = toolchain.platforms or swift_releases.keys() + unsupported_platforms = [ + platform + for platform in platforms + if platform not in swift_releases + ] + if unsupported_platforms: + fail("Unsupported platforms for Swift {}: {}. Choose from: {}".format( + swift_version, + unsupported_platforms, + swift_releases.keys(), + )) + + for platform in platforms: + sha256 = swift_releases[platform] repository_name = toolchain.name + "_{}".format(platform) _standalone_toolchain( name = repository_name, @@ -84,6 +98,7 @@ def _standalone_toolchain_impl(module_ctx): _toolchains_repository( name = toolchain.name, build_file_content = toolchains_build_file_content, + include_apple_toolchains = "xcode" in platforms, ) metadata_kwargs = {} @@ -107,6 +122,9 @@ their hashes. For instance: `bazel run @rules_swift//tools/swift-releases -- list 6.2.4` """, ), + "platforms": attr.string_list( + doc = "Platforms for which repositories and toolchains should be generated. Defaults to every platform available for the selected Swift version.", + ), "swift_version": attr.string(doc = "Version of the swift toolchain to be installed. Cannot be used concurrently with `swift_version_file`"), "swift_version_file": attr.label(doc = "A label to the .swift_version file to use. Cannot be used concurrently with `swift_version`"), }) diff --git a/swift/internal/BUILD b/swift/internal/BUILD index c530736f2..1f3d31e25 100644 --- a/swift/internal/BUILD +++ b/swift/internal/BUILD @@ -187,16 +187,6 @@ bzl_library( deps = ["@bazel_skylib//lib:paths"], ) -bzl_library( - name = "swift_autoconfiguration", - srcs = ["swift_autoconfiguration.bzl"], - visibility = ["//swift:__subpackages__"], - deps = [ - ":toolchain_utils", - "//swift/internal:feature_names", - ], -) - bzl_library( name = "swift_symbol_graph_aspect", srcs = ["swift_symbol_graph_aspect.bzl"], @@ -277,10 +267,7 @@ bzl_library( name = "repositories", srcs = ["repositories.bzl"], visibility = ["//swift:__subpackages__"], - deps = [ - ":swift_autoconfiguration", - "@bazel_tools//tools/build_defs/repo:http.bzl", - ], + deps = ["@bazel_tools//tools/build_defs/repo:http.bzl"], ) # keep diff --git a/swift/internal/extensions/BUILD.bazel.tpl b/swift/internal/extensions/BUILD.bazel.tpl index 231ecbcb9..8cfe74396 100644 --- a/swift/internal/extensions/BUILD.bazel.tpl +++ b/swift/internal/extensions/BUILD.bazel.tpl @@ -1,10 +1,11 @@ -load("@rules_swift//swift/toolchains:swift_toolchain.bzl", "swift_toolchain") -load("@rules_swift//swift/toolchains:swift_tools.bzl", "swift_tools") load("@rules_cc//cc/toolchains:args.bzl", "cc_args") load("@rules_cc//cc/toolchains:make_variable.bzl", "cc_make_variable") load("@rules_cc//cc/toolchains:tool.bzl", "cc_tool") load("@rules_cc//cc/toolchains:tool_map.bzl", "cc_tool_map") load("@rules_cc//cc/toolchains:toolchain.bzl", "cc_toolchain") +load("@rules_swift//swift/toolchains:swift_toolchain.bzl", "swift_toolchain") +load("@rules_swift//swift/toolchains:swift_tools.bzl", "swift_tools") +load("@rules_swift//swift/toolchains:xcode_swift_toolchain.bzl", "xcode_swift_toolchain") package(default_visibility = ["//visibility:public"]) @@ -125,26 +126,15 @@ cc_toolchain( swift_tools( name = "tools", - swift_driver = "usr/bin/swiftc", - swift_autolink_extract = "usr/bin/swift-autolink-extract", - swift_symbolgraph_extract = "usr/bin/swift-symbolgraph-extract", additional_inputs = glob([ - "usr/lib/swift/**" + "usr/lib/swift/**", ] + [ "usr/bin/swift", "usr/bin/swift-frontend", - ], exclude = [ - # for now we only tested embedded, linux and macos so we can exclude files needed for - # other platforms. - "usr/lib/swift/xrsimulator/**", - "usr/lib/swift/appletvos/**", - "usr/lib/swift/watchos/**", - "usr/lib/swift/appletvsimulator/**", - "usr/lib/swift/iphoneos/**", - "usr/lib/swift/iphonesimulator/**", - "usr/lib/swift/watchsimulator/**", - "usr/lib/swift/xros/**", ]), + swift_autolink_extract = "usr/bin/swift-autolink-extract", + swift_driver = "usr/bin/swiftc", + swift_symbolgraph_extract = "usr/bin/swift-symbolgraph-extract", ) swift_toolchain( @@ -173,6 +163,8 @@ swift_toolchain( version_file = ".swift-version", ) +{xcode_toolchains} + swift_toolchain( name = "swift_toolchain_exec", arch = select({ @@ -190,10 +182,13 @@ swift_toolchain( "@platforms//os:macos": "macos", }), parsed_version = "{swift_version}", - runtime = glob([ - "usr/lib/swift/**/*.dylib", # On MacOS - "usr/lib/swift/**/*.so", # On Linux - ], allow_empty = True), + runtime = glob( + [ + "usr/lib/swift/**/*.dylib", # On MacOS + "usr/lib/swift/**/*.so", # On Linux + ], + allow_empty = True, + ), swift_tools = "tools", version_file = ".swift-version", -) \ No newline at end of file +) diff --git a/swift/internal/extensions/standalone_toolchain.bzl b/swift/internal/extensions/standalone_toolchain.bzl index 47501b379..28577fb33 100644 --- a/swift/internal/extensions/standalone_toolchain.bzl +++ b/swift/internal/extensions/standalone_toolchain.bzl @@ -8,6 +8,43 @@ platform detection and appropriate extraction methods. load("@bazel_skylib//lib:paths.bzl", "paths") +_XCODE_TOOLCHAINS = """\ +swift_tools( + name = "xcode_tools", + additional_inputs = glob([ + "usr/lib/swift/**", + ] + [ + "usr/bin/swift", + "usr/bin/swift-frontend", + ]), + swift_autolink_extract = "usr/bin/swift-autolink-extract", + swift_driver = "usr/bin/swiftc", + swift_symbolgraph_extract = "usr/bin/swift-symbolgraph-extract", + swift_synthesize_interface = "usr/bin/swift-synthesize-interface", +) + +xcode_swift_toolchain( + name = "xcode-sdk-toolchain", + features = ["swift.module_map_no_private_headers"], + parsed_version = "{swift_version}", + runtime = glob(["usr/lib/swift/**/*.dylib"]), + swift_tools = "xcode_tools", + version_file = ".swift-version", +) + +xcode_swift_toolchain( + name = "xcode-toolchain", + cross_import_overlays = ["@system_sdk//:all_cross_import_overlays"], + features = ["swift.module_map_no_private_headers"], + implicit_system_modules = "@system_sdk//:implicit_modules", + parsed_version = "{swift_version}", + runtime = glob(["usr/lib/swift/**/*.dylib"]), + swift_tools = "xcode_tools", + system_modules = "@system_sdk//:all_modules", + version_file = ".swift-version", +) +""" + # Download URLs are documented in the OpenAPI spec here: # https://github.com/swiftlang/swiftly/blob/20ceb4748fd55a7e0574cf426b70f978a2636418/Sources/SwiftlyDownloadAPI/openapi.yaml # They're of the form: https://download.swift.org/{category}/{platform}/{version}/{file} @@ -103,6 +140,9 @@ def _standalone_toolchain_impl(repository_ctx): "{exec_os}": repository_ctx.os.name, "{exec_arch}": repository_ctx.os.arch, "{swift_version}": repository_ctx.attr.swift_version, + "{xcode_toolchains}": ( + _XCODE_TOOLCHAINS.format(swift_version = repository_ctx.attr.swift_version) if repository_ctx.attr.platform == "xcode" else "" + ), }, ) diff --git a/swift/internal/extensions/toolchains.bzl b/swift/internal/extensions/toolchains.bzl index 0caaaef37..3f95f8921 100644 --- a/swift/internal/extensions/toolchains.bzl +++ b/swift/internal/extensions/toolchains.bzl @@ -31,7 +31,10 @@ toolchain( visibility = ["//visibility:public"], ) -## Exec toolchains +""" + +_EXEC_TOOLCHAIN = """ +## Exec toolchain toolchain( name = "swift_toolchain_exec_{platform}", exec_compatible_with = {exec_compatible_with}, @@ -43,6 +46,38 @@ toolchain( """ +_XCODE_TOOLCHAINS = """ +## Apple SDK toolchains +[ + toolchain( + name = "xcode-sdk-toolchain-" + arch, + exec_compatible_with = [ + "@platforms//os:macos", + ], + target_compatible_with = constraints, + toolchain = "@{toolchain_repository}//:xcode-sdk-toolchain", + toolchain_type = "@rules_swift//toolchains:sdk_toolchain_type", + visibility = ["//visibility:public"], + ) + for arch, constraints in APPLE_PLATFORMS_CONSTRAINTS.items() +] + +[ + toolchain( + name = "xcode-toolchain-" + arch, + exec_compatible_with = [ + "@platforms//os:macos", + ], + target_compatible_with = constraints, + toolchain = "@{toolchain_repository}//:xcode-toolchain", + toolchain_type = "@rules_swift//toolchains:toolchain_type", + visibility = ["//visibility:public"], + ) + for arch, constraints in APPLE_PLATFORMS_CONSTRAINTS.items() +] + +""" + def toolchains_for_platform(platform, toolchain_repository): # This assumption is baked into the API so we have to go along with it if platform == "xcode": @@ -55,14 +90,37 @@ def toolchains_for_platform(platform, toolchain_repository): "@platforms//cpu:{}".format("aarch64" if "aarch64" in platform else "x86_64"), ] - return _TOOLCHAIN_PLATFORM.format( + toolchains = _TOOLCHAIN_PLATFORM.format( exec_compatible_with = exec_compatible_with, platform = platform, toolchain_repository = toolchain_repository, ) + if platform == "xcode": + toolchains += _XCODE_TOOLCHAINS.format( + toolchain_repository = toolchain_repository, + ) + else: + toolchains += _EXEC_TOOLCHAIN.format( + exec_compatible_with = exec_compatible_with, + platform = platform, + toolchain_repository = toolchain_repository, + ) + return toolchains def _toolchains_impl(repository_ctx): - repository_ctx.file("BUILD.bazel", repository_ctx.attr.build_file_content) + prelude = "" + if repository_ctx.attr.include_apple_toolchains: + prelude = """\ +load( + "@apple_support//configs:platforms.bzl", + "APPLE_PLATFORMS_CONSTRAINTS", +) + +""" + repository_ctx.file( + "BUILD.bazel", + prelude + repository_ctx.attr.build_file_content, + ) toolchains_repository = repository_rule( implementation = _toolchains_impl, @@ -70,5 +128,6 @@ toolchains_repository = repository_rule( "build_file_content": attr.string( mandatory = True, ), + "include_apple_toolchains": attr.bool(), }, ) diff --git a/swift/internal/repositories.bzl b/swift/internal/repositories.bzl index 0a2074ea3..0a7d7c1ba 100644 --- a/swift/internal/repositories.bzl +++ b/swift/internal/repositories.bzl @@ -15,10 +15,6 @@ """Definitions for handling Bazel repositories used by the Swift rules.""" load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") -load( - "//swift/internal:swift_autoconfiguration.bzl", - "swift_autoconfiguration", -) # buildifier: disable=unnamed-macro def swift_rules_dependencies(): @@ -168,5 +164,3 @@ def swift_rules_dependencies(): urls = ["https://github.com/MobileNativeFoundation/index-import/releases/download/6.1.0.1/index-import.tar.gz"], sha256 = "9a54fc1674af6031125a9884480a1e31e1bcf48b8f558b3e8bcc6b6fcd6e8b61", ) - - swift_autoconfiguration(name = "rules_swift_local_config") diff --git a/swift/internal/swift_autoconfiguration.bzl b/swift/internal/swift_autoconfiguration.bzl deleted file mode 100644 index 35b183cb8..000000000 --- a/swift/internal/swift_autoconfiguration.bzl +++ /dev/null @@ -1,370 +0,0 @@ -# Copyright 2019 The Bazel Authors. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Definitions for autoconfiguring Swift toolchains. - -At this time, only the Linux toolchain uses this capability. The Xcode toolchain -determines which -features are supported using Xcode version checks in xcode_toolchain.bzl. - -NOTE: This file is loaded from repositories.bzl, before any workspace -dependencies have been downloaded. Therefore, only files within this repository -should be loaded here. Do not load anything else, even common libraries like -Skylib. -""" - -load( - ":feature_names.bzl", - "SWIFT_FEATURE_CODEVIEW_DEBUG_INFO", - "SWIFT_FEATURE_DEBUG_PREFIX_MAP", - "SWIFT_FEATURE_DECLARE_SWIFTSOURCEINFO", - "SWIFT_FEATURE_EMIT_SWIFTDOC", - "SWIFT_FEATURE_LLD_GC_WORKAROUND", - "SWIFT_FEATURE_MODULE_MAP_NO_PRIVATE_HEADERS", - "SWIFT_FEATURE_NO_EMBED_DEBUG_MODULE", - "SWIFT_FEATURE_USE_AUTOLINK_EXTRACT", - "SWIFT_FEATURE_USE_MODULE_WRAP", -) - -def _scratch_file(repository_ctx, temp_dir, name, content = ""): - """Creates and returns a scratch file with the given name and content. - - Args: - repository_ctx: The repository context. - temp_dir: The `path` to the temporary directory where the file should be - created. - name: The name of the scratch file. - content: The text to write into the scratch file. - - Returns: - The `path` to the file that was created. - """ - path = temp_dir.get_child(name) - repository_ctx.file(path, content) - return path - -def _swift_succeeds(repository_ctx, swiftc_path, *args): - """Returns True if an invocation of the Swift compiler is successful. - - Args: - repository_ctx: The repository context. - swiftc_path: The `path` to the `swiftc` executable to spawn. - *args: Zero or more arguments to pass to `swiftc` on the command line. - - Returns: - True if the invocation was successful (a zero exit code); otherwise, - False. - """ - swift_result = repository_ctx.execute([swiftc_path] + list(args)) - return swift_result.return_code == 0 - -def _check_supports_lld_gc_workaround(repository_ctx, swiftc_path, temp_dir): - """Returns True if lld is being used and it supports nostart-stop-gc.""" - source_file = _scratch_file( - repository_ctx, - temp_dir, - "main.swift", - """\ -print("Hello") -""", - ) - return _swift_succeeds( - repository_ctx, - swiftc_path, - source_file, - "-use-ld=lld", - "-Xlinker", - "-z", - "-Xlinker", - "nostart-stop-gc", - ) - -def _write_swift_version(repository_ctx, swiftc_path): - """Write a file containing the current Swift version info - - This is used to encode the current version of Swift as an input for caching - - Args: - repository_ctx: The repository context. - swiftc_path: The `path` to the `swiftc` executable. - - Returns: - The written file containing the version info - """ - result = repository_ctx.execute([swiftc_path, "-version"]) - contents = "unknown" - parsed = "0.0" - if result.return_code == 0: - contents = result.stdout.strip() - - # Swift version 6.3.1 (swift-6.3.1-RELEASE) -> 6.3.1 - # Swift version 6.4-dev -> 6.4 - parsed = ( - contents.splitlines()[0] - .split("Swift version")[-1] - .strip() - .split(" ")[0] - .split("-")[0] - .strip() - ) or "0.0" - - filename = "swift_version" - repository_ctx.file(filename, contents, executable = False) - return filename, parsed - -def _compute_feature_values(repository_ctx, swiftc_path): - """Computes a list of supported/unsupported features by running checks. - - The result of this function is a list of feature names that can be provided - as the `features` attribute of a toolchain rule. That is, enabled features - are represented by the feature name itself, and unsupported features are - represented as a hyphen ("-") followed by the feature name. - - Args: - repository_ctx: The repository context. - swiftc_path: The `path` to the `swiftc` executable. - - Returns: - A list of feature strings that can be provided as the `features` - attribute of a toolchain rule. - """ - feature_values = [] - for feature, checker in _FEATURE_CHECKS.items(): - # Create a scratch directory in which the check function can write any - # files that it needs to pass to `swiftc`. - mktemp_result = repository_ctx.execute([ - "mktemp", - "-d", - "tmp.autoconfiguration.XXXXXXXXXX", - ]) - temp_dir = repository_ctx.path(mktemp_result.stdout.strip()) - - if checker(repository_ctx, swiftc_path, temp_dir): - feature_values.append(feature) - else: - feature_values.append("-{}".format(feature)) - - # Clean up the scratch directory. - # TODO(allevato): Replace with `repository_ctx.delete` once it's - # released. - repository_ctx.execute(["rm", "-r", temp_dir]) - - return feature_values - -# Features whose support should be checked and the functions used to check them. -# A check function has the following signature: -# -# def (repository_ctx, swiftc_path, temp_dir) -# -# Where `swiftc_path` and `temp_dir` are `path` structures denoting the path to -# the `swiftc` executable and a scratch directory, respectively. The function -# should return True if the feature is supported. -_FEATURE_CHECKS = { - SWIFT_FEATURE_LLD_GC_WORKAROUND: _check_supports_lld_gc_workaround, -} - -def _normalized_linux_cpu(cpu): - if cpu in ("amd64"): - return "x86_64" - return cpu - -def _resolve_toolchain_root(repository_ctx, swiftc_path): - """Returns the Swift toolchain root directory for `swiftc_path`. - - Swiftly installs a symlink that is changed based on the active toolchain. - This resolves that symlink to the actual toolchain directory. - """ - result = repository_ctx.execute([swiftc_path, "-print-target-info"]) - if result.return_code != 0: - fail("Failed to run '{} -print-target-info': {}".format( - swiftc_path, - result.stderr, - )) - runtime_resource_path = json.decode(result.stdout)["paths"]["runtimeResourcePath"] - return repository_ctx.path(runtime_resource_path).dirname.dirname - -def _create_linux_toolchain(*, repository_ctx): - """Creates BUILD targets for the Swift toolchain on Linux. - - Args: - repository_ctx: The repository rule context. - """ - path_to_swiftc = repository_ctx.which("swiftc") - if not path_to_swiftc: - return """\ -# No 'swiftc' executable found in $PATH. Not auto-generating a Linux Swift \ -toolchain. -""" - - root = _resolve_toolchain_root(repository_ctx, path_to_swiftc) - feature_values = _compute_feature_values(repository_ctx, path_to_swiftc) - version_file, parsed_version = _write_swift_version(repository_ctx, path_to_swiftc) - - # TODO: This should be removed so that private headers can be used with - # explicit modules, but the build targets for CgRPC need to be cleaned up - # first because they contain C++ code. - feature_values.append(SWIFT_FEATURE_MODULE_MAP_NO_PRIVATE_HEADERS) - feature_values.append(SWIFT_FEATURE_USE_AUTOLINK_EXTRACT) - feature_values.append(SWIFT_FEATURE_USE_MODULE_WRAP) - - return """\ -swift_toolchain( - name = "linux-toolchain", - arch = "{cpu}", - features = [{feature_list}], - os = "linux", - root = "{root}", - parsed_version = "{parsed_version}", - version_file = "{version_file}", -) -""".format( - cpu = _normalized_linux_cpu(repository_ctx.os.arch), - feature_list = ", ".join([ - '"{}"'.format(feature) - for feature in feature_values - ]), - root = root, - parsed_version = parsed_version, - version_file = version_file, - ) - -def _create_xcode_toolchain(): - """Creates BUILD targets for the Swift toolchain on macOS using Xcode. - """ - feature_values = [ - # TODO: This should be removed so that private headers can be used with - # explicit modules, but the build targets for CgRPC need to be cleaned - # up first because they contain C++ code. - SWIFT_FEATURE_MODULE_MAP_NO_PRIVATE_HEADERS, - ] - - return """\ -xcode_swift_toolchain( - name = "xcode-sdk-toolchain", - features = [{feature_list}], -) - -xcode_swift_toolchain( - name = "xcode-toolchain", - cross_import_overlays = ["@system_sdk//:all_cross_import_overlays"], - features = [{feature_list}], - implicit_system_modules = "@system_sdk//:implicit_modules", - system_modules = "@system_sdk//:all_modules", -) -""".format( - feature_list = ", ".join([ - '"{}"'.format(feature) - for feature in feature_values - ]), - ) - -def _get_python_bin(repository_ctx): - if "PYTHON_BIN_PATH" in repository_ctx.os.environ: - return repository_ctx.os.environ.get("PYTHON_BIN_PATH").strip() - out = repository_ctx.which("python3.exe") - if out: - return out - out = repository_ctx.which("python.exe") - if out: - return out - return None - -def _create_windows_toolchain(*, repository_ctx): - """Creates BUILD targets for the Swift toolchain on Linux. - - Args: - repository_ctx: The repository rule context. - """ - path_to_swiftc = repository_ctx.which("swiftc.exe") - if not path_to_swiftc: - return """\ -# No 'swiftc.exe' executable found in $PATH. Not auto-generating a Windows \ -Swift toolchain. -""" - - root = path_to_swiftc.dirname.dirname - enabled_features = [ - SWIFT_FEATURE_CODEVIEW_DEBUG_INFO, - SWIFT_FEATURE_DECLARE_SWIFTSOURCEINFO, - SWIFT_FEATURE_DEBUG_PREFIX_MAP, - SWIFT_FEATURE_EMIT_SWIFTDOC, - SWIFT_FEATURE_NO_EMBED_DEBUG_MODULE, - SWIFT_FEATURE_MODULE_MAP_NO_PRIVATE_HEADERS, - ] - disabled_features = [] - - version_file, parsed_version = _write_swift_version(repository_ctx, path_to_swiftc) - xctest_version = repository_ctx.execute([ - _get_python_bin(repository_ctx), - "-c", - "import os, plistlib; " + - "print(plistlib.loads(open(os.path.join(r'{}', '..', '..', '..', 'Info.plist'), 'rb').read(), fmt=plistlib.FMT_XML)['DefaultProperties']['XCTEST_VERSION'])".format(repository_ctx.os.environ["SDKROOT"]), - ]) - - env = { - "Path": repository_ctx.os.environ["Path"] if "Path" in repository_ctx.os.environ else repository_ctx.os.environ["PATH"], - "ProgramData": repository_ctx.os.environ["ProgramData"], - } - - return """\ -swift_toolchain( - name = "windows-toolchain", - arch = "x86_64", - features = [{features}], - os = "windows", - root = "{root}", - parsed_version = "{parsed_version}", - version_file = "{version_file}", - env = {env}, - sdkroot = "{sdkroot}", - tool_executable_suffix = ".exe", - xctest_version = "{xctest_version}", -) -""".format( - features = ", ".join(['"{}"'.format(feature) for feature in enabled_features] + ['"-{}"'.format(feature) for feature in disabled_features]), - root = root, - env = env, - parsed_version = parsed_version, - sdkroot = repository_ctx.os.environ["SDKROOT"].replace("\\", "/"), - xctest_version = xctest_version.stdout.rstrip(), - version_file = version_file, - ) - -def _swift_autoconfiguration_impl(repository_ctx): - repository_ctx.file( - "BUILD", - "\n".join([ - """\ -load( - "@rules_swift//swift/toolchains:swift_toolchain.bzl", - "swift_toolchain", -) -load( - "@rules_swift//swift/toolchains:xcode_swift_toolchain.bzl", - "xcode_swift_toolchain", -) - -package(default_visibility = ["//visibility:public"]) -""", - _create_xcode_toolchain(), - _create_windows_toolchain(repository_ctx = repository_ctx), - _create_linux_toolchain(repository_ctx = repository_ctx), - ]), - ) - -swift_autoconfiguration = repository_rule( - environ = ["CC", "PATH", "ProgramData", "Path"], - implementation = _swift_autoconfiguration_impl, - local = True, -) diff --git a/swift/providers.bzl b/swift/providers.bzl index ff5d84a98..d3ce161f0 100644 --- a/swift/providers.bzl +++ b/swift/providers.bzl @@ -530,6 +530,9 @@ import statements in Swift code. `File`. The executable that extracts symbol graph information from Swift modules. This tool generates structured data about APIs, which can be used for documentation generation and other tooling purposes. +""", + "swift_synthesize_interface": """\ +`File` or `None`. The executable that synthesizes Swift interfaces. """, "additional_inputs": """\ `List` of `File`s. Additional files to add to the action input root when calling these tools. diff --git a/swift/toolchains/BUILD b/swift/toolchains/BUILD index 89d84641d..eefb29e29 100644 --- a/swift/toolchains/BUILD +++ b/swift/toolchains/BUILD @@ -1,7 +1,3 @@ -load( - "@apple_support//configs:platforms.bzl", - "APPLE_PLATFORMS_CONSTRAINTS", -) load("@bazel_skylib//:bzl_library.bzl", "bzl_library") licenses(["notice"]) @@ -65,79 +61,6 @@ bzl_library( ], ) -toolchain( - name = "linux-swift-toolchain_x86_64", - exec_compatible_with = [ - "@platforms//os:linux", - "@platforms//cpu:x86_64", - ], - target_compatible_with = [ - "@platforms//os:linux", - "@platforms//cpu:x86_64", - ], - toolchain = "@rules_swift_local_config//:linux-toolchain", - toolchain_type = "//toolchains:toolchain_type", - visibility = ["//visibility:public"], -) - -toolchain( - name = "linux-swift-toolchain_aarch64", - exec_compatible_with = [ - "@platforms//os:linux", - "@platforms//cpu:aarch64", - ], - target_compatible_with = [ - "@platforms//os:linux", - "@platforms//cpu:aarch64", - ], - toolchain = "@rules_swift_local_config//:linux-toolchain", - toolchain_type = "//toolchains:toolchain_type", - visibility = ["//visibility:public"], -) - -[ - toolchain( - name = "xcode-sdk-toolchain-" + arch, - exec_compatible_with = [ - "@platforms//os:macos", - ], - target_compatible_with = constraints, - toolchain = "@rules_swift_local_config//:xcode-sdk-toolchain", - toolchain_type = "//toolchains:sdk_toolchain_type", - visibility = ["//visibility:public"], - ) - for arch, constraints in APPLE_PLATFORMS_CONSTRAINTS.items() -] - -[ - toolchain( - name = "xcode-toolchain-" + arch, - exec_compatible_with = [ - "@platforms//os:macos", - ], - target_compatible_with = constraints, - toolchain = "@rules_swift_local_config//:xcode-toolchain", - toolchain_type = "//toolchains:toolchain_type", - visibility = ["//visibility:public"], - ) - for arch, constraints in APPLE_PLATFORMS_CONSTRAINTS.items() -] - -toolchain( - name = "windows-swift-toolchain-x86_64", - exec_compatible_with = [ - "@platforms//os:windows", - "@platforms//cpu:x86_64", - ], - target_compatible_with = [ - "@platforms//os:windows", - "@platforms//cpu:x86_64", - ], - toolchain = "@rules_swift_local_config//:windows-toolchain", - toolchain_type = "//toolchains:toolchain_type", - visibility = ["//visibility:public"], -) - # Consumed by Bazel integration tests. filegroup( name = "for_bazel_tests", diff --git a/swift/toolchains/swift_toolchain.bzl b/swift/toolchains/swift_toolchain.bzl index af31dcec9..d530c895b 100644 --- a/swift/toolchains/swift_toolchain.bzl +++ b/swift/toolchains/swift_toolchain.bzl @@ -173,6 +173,7 @@ def _all_tool_configs( ), SWIFT_ACTION_COMPILE_MODULE_INTERFACE: ( ToolConfigInfo( + additional_tools = additional_tools, driver_config = _driver_config(mode = "swiftc") if not swift_tools else None, args = ["-frontend"], executable = swift_tools.swift_driver if swift_tools else None, @@ -202,6 +203,15 @@ def _all_tool_configs( worker_mode = "wrap", ) + if swift_tools and swift_tools.swift_synthesize_interface: + tool_configs[SWIFT_ACTION_SYNTHESIZE_INTERFACE] = ToolConfigInfo( + additional_tools = additional_tools, + executable = swift_tools.swift_synthesize_interface, + use_param_file = True, + worker_mode = "wrap", + env = env, + ) + return tool_configs def _all_action_configs(os, arch, target_triple, sdkroot, xctest_version, additional_swiftc_copts): @@ -526,7 +536,7 @@ def _swift_toolchain_impl(ctx): else: swiftcopts.extend(ctx.attr._copts[BuildSettingInfo].value) - # Combine build mode features, autoconfigured features, and required + # Combine build mode features, compiler-option features, and required # features. requested_features = ( features_for_build_modes(ctx) + diff --git a/swift/toolchains/swift_tools.bzl b/swift/toolchains/swift_tools.bzl index 6dc00d317..0107431a7 100644 --- a/swift/toolchains/swift_tools.bzl +++ b/swift/toolchains/swift_tools.bzl @@ -23,6 +23,7 @@ def _swift_tools_impl(ctx): swift_driver = ctx.file.swift_driver, swift_autolink_extract = ctx.file.swift_autolink_extract, swift_symbolgraph_extract = ctx.file.swift_symbolgraph_extract, + swift_synthesize_interface = ctx.file.swift_synthesize_interface, additional_inputs = ctx.files.additional_inputs, ), ] @@ -41,6 +42,7 @@ Example: swift_driver = "//path/to:swift-driver", swift_autolink_extract = "//path/to:swift-autolink-extract", swift_symbolgraph_extract = "//path/to:swift-symbolgraph-extract", + swift_synthesize_interface = "//path/to:swift-synthesize-interface", additional_inputs = glob("path/to/runtime/**"), ) """, @@ -61,6 +63,10 @@ Example: doc = "Label of the swift-symbolgraph-extract executable.", mandatory = True, ), + "swift_synthesize_interface": attr.label( + allow_single_file = True, + doc = "Label of the swift-synthesize-interface executable.", + ), "additional_inputs": attr.label_list( allow_files = True, doc = "List of labels to include in the input-tree when invoking these tools.", diff --git a/swift/toolchains/xcode_swift_toolchain.bzl b/swift/toolchains/xcode_swift_toolchain.bzl index a4dc82686..8b4a3860c 100644 --- a/swift/toolchains/xcode_swift_toolchain.bzl +++ b/swift/toolchains/xcode_swift_toolchain.bzl @@ -32,6 +32,7 @@ load( "SwiftInfo", "SwiftPackageConfigurationInfo", "SwiftToolchainInfo", + "SwiftToolsInfo", ) load( "//swift/internal:action_names.bzl", @@ -195,6 +196,7 @@ def _sdk_developer_framework_dir(apple_toolchain, target_triple): return paths.join(apple_toolchain.sdk_dir(), "Developer/Library/Frameworks") def _swift_linkopts_cc_info( + additional_inputs, apple_toolchain, target_triple, toolchain_label, @@ -207,6 +209,8 @@ def _swift_linkopts_cc_info( code will link to the standard libraries correctly. Args: + additional_inputs: Files from the hermetic Swift toolchain required by + the linker. apple_toolchain: The `apple_common.apple_toolchain()` object. target_triple: The target triple `struct`. toolchain_label: The label of the Swift toolchain that will act as the @@ -302,6 +306,7 @@ def _swift_linkopts_cc_info( linking_context = cc_common.create_linking_context( linker_inputs = depset([ cc_common.create_linker_input( + additional_inputs = depset(additional_inputs), owner = toolchain_label, user_link_flags = depset(linkopts), ), @@ -386,6 +391,7 @@ def _all_action_configs( apple_toolchain, generated_header_rewriter, needs_resource_directory, + resource_directory, target_triple, xcode_config): """Returns the action configurations for the Swift toolchain. @@ -402,6 +408,7 @@ def _all_action_configs( desired. needs_resource_directory: If True, the toolchain needs the resource directory passed explicitly to the compiler. + resource_directory: The Swift resource directory. target_triple: The triple of the platform being targeted. xcode_config: The Xcode configuration. @@ -543,7 +550,7 @@ def _all_action_configs( configurators = [ add_arg( "-resource-dir", - _resource_dir_path(apple_toolchain), + resource_directory, ), ], ), @@ -560,7 +567,7 @@ def _all_action_configs( configurators = [ add_arg( "-resource-dir", - _resource_dir_path(apple_toolchain), + resource_directory, ), add_arg( "-target-sdk-version", @@ -586,7 +593,7 @@ def _all_action_configs( add_arg( "-plugin-path", "{resource_dir}/host/plugins/testing".format( - resource_dir = _resource_dir_path(apple_toolchain), + resource_dir = resource_directory, ), ), ], @@ -611,21 +618,25 @@ def _swift_compile_resource_set(_os, inputs_size): return {"cpu": 1, "memory": 200. + inputs_size * 0.015} def _all_tool_configs( + additional_tools, custom_toolchain, env, execution_requirements, swift_executable, + swift_tools, toolchain_root, xcode_config): """Returns the tool configurations for the Swift toolchain. Args: + additional_tools: Files required by every Swift tool action. custom_toolchain: The bundle identifier of a custom Swift toolchain, if one was requested. env: The environment variables to set when launching tools. execution_requirements: The execution requirements for tools. swift_executable: A custom Swift driver executable to be used during the build, if provided. + swift_tools: The hermetic Swift tools, if provided. toolchain_root: The root directory of the toolchain, if provided. xcode_config: The Xcode configuration. @@ -641,6 +652,8 @@ def _all_tool_configs( env["TOOLCHAINS"] = custom_toolchain def _driver_config(*, mode): + if swift_tools: + return None return { "mode": mode, "swift_executable": swift_executable, @@ -648,7 +661,9 @@ def _all_tool_configs( } tool_config = ToolConfigInfo( + additional_tools = additional_tools, driver_config = _driver_config(mode = "swiftc"), + executable = swift_tools.swift_driver if swift_tools else None, env = env, execution_requirements = execution_requirements, resource_set = _swift_compile_resource_set, @@ -662,7 +677,9 @@ def _all_tool_configs( SWIFT_ACTION_DUMP_AST: tool_config, SWIFT_ACTION_PRECOMPILE_C_MODULE: ( ToolConfigInfo( + additional_tools = additional_tools, driver_config = _driver_config(mode = "swiftc"), + executable = swift_tools.swift_driver if swift_tools else None, env = env, execution_requirements = execution_requirements, use_param_file = True, @@ -671,7 +688,9 @@ def _all_tool_configs( ), SWIFT_ACTION_COMPILE_MODULE_INTERFACE: ( ToolConfigInfo( + additional_tools = additional_tools, driver_config = _driver_config(mode = "swiftc"), + executable = swift_tools.swift_driver if swift_tools else None, args = ["-frontend"], env = env, execution_requirements = execution_requirements, @@ -682,9 +701,11 @@ def _all_tool_configs( ), SWIFT_ACTION_SYMBOL_GRAPH_EXTRACT: ( ToolConfigInfo( + additional_tools = additional_tools, driver_config = _driver_config( mode = "swift-symbolgraph-extract", ), + executable = swift_tools.swift_symbolgraph_extract if swift_tools else None, env = env, execution_requirements = execution_requirements, use_param_file = True, @@ -693,10 +714,16 @@ def _all_tool_configs( ), } - # swift-synthesize-interface is only available in Xcode 16.3 and later. - if _is_xcode_at_least_version(xcode_config, "16.3"): + # Xcode supplies swift-synthesize-interface starting with Xcode 16.3. A + # hermetic toolchain declares the executable directly. + if ( + (swift_tools and swift_tools.swift_synthesize_interface) or + (not swift_tools and _is_xcode_at_least_version(xcode_config, "16.3")) + ): tool_configs[SWIFT_ACTION_SYNTHESIZE_INTERFACE] = ToolConfigInfo( + additional_tools = additional_tools, driver_config = _driver_config(mode = "swift-synthesize-interface"), + executable = swift_tools.swift_synthesize_interface if swift_tools else None, env = env, execution_requirements = execution_requirements, use_param_file = True, @@ -810,10 +837,19 @@ def _xcode_swift_toolchain_impl(ctx): # # To use a "standard" custom toolchain built using the full Swift build # script, set the `TOOLCHAINS` envirinment variable as shown below. + swift_tools = ctx.attr.swift_tools[SwiftToolsInfo] if ctx.attr.swift_tools else None + if swift_tools and ctx.attr.swift_executable: + fail("`swift_executable` and `swift_tools` cannot be used concurrently. " + + "Use `swift_tools` for a hermetic toolchain.") + swift_executable = get_swift_executable_for_toolchain(ctx) toolchain_root = ctx.var.get("SWIFT_USE_TOOLCHAIN_ROOT") custom_toolchain = ctx.configuration.default_shell_env.get("TOOLCHAINS") + if swift_tools and (toolchain_root or custom_toolchain): + fail("SWIFT_USE_TOOLCHAIN_ROOT and TOOLCHAINS cannot override a hermetic " + + "Swift toolchain.") + custom_xcode_toolchain_root = None if toolchain_root and custom_toolchain: fail("Do not use SWIFT_USE_TOOLCHAIN_ROOT and TOOLCHAINS" + @@ -821,11 +857,23 @@ def _xcode_swift_toolchain_impl(ctx): elif custom_toolchain: custom_xcode_toolchain_root = "__BAZEL_SWIFT_TOOLCHAIN_PATH__" + hermetic_toolchain_root = None + if swift_tools: + hermetic_toolchain_root = paths.dirname(paths.dirname(swift_tools.swift_driver.dirname)) + + link_toolchain_root = hermetic_toolchain_root or toolchain_root or custom_xcode_toolchain_root + additional_tools = [] + if ctx.file.version_file: + additional_tools.append(ctx.file.version_file) + if swift_tools: + additional_tools.extend(swift_tools.additional_inputs) + swift_linkopts_cc_info = _swift_linkopts_cc_info( + additional_inputs = swift_tools.additional_inputs if swift_tools else [], apple_toolchain = apple_toolchain, target_triple = target_triple, toolchain_label = ctx.label, - toolchain_root = toolchain_root or custom_xcode_toolchain_root, + toolchain_root = link_toolchain_root, xcode_config = xcode_config, ) @@ -848,7 +896,11 @@ def _xcode_swift_toolchain_impl(ctx): SWIFT_FEATURE_MODULE_HOME_IS_CWD, ]) - if _is_xcode_at_least_version(xcode_config, "26.4"): + supports_hermetic_swiftmodule = ( + ctx.attr.parsed_version and + apple_common.dotted_version(ctx.attr.parsed_version) >= apple_common.dotted_version("6.3") + ) if swift_tools else _is_xcode_at_least_version(xcode_config, "26.4") + if supports_hermetic_swiftmodule: requested_features.append(SWIFT_FEATURE__SUPPORTS_HERMETIC_SWIFTMODULE) # Xcode toolchains always support DEVELOPER_DIR @@ -863,22 +915,28 @@ def _xcode_swift_toolchain_impl(ctx): unsupported_features.extend(ctx.attr.default_unsupported_features) env = _xcode_env(target_triple = target_triple, xcode_config = xcode_config) - - # TODO: Remove once we drop support for Xcode 16.x. - # We set a private environment variable when using a version older than Xcode 16.3 - # which comes with Swift 6.1 which changes the hash algorithm for the index-import tool. - # When using an older version we switch to the older version of index-import. - if not _is_xcode_at_least_version(xcode_config, "16.3"): + if hermetic_toolchain_root: + env["TOOLCHAIN_PATH"] = hermetic_toolchain_root + + # Swift 6.1 changed the hash algorithm used by index-import. Select the + # index-import version from the hermetic compiler version when available. + uses_legacy_index_import = ( + not ctx.attr.parsed_version or + apple_common.dotted_version(ctx.attr.parsed_version) < apple_common.dotted_version("6.1") + ) if swift_tools else not _is_xcode_at_least_version(xcode_config, "16.3") + if uses_legacy_index_import: env["__RULES_SWIFT_USE_LEGACY_INDEX_IMPORT"] = "1" execution_requirements = xcode_config.execution_info() generated_header_rewriter = ctx.executable.generated_header_rewriter all_tool_configs = _all_tool_configs( + additional_tools = additional_tools, custom_toolchain = custom_toolchain, env = env, execution_requirements = execution_requirements, swift_executable = swift_executable, + swift_tools = swift_tools, toolchain_root = toolchain_root, xcode_config = xcode_config, ) @@ -891,7 +949,10 @@ def _xcode_swift_toolchain_impl(ctx): additional_swiftc_copts = ctx.attr.copts + swiftcopts, apple_toolchain = apple_toolchain, generated_header_rewriter = generated_header_rewriter, - needs_resource_directory = swift_executable or toolchain_root, + needs_resource_directory = (swift_executable or toolchain_root) and not swift_tools, + resource_directory = ( + paths.join(hermetic_toolchain_root, "usr/lib/swift") if hermetic_toolchain_root else _resource_dir_path(apple_toolchain) + ), target_triple = target_triple, xcode_config = xcode_config, ) @@ -955,7 +1016,7 @@ def _xcode_swift_toolchain_impl(ctx): for target in ctx.attr.package_configurations ], requested_features = requested_features, - runtime = depset(), + runtime = depset(ctx.files.runtime), system_modules = collect_implicit_deps_providers( [ctx.attr.system_modules] if ctx.attr.system_modules else [], ), @@ -1076,6 +1137,17 @@ configuration options that are applied to targets on a per-package basis. """, providers = [[SwiftPackageConfigurationInfo]], ), + "parsed_version": attr.string( + doc = "The Swift compiler version used by a hermetic toolchain.", + ), + "runtime": attr.label_list( + allow_files = True, + doc = "Files required when running binaries built with the Swift toolchain.", + ), + "version_file": attr.label( + allow_single_file = True, + doc = "A file containing the Swift compiler version.", + ), "const_protocols_to_gather": attr.label( default = Label( "//swift/toolchains/config:const_protocols_to_gather.json", diff --git a/test/synthesize_interface_tests.bzl b/test/synthesize_interface_tests.bzl index 05c0e4de9..fb37fc283 100644 --- a/test/synthesize_interface_tests.bzl +++ b/test/synthesize_interface_tests.bzl @@ -36,7 +36,7 @@ def synthesize_interface_test_suite(name, tags = []): provider_test( name = "{}_expected_files".format(name), expected_files = [ - "test/fixtures/synthesize_interface/c_module.synthesized.swift", + "test/fixtures/synthesize_interface/c_module.synthesized_interfaces/test_fixtures_synthesize_interface_c_module.synthesized.swift", ], field = "files", provider = "DefaultInfo", diff --git a/tools/common/BUILD b/tools/common/BUILD index bb591d266..0fb8ecc04 100644 --- a/tools/common/BUILD +++ b/tools/common/BUILD @@ -21,9 +21,6 @@ cc_library( "-std=c++17", ], }), - deps = [ - ":process", - ], ) cc_library( diff --git a/tools/common/bazel_substitutions.cc b/tools/common/bazel_substitutions.cc index b862e43ed..b6fde0f63 100644 --- a/tools/common/bazel_substitutions.cc +++ b/tools/common/bazel_substitutions.cc @@ -15,14 +15,10 @@ #include "tools/common/bazel_substitutions.h" #include -#include #include #include -#include #include -#include "tools/common/process.h" - namespace bazel_rules_swift { namespace { @@ -59,46 +55,14 @@ std::string GetAppleEnvironmentVariable(const char* name) { } std::string GetToolchainPath() { - // If TOOLCHAIN_PATH is set, we will use that as a toolchain path. - // Otherwise, we will try to derive it from DEVELOPER_DIR and TOOLCHAINS - // using xcrun by calling GetToolchainPath(). char* toolchain_path = getenv("TOOLCHAIN_PATH"); - if (toolchain_path != nullptr) { - return std::string(toolchain_path); - } - - char* toolchain_id = getenv("TOOLCHAINS"); - std::ostringstream output_stream; - int exit_code = - RunSubProcess({"/usr/bin/xcrun", "--find", "clang"}, - /*env=*/nullptr, &output_stream, /*stdout_to_stderr=*/true); - if (exit_code != 0) { - std::cerr << output_stream.str() << "Error: `TOOLCHAINS=" << toolchain_id - << "xcrun --find clang` failed with error code " << exit_code - << std::endl; - exit(EXIT_FAILURE); - } - - if (output_stream.str().empty()) { - std::cerr << "Error: TOOLCHAINS was set to '" << toolchain_id - << "' but no toolchain with that ID was found" << std::endl; - exit(EXIT_FAILURE); - } else if ((toolchain_id != nullptr) && - output_stream.str().find("XcodeDefault.xctoolchain") != - std::string::npos) { - // NOTE: Ideally xcrun would fail if the toolchain we asked for didn't exist - // but it falls back to the DEVELOPER_DIR instead, so we have to check the - // output ourselves. - std::cerr << "Error: TOOLCHAINS was set to '" << toolchain_id - << "' but the default toolchain was found, that likely means a " - "matching " - << "toolchain isn't installed" << std::endl; + if (toolchain_path == nullptr) { + std::cerr << "error: required Swift toolchain environment variable " + "'TOOLCHAIN_PATH' was not set. Please file an issue on " + "bazelbuild/rules_swift.\n"; exit(EXIT_FAILURE); } - - std::filesystem::path clang_path(output_stream.str()); - // Remove usr/bin/clang components to get the root of the custom toolchain - return clang_path.parent_path().parent_path().parent_path().string(); + return std::string(toolchain_path); } } // namespace diff --git a/tools/dump_toolchains/BUILD b/tools/dump_toolchains/BUILD deleted file mode 100644 index d5a716b40..000000000 --- a/tools/dump_toolchains/BUILD +++ /dev/null @@ -1,9 +0,0 @@ -load("@rules_shell//shell:sh_binary.bzl", "sh_binary") - -licenses(["notice"]) - -sh_binary( - name = "dump_toolchains", - srcs = ["dump_toolchains.sh"], - visibility = ["//visibility:public"], -) diff --git a/tools/dump_toolchains/dump_toolchains.sh b/tools/dump_toolchains/dump_toolchains.sh deleted file mode 100755 index c8c2dd455..000000000 --- a/tools/dump_toolchains/dump_toolchains.sh +++ /dev/null @@ -1,44 +0,0 @@ -#!/bin/bash -# -# Copyright 2019 The Bazel Authors. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -set -euo pipefail - -if [[ "$(uname)" != Darwin ]]; then - echo "error: dumping toolchains is only supported on macOS" - exit 1 -fi - -readonly toolchain_directories=( - /Library/Developer/Toolchains - ~/Library/Developer/Toolchains -) - -shopt -s nullglob -for toolchain_directory in "${toolchain_directories[@]}" -do - for toolchain in "$toolchain_directory"/*.xctoolchain - do - plist_path="$toolchain/Info.plist" - - if [[ ! -f "$plist_path" ]]; then - echo "error: '$toolchain' is missing Info.plist" - exit 1 - fi - - bundle_id=$(/usr/libexec/PlistBuddy -c "print :CFBundleIdentifier" "$plist_path") - echo "$toolchain -> $bundle_id" - done -done