diff --git a/.bazelci/presubmit.yml b/.bazelci/presubmit.yml index 1659c84f7..f658181a9 100644 --- a/.bazelci/presubmit.yml +++ b/.bazelci/presubmit.yml @@ -25,8 +25,9 @@ x_defaults: test_flags: *linux_flags test_targets: - "--" + - "//doc/..." - "//examples/..." - # - "//test/..." TODO: Enable once https://github.com/bazelbuild/continuous-integration/pull/1015 is deployed + - "//test/..." - "-//examples/apple/..." tasks: diff --git a/.bazelci/update_workspace_to_deps_heads.sh b/.bazelci/update_workspace_to_deps_heads.sh index 4b82dc75c..45681551b 100755 --- a/.bazelci/update_workspace_to_deps_heads.sh +++ b/.bazelci/update_workspace_to_deps_heads.sh @@ -33,7 +33,7 @@ load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")\ git_repository(\ \ name = "bazel_skylib",\ \ remote = "https://github.com/bazelbuild/bazel-skylib.git",\ -\ branch = "master",\ +\ branch = "main",\ )\ \ git_repository(\ diff --git a/.bazelrc b/.bazelrc new file mode 100644 index 000000000..2ae30cf59 --- /dev/null +++ b/.bazelrc @@ -0,0 +1,8 @@ +# Since there's no way to set the deployment version for swift_{binary,test}, +# this forces all targets' minimum macOS to Catalina until Bazel CI has +# upgraded their Mac machines to Big Sur. +build --macos_minimum_os=10.15 + +# Make sure no warnings slip into the C++ tools we vendor +build --copt=-Werror +build --host_copt=-Werror diff --git a/.clang-format b/.clang-format new file mode 100644 index 000000000..f6cb8ad93 --- /dev/null +++ b/.clang-format @@ -0,0 +1 @@ +BasedOnStyle: Google diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 000000000..fa3c57b57 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,9 @@ +# See CONTRIBUTING.md for instructions. +# See https://pre-commit.com for more information +# See https://pre-commit.com/hooks.html for more hooks +repos: + - repo: https://github.com/keith/pre-commit-buildifier + rev: 4.0.1.1 + hooks: + - id: buildifier + - id: buildifier-lint diff --git a/BUILD b/BUILD index 1f684e0fc..fa55c907f 100644 --- a/BUILD +++ b/BUILD @@ -11,6 +11,7 @@ filegroup( srcs = [ "WORKSPACE", "//swift:for_bazel_tests", + "//third_party:for_bazel_tests", "//tools:for_bazel_tests", ], ) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4ae040462..4d47688c2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -3,6 +3,19 @@ We'd love to accept your patches and contributions to this project. There are just a few small guidelines you need to follow. +## Formatting + +Starlark files should be formatted by buildifier. +We suggest using a pre-commit hook to automate this. +First [install pre-commit](https://pre-commit.com/#installation), +then run + +```shell +pre-commit install +``` + +Otherwise the Buildkite CI will yell at you about formatting/linting violations. + ## File or claim an issue Please let us know what you're working on if you want to change or add to the diff --git a/CONTRIBUTORS b/CONTRIBUTORS deleted file mode 100644 index a8d530dca..000000000 --- a/CONTRIBUTORS +++ /dev/null @@ -1,12 +0,0 @@ -# People who have agreed to one of the CLAs and can contribute patches. -# The AUTHORS file lists the copyright holders; this file -# lists people. For example, Google employees are listed here -# but not in AUTHORS, because Google holds the copyright. -# -# https://developers.google.com/open-source/cla/individual -# https://developers.google.com/open-source/cla/corporate -# -# Names should be added to this file as: -# Name - -Tony Allevato diff --git a/README.md b/README.md index a98b0fcb2..4e9c7c4a5 100644 --- a/README.md +++ b/README.md @@ -47,23 +47,12 @@ replacing the `urls` and `sha256` attributes with the values from the release you wish to depend on: ```python -load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository") load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") -# rules_swift and apple_support no longer support releases. If you'd like to pin -# down these dependencies to a specific commit, please add the following to the -# top of your WORKSPACE, using the commit you'd like to pin for each of the -# repositories. -git_repository( +http_archive( name = "build_bazel_rules_swift", - remote = "https://github.com/bazelbuild/rules_swift.git", - commit = "[SOME_HASH_VALUE]", -) - -git_repository( - name = "build_bazel_apple_support", - remote = "https://github.com/bazelbuild/apple_support.git", - commit = "[SOME_HASH_VALUE]", + sha256 = "653e8756001616500b110fd156694de7899278bb7480aba22b2f156438a1d810", + url = "https://github.com/bazelbuild/rules_swift/releases/download/0.22.0/rules_swift.0.22.0.tar.gz", ) load( @@ -74,18 +63,11 @@ load( swift_rules_dependencies() load( - "@build_bazel_apple_support//lib:repositories.bzl", - "apple_support_dependencies", -) - -apple_support_dependencies() - -load( - "@com_google_protobuf//:protobuf_deps.bzl", - "protobuf_deps", + "@build_bazel_rules_swift//swift:extras.bzl", + "swift_rules_extra_dependencies", ) -protobuf_deps() +swift_rules_extra_dependencies() ``` The `swift_rules_dependencies` macro creates a toolchain appropriate for your @@ -109,7 +91,7 @@ uses `clang`. `/Library/Developer/Toolchains` instead of Xcode's default. To do so, pass the following flag to Bazel: -``` +```lang-none --define=SWIFT_CUSTOM_TOOLCHAIN=toolchain.id ``` @@ -118,22 +100,27 @@ toolchain's Info.plist file. To list the available toolchains and their bundle identifiers, you can run: -``` +```command bazel run @build_bazel_rules_swift//tools/dump_toolchains ``` **Linux hosts:** At this time, Bazel uses whichever `swift` executable is encountered first on your `PATH`. +## Supporting remote builds + +To make remote builds work correctly with debugging and general +reproducibility see [this doc](doc/debuggable_remote_swift.md) + ## Future Work -* Support for building and linking to shared libraries (`.dylib`/`.so`) written +- Support for building and linking to shared libraries (`.dylib`/`.so`) written in Swift. -* Interoperability with Swift Package Manager. -* Migration to the Bazel platforms/toolchains APIs. -* Support for multiple toolchains, and support for non-Xcode toolchains on +- Interoperability with Swift Package Manager. +- Migration to the Bazel platforms/toolchains APIs. +- Support for multiple toolchains, and support for non-Xcode toolchains on macOS. -* Automatically download a Linux toolchain from [swift.org](https://swift.org) +- Automatically download a Linux toolchain from [swift.org](https://swift.org) if one is not already installed. ## Acknowledgments @@ -141,10 +128,10 @@ encountered first on your `PATH`. We gratefully acknowledge the following external packages that rules_swift depends on: -* [Apple Support for Bazel](https://github.com/bazelbuild/apple_support) (Google) -* [Bazel Skylib](https://github.com/bazelbuild/bazel-skylib) (Google) -* [JSON for Modern C++](https://github.com/nlohmann/json) (Niels Lohmann) -* [Protocol Buffers](https://github.com/protocolbuffers/protobuf) (Google) -* [Swift gRPC](https://github.com/grpc/grpc-swift) (Google) -* [Swift Protobuf](https://github.com/apple/swift-protobuf) (Apple) -* [zlib](https://www.zlib.net) (Jean-loup Gailly and Mark Adler) +- [Apple Support for Bazel](https://github.com/bazelbuild/apple_support) (Google) +- [Bazel Skylib](https://github.com/bazelbuild/bazel-skylib) (Google) +- [JSON for Modern C++](https://github.com/nlohmann/json) (Niels Lohmann) +- [Protocol Buffers](https://github.com/protocolbuffers/protobuf) (Google) +- [Swift gRPC](https://github.com/grpc/grpc-swift) (Google) +- [Swift Protobuf](https://github.com/apple/swift-protobuf) (Apple) +- [zlib](https://www.zlib.net) (Jean-loup Gailly and Mark Adler) diff --git a/WORKSPACE b/WORKSPACE index d3760f2ed..c48f7685a 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -8,19 +8,27 @@ load( swift_rules_dependencies() load( - "@build_bazel_apple_support//lib:repositories.bzl", - "apple_support_dependencies", + "@build_bazel_rules_swift//swift:extras.bzl", + "swift_rules_extra_dependencies", ) -apple_support_dependencies() - -load( - "@com_google_protobuf//:protobuf_deps.bzl", - "protobuf_deps", -) - -protobuf_deps() +swift_rules_extra_dependencies() load("@bazel_skylib//:workspace.bzl", "bazel_skylib_workspace") bazel_skylib_workspace() + +# For API doc generation +# This is a dev dependency, users should not need to install it +# so we declare it in the WORKSPACE +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") + +http_archive( + name = "io_bazel_stardoc", + patches = ["//doc:stardoc.pr103.patch"], + sha256 = "f89bda7b6b696c777b5cf0ba66c80d5aa97a6701977d43789a9aee319eef71e8", + strip_prefix = "stardoc-d93ee5347e2d9c225ad315094507e018364d5a67", + urls = [ + "https://github.com/bazelbuild/stardoc/archive/d93ee5347e2d9c225ad315094507e018364d5a67.tar.gz", + ], +) diff --git a/doc/BUILD.bazel b/doc/BUILD.bazel new file mode 100644 index 000000000..72eaced5e --- /dev/null +++ b/doc/BUILD.bazel @@ -0,0 +1,155 @@ +load("@bazel_skylib//rules:write_file.bzl", "write_file") +load("@bazel_skylib//rules:diff_test.bzl", "diff_test") +load("@io_bazel_stardoc//stardoc:stardoc.bzl", "stardoc") + +_DOC_SRCS = { + "api": [ + "swift_common", + ], + "aspects": [ + "swift_usage_aspect", + ], + "providers": [ + "SwiftInfo", + "SwiftToolchainInfo", + "SwiftProtoInfo", + "SwiftUsageInfo", + ], + "rules": [ + "swift_binary", + "swift_c_module", + "swift_feature_allowlist", + "swift_grpc_library", + "swift_import", + "swift_library", + "swift_module_alias", + "swift_proto_library", + "swift_test", + ], +} + +write_file( + name = "api_header", + out = "api_header.vm", + content = [ + "", + "# Build API", + "", + "The `swift_common` module provides API access to the behavior implemented", + "by the Swift build rules, so that other custom rules can invoke Swift", + "compilation and/or linking as part of their implementation.", + ], +) + +write_file( + name = "aspects_header", + out = "aspects_header.vm", + content = [ + "", + "# Aspects", + "", + "The aspects described below are used within the build rule implementations.", + "Clients interested in writing custom rules that interface with the rules/provides", + "in this package might needs them to provide some of the same information.", + "", + "On this page:", + "", + ] + [" * [{0}](#{0})".format(r) for r in _DOC_SRCS["aspects"]] + [ + "", + ], +) + +write_file( + name = "rules_header", + out = "rules_header.vm", + content = [ + "", + "", + "${moduleDocstring}", + "On this page:", + "", + ] + [" * [{0}](#{0})".format(r) for r in _DOC_SRCS["rules"]] + [ + "", + ], +) + +write_file( + name = "providers_header", + out = "providers_header.vm", + content = [ + "", + "", + "The providers described below are propagated and required by various Swift", + "build rules. Clients interested in writing custom rules that interface", + "with the rules in this package should use these providers to communicate", + "with the Swift build rules as needed.", + "", + "On this page:", + "", + ] + [" * [{0}](#{0})".format(r) for r in _DOC_SRCS["providers"]] + [ + "", + ], +) + +write_file( + name = "setup_header", + out = "setup_header.vm", + content = [ + "", + "# Workspace Setup", + ], +) + +[ + stardoc( + name = file + "_doc", + out = file + ".md_", + header_template = file + "_header.vm", + input = "//swift:swift.bzl", + symbol_names = symbols, + deps = ["//swift"], + ) + for [ + file, + symbols, + ] in _DOC_SRCS.items() +] + +stardoc( + name = "setup_doc", + out = "setup.md_", + header_template = "setup_header.vm", + input = "//swift:repositories.bzl", + deps = ["//swift:repositories"], +) + +# To make these tests pass, run +# bazel run //doc:update +[ + diff_test( + name = "test_" + file, + file1 = file + ".md_", + file2 = file + ".md", + ) + for file in _DOC_SRCS.keys() + ["setup"] +] + +write_file( + name = "gen_update", + out = "update.sh", + content = [ + "#!/usr/bin/env bash", + "cd $BUILD_WORKSPACE_DIRECTORY", + ] + [ + "cp -fv bazel-bin/doc/{0}.md_ doc/{0}.md".format( + file, + ) + for file in _DOC_SRCS.keys() + ["setup"] + ], +) + +sh_binary( + name = "update", + srcs = ["update.sh"], + data = [file + ".md_" for file in _DOC_SRCS.keys()], +) diff --git a/doc/api.md b/doc/api.md index 656c2e6e3..7cfdff859 100644 --- a/doc/api.md +++ b/doc/api.md @@ -1,70 +1,39 @@ + # Build API - - - - The `swift_common` module provides API access to the behavior implemented by the Swift build rules, so that other custom rules can invoke Swift compilation and/or linking as part of their implementation. + -On this page: - - * [swift_common.cc_feature_configuration](#swift_common.cc_feature_configuration) - * [swift_common.compilation_attrs](#swift_common.compilation_attrs) - * [swift_common.compile](#swift_common.compile) - * [swift_common.configure_features](#swift_common.configure_features) - * [swift_common.create_clang_module](#swift_common.create_clang_module) - * [swift_common.create_module](#swift_common.create_module) - * [swift_common.create_swift_info](#swift_common.create_swift_info) - * [swift_common.create_swift_module](#swift_common.create_swift_module) - * [swift_common.derive_module_name](#swift_common.derive_module_name) - * [swift_common.get_implicit_deps](#swift_common.get_implicit_deps) - * [swift_common.is_enabled](#swift_common.is_enabled) - * [swift_common.library_rule_attrs](#swift_common.library_rule_attrs) - * [swift_common.precompile_clang_module](#swift_common.precompile_clang_module) - * [swift_common.swift_clang_module_aspect](#swift_common.swift_clang_module_aspect) - * [swift_common.swift_runtime_linkopts](#swift_common.swift_runtime_linkopts) - * [swift_common.toolchain_attrs](#swift_common.toolchain_attrs) - - ## swift_common.cc_feature_configuration -
-swift_common.cc_feature_configuration(feature_configuration)
+
+swift_common.cc_feature_configuration(feature_configuration)
 
Returns the C++ feature configuration in a Swift feature configuration. - -### Arguments - - - - - - - - - - - - -
feature_configuration

Required

The Swift feature configuration, as returned from -swift_common.configure_features.

- - -### Returns +**PARAMETERS** + + +| Name | Description | Default Value | +| :------------- | :------------- | :------------- | +| feature_configuration | The Swift feature configuration, as returned from swift_common.configure_features. | none | + +**RETURNS** A C++ `FeatureConfiguration` value (see -[`cc_common.configure_features`](https://docs.bazel.build/versions/master/skylark/lib/cc_common.html#configure_features) -for more information). + [`cc_common.configure_features`](https://docs.bazel.build/versions/master/skylark/lib/cc_common.html#configure_features) + for more information). + + + - ## swift_common.compilation_attrs -
-swift_common.compilation_attrs(additional_deps_aspects=[])
+
+swift_common.compilation_attrs(additional_deps_aspects, requires_srcs)
 
Returns an attribute dictionary for rules that compile Swift code. @@ -93,172 +62,97 @@ API: Each of the attribute functions in the list above also contains the attributes from the earlier items in the list. - -### Arguments - - - - - - - - - - - - -
additional_deps_aspects

Optional; default is []

A list of additional aspects that should be -applied to deps. Defaults to the empty list. These must be passed -by the individual rules to avoid potential circular dependencies -between the API and the aspects; the API loaded the aspects -directly, then those aspects would not be able to load the API.

- - -### Returns + +**PARAMETERS** + + +| Name | Description | Default Value | +| :------------- | :------------- | :------------- | +| additional_deps_aspects | A list of additional aspects that should be applied to deps. Defaults to the empty list. These must be passed by the individual rules to avoid potential circular dependencies between the API and the aspects; the API loaded the aspects directly, then those aspects would not be able to load the API. | [] | +| requires_srcs | Indicates whether the srcs attribute should be marked as mandatory and non-empty. Defaults to True. | True | + +**RETURNS** A new attribute dictionary that can be added to the attributes of a -custom build rule to provide a similar interface to `swift_binary`, -`swift_library`, and `swift_test`. + custom build rule to provide a similar interface to `swift_binary`, + `swift_library`, and `swift_test`. + + + - ## swift_common.compile -
-swift_common.compile(*, actions, feature_configuration, module_name, srcs, swift_toolchain,
-target_name, additional_inputs=[], bin_dir=None, copts=[], defines=[], deps=[],
-generated_header_name=None, genfiles_dir=None)
+
+swift_common.compile(actions, feature_configuration, module_name, srcs, swift_toolchain,
+                     target_name, workspace_name, additional_inputs, bin_dir, copts, defines, deps,
+                     generated_header_name, genfiles_dir, private_deps)
 
Compiles a Swift module. - -### Arguments - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
actions

Required

The context's actions object.

feature_configuration

Required

A feature configuration obtained from -swift_common.configure_features.

module_name

Required

The name of the Swift module being compiled. This must be -present and valid; use swift_common.derive_module_name to generate -a default from the target's label if needed.

srcs

Required

The Swift source files to compile.

swift_toolchain

Required

The SwiftToolchainInfo provider of the toolchain.

target_name

Required

The name of the target for which the code is being -compiled, which is used to determine unique file paths for the -outputs.

additional_inputs

Optional; default is []

A list of Files representing additional input files -that need to be passed to the Swift compile action because they are -referenced by compiler flags.

bin_dir

Optional; default is None

The Bazel *-bin directory root. If provided, its path is used -to store the cache for modules precompiled by Swift's ClangImporter, -and it is added to ClangImporter's header search paths for -compatibility with Bazel's C++ and Objective-C rules which support -includes of generated headers from that location.

copts

Optional; default is []

A list of compiler flags that apply to the target being built. -These flags, along with those from Bazel's Swift configuration -fragment (i.e., --swiftcopt command line flags) are scanned to -determine whether whole module optimization is being requested, -which affects the nature of the output files.

defines

Optional; default is []

Symbols that should be defined by passing -D to the compiler.

deps

Optional; default is []

Dependencies of the target being compiled. These targets must -propagate one of the following providers: CcInfo, SwiftInfo, or -apple_common.Objc.

generated_header_name

Optional; default is None

The name of the Objective-C generated header that -should be generated for this module. If omitted, the name -${target_name}-Swift.h will be used.

genfiles_dir

Optional; default is None

The Bazel *-genfiles directory root. If provided, its -path is added to ClangImporter's header search paths for -compatibility with Bazel's C++ and Objective-C rules which support -inclusions of generated headers from that location.

- - -### Returns +**PARAMETERS** + + +| Name | Description | Default Value | +| :------------- | :------------- | :------------- | +| actions | The context's actions object. | none | +| feature_configuration | A feature configuration obtained from swift_common.configure_features. | none | +| module_name | The name of the Swift module being compiled. This must be present and valid; use swift_common.derive_module_name to generate a default from the target's label if needed. | none | +| srcs | The Swift source files to compile. | none | +| swift_toolchain | The SwiftToolchainInfo provider of the toolchain. | none | +| target_name | The name of the target for which the code is being compiled, which is used to determine unique file paths for the outputs. | none | +| workspace_name | The name of the workspace for which the code is being compiled, which is used to determine unique file paths for some outputs. | none | +| additional_inputs | A list of Files representing additional input files that need to be passed to the Swift compile action because they are referenced by compiler flags. | [] | +| bin_dir | The Bazel *-bin directory root. If provided, its path is used to store the cache for modules precompiled by Swift's ClangImporter, and it is added to ClangImporter's header search paths for compatibility with Bazel's C++ and Objective-C rules which support includes of generated headers from that location. | None | +| copts | A list of compiler flags that apply to the target being built. These flags, along with those from Bazel's Swift configuration fragment (i.e., --swiftcopt command line flags) are scanned to determine whether whole module optimization is being requested, which affects the nature of the output files. | [] | +| defines | Symbols that should be defined by passing -D to the compiler. | [] | +| deps | Non-private dependencies of the target being compiled. These targets are used as dependencies of both the Swift module being compiled and the Clang module for the generated header. These targets must propagate one of the following providers: CcInfo, SwiftInfo, or apple_common.Objc. | [] | +| generated_header_name | The name of the Objective-C generated header that should be generated for this module. If omitted, no header will be generated. | None | +| genfiles_dir | The Bazel *-genfiles directory root. If provided, its path is added to ClangImporter's header search paths for compatibility with Bazel's C++ and Objective-C rules which support inclusions of generated headers from that location. | None | +| private_deps | Private (implementation-only) dependencies of the target being compiled. These are only used as dependencies of the Swift module, not of the Clang module for the generated header. These targets must propagate one of the following providers: CcInfo, SwiftInfo, or apple_common.Objc. | [] | + +**RETURNS** A `struct` containing the following fields: -* `generated_header`: A `File` representing the Objective-C header - that was generated for the compiled module. If no header was - generated, this field will be None. -* `generated_header_module_map`: A `File` representing the module map - that was generated to correspond to the generated Objective-C - header. If no module map was generated, this field will be None. -* `indexstore`: A `File` representing the directory that contains the - index store data generated by the compiler if index-while-building - is enabled. May be None if no indexing was requested. -* `linker_flags`: A list of strings representing additional flags that - should be passed to the linker when linking these objects into a - binary. If there are none, this field will always be an empty list, - never None. -* `linker_inputs`: A list of `File`s representing additional input - files (such as those referenced in `linker_flags`) that need to be - available to the link action when linking these objects into a - binary. If there are none, this field will always be an empty list, - never None. -* `object_files`: A list of `.o` files that were produced by the - compiler. -* `stats_directory`: A `File` representing the directory that contains - the timing statistics emitted by the compiler. If no stats were - requested, this field will be None. -* `swiftdoc`: The `.swiftdoc` file that was produced by the compiler. -* `swiftinterface`: The `.swiftinterface` file that was produced by - the compiler. If no interface file was produced (because the - toolchain does not support them or it was not requested), this field - will be None. -* `swiftmodule`: The `.swiftmodule` file that was produced by the - compiler. - - + * `generated_header`: A `File` representing the Objective-C header + that was generated for the compiled module. If no header was + generated, this field will be None. + * `generated_header_module_map`: A `File` representing the module map + that was generated to correspond to the generated Objective-C + header. If no module map was generated, this field will be None. + * `indexstore`: A `File` representing the directory that contains the + index store data generated by the compiler if index-while-building + is enabled. May be None if no indexing was requested. + * `linker_flags`: A list of strings representing additional flags that + should be passed to the linker when linking these objects into a + binary. If there are none, this field will always be an empty list, + never None. + * `linker_inputs`: A list of `File`s representing additional input + files (such as those referenced in `linker_flags`) that need to be + available to the link action when linking these objects into a + binary. If there are none, this field will always be an empty list, + never None. + * `object_files`: A list of `.o` files that were produced by the + compiler. + * `precompiled_module`: A `File` representing the explicit module + (`.pcm`) of the Clang module for the generated header, or `None` if + no explicit module was generated. + * `swiftdoc`: The `.swiftdoc` file that was produced by the compiler. + * `swiftinterface`: The `.swiftinterface` file that was produced by + the compiler. If no interface file was produced (because the + toolchain does not support them or it was not requested), this field + will be None. + * `swiftmodule`: The `.swiftmodule` file that was produced by the + compiler. + + + + ## swift_common.configure_features -
-swift_common.configure_features(ctx, swift_toolchain, *, requested_features=[],
-unsupported_features=[])
+
+swift_common.configure_features(ctx, swift_toolchain, requested_features, unsupported_features)
 
Creates a feature configuration to be passed to Swift build APIs. @@ -269,99 +163,55 @@ inside the Swift one. Users who need to call C++ APIs that require a feature configuration can extract it by calling `swift_common.cc_feature_configuration(feature_configuration)`. - -### Arguments - - - - - - - - - - - - - - - - - - - - - - - - -
ctx

Required

The rule context.

swift_toolchain

Required

The SwiftToolchainInfo provider of the toolchain -being used to build. The C++ toolchain associated with the Swift -toolchain is used to create the underlying C++ feature -configuration.

requested_features

Optional; default is []

The list of features to be enabled. This is -typically obtained using the ctx.features field in a rule -implementation function.

unsupported_features

Optional; default is []

The list of features that are unsupported by the -current rule. This is typically obtained using the -ctx.disabled_features field in a rule implementation function.

- - -### Returns + +**PARAMETERS** + + +| Name | Description | Default Value | +| :------------- | :------------- | :------------- | +| ctx | The rule context. | none | +| swift_toolchain | The SwiftToolchainInfo provider of the toolchain being used to build. This is used to determine features that are enabled by default or unsupported by the toolchain, and the C++ toolchain associated with the Swift toolchain is used to create the underlying C++ feature configuration. | none | +| requested_features | The list of features to be enabled. This is typically obtained using the ctx.features field in a rule implementation function. | [] | +| unsupported_features | The list of features that are unsupported by the current rule. This is typically obtained using the ctx.disabled_features field in a rule implementation function. | [] | + +**RETURNS** An opaque value representing the feature configuration that can be -passed to other `swift_common` functions. + passed to other `swift_common` functions. Note that the structure of + this value should otherwise not be relied on or inspected directly. + + + - ## swift_common.create_clang_module -
-swift_common.create_clang_module(*, compilation_context, module_map, precompiled_module=None)
+
+swift_common.create_clang_module(compilation_context, module_map, precompiled_module)
 
Creates a value representing a Clang module used as a Swift dependency. - -### Arguments - - - - - - - - - - - - - - - - - - - - -
compilation_context

Required

A CcCompilationContext that contains the header -files, include paths, and other context necessary to compile targets -that depend on this module (if using the text module map instead of -the precompiled module).

module_map

Required

A File representing the text module map file that defines -this module.

precompiled_module

Optional; default is None

A File representing the precompiled module (.pcm -file) if one was emitted for the module. This may be None if no -explicit module was built for the module; in that case, targets that -depend on the module will fall back to the text module map and -headers.

- - -### Returns +**PARAMETERS** + + +| Name | Description | Default Value | +| :------------- | :------------- | :------------- | +| compilation_context | A CcCompilationContext that contains the header files, include paths, and other context necessary to compile targets that depend on this module (if using the text module map instead of the precompiled module). | none | +| module_map | The text module map file that defines this module. This argument may be specified as a File or as a string; in the latter case, it is assumed to be the path to a file that cannot be provided as an action input because it is outside the workspace (for example, the module map for a module from an Xcode SDK). | none | +| precompiled_module | A File representing the precompiled module (.pcm file) if one was emitted for the module. This may be None if no explicit module was built for the module; in that case, targets that depend on the module will fall back to the text module map and headers. | None | + +**RETURNS** A `struct` containing the `compilation_context`, `module_map`, and -`precompiled_module` fields provided as arguments. + `precompiled_module` fields provided as arguments. + + + - ## swift_common.create_module -
-swift_common.create_module(name, *, clang=None, swift=None)
+
+swift_common.create_module(name, clang, is_system, swift)
 
Creates a value containing Clang/Swift module artifacts of a dependency. @@ -371,48 +221,29 @@ valid for both to be present; this is the case for most Swift modules, which provide both Swift module artifacts as well as a generated header/module map for Objective-C targets to depend on. - -### Arguments - - - - - - - - - - - - - - - - - - - - -
name

Required

The name of the module.

clang

Optional; default is None

A value returned by swift_common.create_clang_module that -contains artifacts related to Clang modules, such as a module map or -precompiled module. This may be None if the module is a pure Swift -module with no generated Objective-C interface.

swift

Optional; default is None

A value returned by swift_common.create_swift_module that -contains artifacts related to Swift modules, such as the -.swiftmodule, .swiftdoc, and/or .swiftinterface files emitted -by the compiler. This may be None if the module is a pure -C/Objective-C module.

- - -### Returns - -A `struct` containing the `name`, `clang`, and `swift` fields provided -as arguments. - - + +**PARAMETERS** + + +| Name | Description | Default Value | +| :------------- | :------------- | :------------- | +| name | The name of the module. | none | +| clang | A value returned by swift_common.create_clang_module that contains artifacts related to Clang modules, such as a module map or precompiled module. This may be None if the module is a pure Swift module with no generated Objective-C interface. | None | +| is_system | Indicates whether the module is a system module. The default value is False. System modules differ slightly from non-system modules in the way that they are passed to the compiler. For example, non-system modules have their Clang module maps passed to the compiler in both implicit and explicit module builds. System modules, on the other hand, do not have their module maps passed to the compiler in implicit module builds because there is currently no way to indicate that modules declared in a file passed via -fmodule-map-file should be treated as system modules even if they aren't declared with the [system] attribute, and some system modules may not build cleanly with respect to warnings otherwise. Therefore, it is assumed that any module with is_system == True must be able to be found using import search paths in order for implicit module builds to succeed. | False | +| swift | A value returned by swift_common.create_swift_module that contains artifacts related to Swift modules, such as the .swiftmodule, .swiftdoc, and/or .swiftinterface files emitted by the compiler. This may be None if the module is a pure C/Objective-C module. | None | + +**RETURNS** + +A `struct` containing the `name`, `clang`, `is_system`, and `swift` + fields provided as arguments. + + + + ## swift_common.create_swift_info -
-swift_common.create_swift_info(*, module_name=None, modules=[], swift_infos=[], swift_version=None)
+
+swift_common.create_swift_info(direct_swift_infos, modules, swift_infos)
 
Creates a new `SwiftInfo` provider with the given values. @@ -423,175 +254,147 @@ may not be interested in and ensures that the direct and transitive fields are set consistently. This function can also be used to do a simple merge of `SwiftInfo` -providers, by leaving all of the arguments except for `swift_infos` as their -empty defaults. In that case, the returned provider will not represent a -true Swift module; it is merely a "collector" for other dependencies. - - -### Arguments - - - - - - - - - - - - - - - - - - - - - - - - -
module_name

Optional; default is None

This argument is deprecated. The module name(s) should be -specified in the values passed to the modules argument.

modules

Optional; default is []

A list of values (as returned by swift_common.create_module) -that represent Clang and/or Swift module artifacts that are direct -outputs of the target being built.

swift_infos

Optional; default is []

A list of SwiftInfo providers from dependencies, whose -transitive fields should be merged into the new one. If omitted, no -transitive data is collected.

swift_version

Optional; default is None

A string containing the value of the -swift-version -flag used when compiling this target, or None (the default) if it -was not set or is not relevant.

- - -### Returns +providers, by leaving the `modules` argument unspecified. In that case, the +returned provider will not represent a true Swift module; it is merely a +"collector" for other dependencies. + + +**PARAMETERS** + + +| Name | Description | Default Value | +| :------------- | :------------- | :------------- | +| direct_swift_infos | A list of SwiftInfo providers from dependencies whose direct modules should be treated as direct modules in the resulting provider, in addition to their transitive modules being merged. | [] | +| modules | A list of values (as returned by swift_common.create_module) that represent Clang and/or Swift module artifacts that are direct outputs of the target being built. | [] | +| swift_infos | A list of SwiftInfo providers from dependencies whose transitive modules should be merged into the resulting provider. | [] | + +**RETURNS** A new `SwiftInfo` provider with the given values. - + + + +## swift_common.create_swift_interop_info + +
+swift_common.create_swift_interop_info(module_map, module_name, requested_features, swift_infos,
+                                       unsupported_features)
+
+ +Returns a provider that lets a target expose C/Objective-C APIs to Swift. + +The provider returned by this function allows custom build rules written in +Starlark to be uninvolved with much of the low-level machinery involved in +making a Swift-compatible module. Such a target should propagate a `CcInfo` +provider whose compilation context contains the headers that it wants to +make into a module, and then also propagate the provider returned from this +function. + +The simplest usage is for a custom rule to call +`swift_common.create_swift_interop_info` passing it only the list of +`SwiftInfo` providers from its dependencies; this tells +`swift_clang_module_aspect` to derive the module name from the target label +and create a module map using the headers from the compilation context. + +If the custom rule has reason to provide its own module name or module map, +then it can do so using the `module_name` and `module_map` arguments. + +When a rule returns this provider, it must provide the full set of +`SwiftInfo` providers from dependencies that will be merged with the one +that `swift_clang_module_aspect` creates for the target itself; the aspect +will not do so automatically. This allows the rule to not only add extra +dependencies (such as support libraries from implicit attributes) but also +exclude dependencies if necessary. + + +**PARAMETERS** + + +| Name | Description | Default Value | +| :------------- | :------------- | :------------- | +| module_map | A File representing an existing module map that should be used to represent the module, or None (the default) if the module map should be generated based on the headers in the target's compilation context. If this argument is provided, then module_name must also be provided. | None | +| module_name | A string denoting the name of the module, or None (the default) if the name should be derived automatically from the target label. | None | +| requested_features | A list of features (empty by default) that should be requested for the target, which are added to those supplied in the features attribute of the target. These features will be enabled unless they are otherwise marked as unsupported (either on the target or by the toolchain). This allows the rule implementation to have additional control over features that should be supported by default for all instances of that rule as if it were creating the feature configuration itself; for example, a rule can request that swift.emit_c_module always be enabled for its targets even if it is not explicitly enabled in the toolchain or on the target directly. | [] | +| swift_infos | A list of SwiftInfo providers from dependencies, which will be merged with the new SwiftInfo created by the aspect. | [] | +| unsupported_features | A list of features (empty by default) that should be considered unsupported for the target, which are added to those supplied as negations in the features attribute. This allows the rule implementation to have additional control over features that should be disabled by default for all instances of that rule as if it were creating the feature configuration itself; for example, a rule that processes frameworks with headers that do not follow strict layering can request that swift.strict_module always be disabled for its targets even if it is enabled by default in the toolchain. | [] | + +**RETURNS** + +A provider whose type/layout is an implementation detail and should not + be relied upon. + + + + ## swift_common.create_swift_module -
-swift_common.create_swift_module(*, swiftdoc, swiftmodule, defines=[], swiftinterface=None)
+
+swift_common.create_swift_module(swiftdoc, swiftmodule, defines, swiftinterface)
 
Creates a value representing a Swift module use as a Swift dependency. - -### Arguments - - - - - - - - - - - - - - - - - - - - - - - - -
swiftdoc

Required

The .swiftdoc file emitted by the compiler for this module.

swiftmodule

Required

The .swiftmodule file emitted by the compiler for this -module.

defines

Optional; default is []

A list of defines that will be provided as copts to targets -that depend on this module. If omitted, the empty list will be used.

swiftinterface

Optional; default is None

The .swiftinterface file emitted by the compiler for -this module. May be None if no module interface file was emitted.

- - -### Returns +**PARAMETERS** + + +| Name | Description | Default Value | +| :------------- | :------------- | :------------- | +| swiftdoc | The .swiftdoc file emitted by the compiler for this module. | none | +| swiftmodule | The .swiftmodule file emitted by the compiler for this module. | none | +| defines | A list of defines that will be provided as copts to targets that depend on this module. If omitted, the empty list will be used. | [] | +| swiftinterface | The .swiftinterface file emitted by the compiler for this module. May be None if no module interface file was emitted. | None | + +**RETURNS** A `struct` containing the `defines`, `swiftdoc`, `swiftmodule`, and -`swiftinterface` fields provided as arguments. + `swiftinterface` fields provided as arguments. + + + - ## swift_common.derive_module_name -
-swift_common.derive_module_name(*args)
+
+swift_common.derive_module_name(args)
 
Returns a derived module name from the given build label. For targets whose module name is not explicitly specified, the module name -is computed by creating an underscore-delimited string from the components -of the label, replacing any non-identifier characters also with underscores. - -This mapping is not intended to be reversible. - - -### Arguments - - - - - - - - - - - - -
*args

Either a single argument of type Label, or two arguments of -type str where the first argument is the package name and the -second argument is the target name.

- - -### Returns +is computed using the following algorithm: + +* The package and name components of the label are considered separately. + All _interior_ sequences of non-identifier characters (anything other + than `a-z`, `A-Z`, `0-9`, and `_`) are replaced by a single underscore + (`_`). Any leading or trailing non-identifier characters are dropped. +* If the package component is non-empty after the above transformation, + it is joined with the transformed name component using an underscore. + Otherwise, the transformed name is used by itself. +* If this would result in a string that begins with a digit (`0-9`), an + underscore is prepended to make it identifier-safe. + +This mapping is intended to be fairly predictable, but not reversible. + + +**PARAMETERS** + + +| Name | Description | Default Value | +| :------------- | :------------- | :------------- | +| args | Either a single argument of type Label, or two arguments of type str where the first argument is the package name and the second argument is the target name. | none | + +**RETURNS** The module name derived from the label. - -## swift_common.get_implicit_deps -
-swift_common.get_implicit_deps(feature_configuration, swift_toolchain)
-
+ -Gets the list of implicit dependencies from the toolchain. - - -### Arguments - - - - - - - - - - - - - - - - -
feature_configuration

Required

The feature configuration, which determines -whether optional implicit dependencies are included.

swift_toolchain

Required

The Swift toolchain.

- - -### Returns - -A list of targets that should be treated as implicit dependencies of -the toolchain under the given feature configuration. - - ## swift_common.is_enabled -
-swift_common.is_enabled(feature_configuration, feature_name)
+
+swift_common.is_enabled(feature_configuration, feature_name)
 
Returns `True` if the feature is enabled in the feature configuration. @@ -600,37 +403,26 @@ This function handles both Swift-specific features and C++ features so that users do not have to manually extract the C++ configuration in order to check it. - -### Arguments - - - - - - - - - - - - - - - - -
feature_configuration

Required

The Swift feature configuration, as returned by -swift_common.configure_features.

feature_name

Required

The name of the feature to check.

- - -### Returns + +**PARAMETERS** + + +| Name | Description | Default Value | +| :------------- | :------------- | :------------- | +| feature_configuration | The Swift feature configuration, as returned by swift_common.configure_features. | none | +| feature_name | The name of the feature to check. | none | + +**RETURNS** `True` if the given feature is enabled in the feature configuration. - + + + ## swift_common.library_rule_attrs -
-swift_common.library_rule_attrs(additional_deps_aspects=[])
+
+swift_common.library_rule_attrs(additional_deps_aspects, requires_srcs)
 
Returns an attribute dictionary for `swift_library`-like rules. @@ -661,230 +453,61 @@ API: Each of the attribute functions in the list above also contains the attributes from the earlier items in the list. - -### Arguments - - - - - - - - - - - - -
additional_deps_aspects

Optional; default is []

A list of additional aspects that should be -applied to deps. Defaults to the empty list. These must be passed -by the individual rules to avoid potential circular dependencies -between the API and the aspects; the API loaded the aspects -directly, then those aspects would not be able to load the API.

- - -### Returns + +**PARAMETERS** + + +| Name | Description | Default Value | +| :------------- | :------------- | :------------- | +| additional_deps_aspects | A list of additional aspects that should be applied to deps. Defaults to the empty list. These must be passed by the individual rules to avoid potential circular dependencies between the API and the aspects; the API loaded the aspects directly, then those aspects would not be able to load the API. | [] | +| requires_srcs | Indicates whether the srcs attribute should be marked as mandatory and non-empty. Defaults to True. | True | + +**RETURNS** A new attribute dictionary that can be added to the attributes of a -custom build rule to provide the same interface as `swift_library`. + custom build rule to provide the same interface as `swift_library`. + + + - ## swift_common.precompile_clang_module -
-swift_common.precompile_clang_module(*, actions, cc_compilation_context, feature_configuration,
-module_map_file, module_name, swift_toolchain, target_name, bin_dir=None, genfiles_dir=None,
-swift_info=None)
+
+swift_common.precompile_clang_module(actions, cc_compilation_context, feature_configuration,
+                                     module_map_file, module_name, swift_toolchain, target_name,
+                                     bin_dir, genfiles_dir, swift_infos)
 
Precompiles an explicit Clang module that is compatible with Swift. - -### Arguments - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
actions

Required

The context's actions object.

cc_compilation_context

Required

A CcCompilationContext that contains headers -and other information needed to compile this module. This -compilation context should contain all headers required to compile -the module, which includes the headers for the module itself and -any others that must be present on the file system/in the sandbox -for compilation to succeed. The latter typically refers to the set -of headers of the direct dependencies of the module being compiled, -which Clang needs to be physically present before it detects that -they belong to one of the precompiled module dependencies.

feature_configuration

Required

A feature configuration obtained from -swift_common.configure_features.

module_map_file

Required

A textual module map file that defines the Clang module -to be compiled.

module_name

Required

The name of the top-level module in the module map that -will be compiled.

swift_toolchain

Required

The SwiftToolchainInfo provider of the toolchain.

target_name

Required

The name of the target for which the code is being -compiled, which is used to determine unique file paths for the -outputs.

bin_dir

Optional; default is None

The Bazel *-bin directory root. If provided, its path is used -to store the cache for modules precompiled by Swift's ClangImporter, -and it is added to ClangImporter's header search paths for -compatibility with Bazel's C++ and Objective-C rules which support -includes of generated headers from that location.

genfiles_dir

Optional; default is None

The Bazel *-genfiles directory root. If provided, its -path is added to ClangImporter's header search paths for -compatibility with Bazel's C++ and Objective-C rules which support -inclusions of generated headers from that location.

swift_info

Optional; default is None

A SwiftInfo provider that contains dependencies required -to compile this module.

- - -### Returns +**PARAMETERS** -A `File` representing the precompiled module (`.pcm`) file, or `None` if -the toolchain or target does not support precompiled modules. +| Name | Description | Default Value | +| :------------- | :------------- | :------------- | +| actions | The context's actions object. | none | +| cc_compilation_context | A CcCompilationContext that contains headers and other information needed to compile this module. This compilation context should contain all headers required to compile the module, which includes the headers for the module itself *and* any others that must be present on the file system/in the sandbox for compilation to succeed. The latter typically refers to the set of headers of the direct dependencies of the module being compiled, which Clang needs to be physically present before it detects that they belong to one of the precompiled module dependencies. | none | +| feature_configuration | A feature configuration obtained from swift_common.configure_features. | none | +| module_map_file | A textual module map file that defines the Clang module to be compiled. | none | +| module_name | The name of the top-level module in the module map that will be compiled. | none | +| swift_toolchain | The SwiftToolchainInfo provider of the toolchain. | none | +| target_name | The name of the target for which the code is being compiled, which is used to determine unique file paths for the outputs. | none | +| bin_dir | The Bazel *-bin directory root. If provided, its path is used to store the cache for modules precompiled by Swift's ClangImporter, and it is added to ClangImporter's header search paths for compatibility with Bazel's C++ and Objective-C rules which support includes of generated headers from that location. | None | +| genfiles_dir | The Bazel *-genfiles directory root. If provided, its path is added to ClangImporter's header search paths for compatibility with Bazel's C++ and Objective-C rules which support inclusions of generated headers from that location. | None | +| swift_infos | A list of SwiftInfo providers representing dependencies required to compile this module. | [] | - -## swift_common.swift_clang_module_aspect +**RETURNS** -
-swift_common.swift_clang_module_aspect(name)
-
+A `File` representing the precompiled module (`.pcm`) file, or `None` if + the toolchain or target does not support precompiled modules. -Propagates unified `SwiftInfo` providers for targets that represent -C/Objective-C modules. - -This aspect unifies the propagation of Clang module artifacts so that Swift -targets that depend on C/Objective-C targets can find the necessary module -artifacts, and so that Swift module artifacts are not lost when passing through -a non-Swift target in the build graph (for example, a `swift_library` that -depends on an `objc_library` that depends on a `swift_library`). - -It also manages module map generation for `cc_library` targets that have the -`swift_module` tag. This tag may take one of two forms: - - * `swift_module`: By itself, this indicates that the target is compatible - with Swift and should be given a module name that is derived from its - target label. - * `swift_module=name`: The module should be given the name `name`. - -Note that the public headers of such `cc_library` targets must be parsable as C, -since Swift does not support C++ interop at this time. - -Most users will not need to interact directly with this aspect, since it is -automatically applied to the `deps` attribute of all `swift_binary`, -`swift_library`, and `swift_test` targets. However, some rules may need to -provide custom propagation logic of C/Objective-C module dependencies; for -example, a rule that has a support library as a private attribute would need to -ensure that `SwiftInfo` providers for that library and its dependencies are -propagated to any targets that depend on it, since they would not be propagated -via `deps`. In this case, the custom rule can attach this aspect to that support -library's attribute and then merge its `SwiftInfo` provider with any others that -it propagates for its targets. - - -### Attributes - - - - - - - - - - - - -
name -

Name; required

A unique name for this target.

- - -## swift_common.swift_runtime_linkopts - -
-swift_common.swift_runtime_linkopts(is_static, toolchain, is_test=False)
-
-Returns the flags that should be passed when linking a Swift binary. - -This function provides the appropriate linker arguments to callers who need -to link a binary using something other than `swift_binary` (for example, an -application bundle containing a universal `apple_binary`). - - -### Arguments - - - - - - - - - - - - - - - - - - - - -
is_static

Required

A Boolean value indicating whether the binary should be -linked against the static (rather than the dynamic) Swift runtime -libraries.

toolchain

Required

The SwiftToolchainInfo provider of the toolchain whose -linker options are desired.

is_test

Optional; default is False

A Boolean value indicating whether the target being linked is -a test target.

- - -### Returns - -A `list` of command line flags that should be passed when linking a -binary against the Swift runtime libraries. - - + + ## swift_common.toolchain_attrs -
-swift_common.toolchain_attrs(toolchain_attr_name='_toolchain')
+
+swift_common.toolchain_attrs(toolchain_attr_name)
 
Returns an attribute dictionary for toolchain users. @@ -911,30 +534,17 @@ API: Each of the attribute functions in the list above also contains the attributes from the earlier items in the list. - -### Arguments - - - - - - - - - - - - -
toolchain_attr_name

Optional; default is '_toolchain'

The name of the attribute that should be created -that points to the toolchain. This defaults to _toolchain, which -is sufficient for most rules; it is customizable for certain aspects -where having an attribute with the same name but different values -applied to a particular target causes a build crash.

- - -### Returns + +**PARAMETERS** + + +| Name | Description | Default Value | +| :------------- | :------------- | :------------- | +| toolchain_attr_name | The name of the attribute that should be created that points to the toolchain. This defaults to _toolchain, which is sufficient for most rules; it is customizable for certain aspects where having an attribute with the same name but different values applied to a particular target causes a build crash. | "_toolchain" | + +**RETURNS** A new attribute dictionary that can be added to the attributes of a -custom build rule to provide access to the Swift toolchain. + custom build rule to provide access to the Swift toolchain. diff --git a/doc/aspects.md b/doc/aspects.md index c6031c107..d28f385e2 100644 --- a/doc/aspects.md +++ b/doc/aspects.md @@ -1,9 +1,6 @@ + # Aspects - - - - The aspects described below are used within the build rule implementations. Clients interested in writing custom rules that interface with the rules/provides in this package might needs them to provide some of the same information. @@ -12,11 +9,12 @@ On this page: * [swift_usage_aspect](#swift_usage_aspect) - + + ## swift_usage_aspect -
-swift_usage_aspect()
+
+swift_usage_aspect(name)
 
Collects information about how Swift is used in a dependency tree. @@ -40,3 +38,20 @@ providers returned by `swift_library`) because the information is needed if Swift is used _anywhere_ in a dependency graph, even as dependencies of other language rules that wouldn't know how to propagate the Swift-specific providers. + +**ASPECT ATTRIBUTES** + + +| Name | Type | +| :------------- | :------------- | +| deps| String | + + +**ATTRIBUTES** + + +| Name | Description | Type | Mandatory | Default | +| :------------- | :------------- | :------------- | :------------- | :------------- | +| name | A unique name for this target. | Name | required | | + + diff --git a/doc/debuggable_remote_swift.md b/doc/debuggable_remote_swift.md new file mode 100644 index 000000000..24e6d4ccb --- /dev/null +++ b/doc/debuggable_remote_swift.md @@ -0,0 +1,112 @@ +# Debuggable Remotely Built Swift + +This is a guide to using remotely built Swift modules in local debug builds. + +At the time of writing, `lldb` depends on debugging options embedded in `.swiftmodule` files. These options include paths that are only valid on the build host. For local builds, this all just works, but for remote builds, it doesn't. + +The solution is two parts: + +1. Use `-no-serialize-debugging-options` globally, to prevent embedded paths +2. Use `-serialize-debugging-options` locally in one empty module + +Globally disabling debugging options makes those `.swiftmodule`s usable on any machine. Locally enabling debugging options for one module provides lldb with enough info to make debugging work. + +An lldb bug has been filed here: https://bugs.swift.org/browse/SR-11485 + +### Disable Debugging Options Globally + +To globally disable debugging options, use the `swift.cacheable_swiftmodules` feature in rules_swift. For example, your `.bazelrc` could look like this: + +``` +build --features=swift.cacheable_swiftmodules +``` + +What this does is ensure all modules are built explicitly with `-no-serialize-debugging-options`. It has to be explicit because `swiftc` enables `-serialize-debugging-options` in some cases. + +### Add Debug Build Config + +In a `BUILD` file - in this example, the root `BUILD` file, define the following [`config_setting`](https://docs.bazel.build/versions/master/be/general.html#config_setting). This will allow targets to conditionally depend on the locally built debugging module. + +```python +config_setting( + name = "debug", + values = { + "compilation_mode": "dbg", + }, +) +``` + +### Define Local Debug Target + +In the `BUILD` file of your choice, define a `swift_library` with: + +* A single empty source file +* Enables `-serialize-debugging-options` +* Built locally, not remote - using the `no-remote` tag + +Here is one way to define the `BUILD` file, using a [`genrule`](https://docs.bazel.build/versions/master/be/general.html#genrule) to create the empty swift file. + +```python +genrule( + name = "empty", + outs = ["empty.swift"], + cmd = "touch $(OUTS)", +) + +swift_library( + name = "_LocalDebugOptions", + srcs = [":empty"], + copts = [ + "-Xfrontend", + "-serialize-debugging-options", + ], + module_name = "_LocalDebugOptions", + tags = ["no-remote"], + visibility = ["//visibility:public"], +) +``` + +### Update Top Level Test Targets + +Finally, for each top-level test target (`ios_unit_test`, `ios_ui_test`*, etc), conditionally add the local debugging module to the deps. This is done via the [debug config](#add-debug-build-config). In the past this also had to be done for `ios_application` targets but that has since been fixed in lldb. + +```python +debug_deps = select({ + "//:debug": ["//some/path:_LocalDebugOptions"], + "//conditions:default": [], +}) + +ios_unit_test( + name = "...", + deps = debug_deps + [ + # ... + ], + # ... +) +``` + +##### Note about `ios_unit_test` + +When using a test host, the debugging module must be added to the test host target only, not the unit test target. _However_, for tests without a test host, the debugging module must be added to the unit test target. + +### LLDB Settings + +Additional settings may be required, depending on your build setup. For example, an Xcode Run Script may look like: + +``` +echo "settings set target.sdk-path $SDKROOT" +echo "settings set target.swift-framework-search-paths $FRAMEWORK_SEARCH_PATHS" +``` + +Other settings you can try customizing are: + +* `target.clang-module-search-paths` +* `target.debug-file-search-paths` +* `target.sdk-path` +* `target.swift-extra-clang-flags` +* `target.swift-framework-search-paths` +* `target.swift-module-search-paths` +* `target.use-all-compiler-flags` +* `symbols.clang-modules-cache-path` + +These settings would be written to some project specific lldbinit file which you can include directly in Xcode's scheme. diff --git a/doc/providers.md b/doc/providers.md index 6e68f71ab..a3c6ad7cc 100644 --- a/doc/providers.md +++ b/doc/providers.md @@ -1,8 +1,4 @@ -# Providers - - - - + The providers described below are propagated and required by various Swift build rules. Clients interested in writing custom rules that interface @@ -15,9 +11,15 @@ On this page: * [SwiftToolchainInfo](#SwiftToolchainInfo) * [SwiftProtoInfo](#SwiftProtoInfo) * [SwiftUsageInfo](#SwiftUsageInfo) - + + + ## SwiftInfo +
+SwiftInfo(direct_modules, transitive_modules)
+
+ Contains information about the compiled artifacts of a Swift module. This provider contains a large number of fields and many custom rules may not @@ -25,260 +27,94 @@ need to set all of them. Instead of constructing a `SwiftInfo` provider directly, consider using the `swift_common.create_swift_info` function, which has reasonable defaults for any fields not explicitly set. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
direct_defines

List of strings. The values specified by the defines attribute of the -library that directly propagated this provider.

direct_modules

List of values returned from swift_common.create_module. The modules (both -Swift and C/Objective-C) emitted by the library that propagated this provider.

direct_swiftdocs

List of Files. The Swift documentation (.swiftdoc) files for the library -that directly propagated this provider.

direct_swiftmodules

List of Files. The Swift modules (.swiftmodule) for the library that -directly propagated this provider.

module_name

String. The name of the Swift module represented by the target that directly -propagated this provider.

-

This field will be equal to the explicitly assigned module name (if present); -otherwise, it will be equal to the autogenerated module name.

swift_version

String. The version of the Swift language that was used when compiling the -propagating target; that is, the value passed via the -swift-version compiler -flag. This will be None if the flag was not set.

transitive_defines

Depset of strings. The transitive defines specified for the library that -propagated this provider and all of its dependencies.

transitive_generated_headers

Depset of Files. The transitive generated header files that can be used by -Objective-C sources to interop with the transitive Swift libraries.

transitive_modulemaps

Depset of Files. The transitive module map files that will be passed to -Clang using the -fmodule-map-file option.

-

This field is deprecated; use transitive_modules instead.

transitive_modules

Depset of values returned from swift_common.create_module. The transitive -modules (both Swift and C/Objective-C) emitted by the library that propagated -this provider and all of its dependencies.

transitive_swiftdocs

Depset of Files. The transitive Swift documentation (.swiftdoc) files -emitted by the library that propagated this provider and all of its -dependencies.

transitive_swiftinterfaces

Depset of Files. The transitive Swift interface (.swiftinterface) files -emitted by the library that propagated this provider and all of its -dependencies.

transitive_swiftmodules

Depset of Files. The transitive Swift modules (.swiftmodule) emitted by -the library that propagated this provider and all of its dependencies.

- - - + +**FIELDS** + + +| Name | Description | +| :------------- | :------------- | +| direct_modules | List of values returned from swift_common.create_module. The modules (both Swift and C/Objective-C) emitted by the library that propagated this provider. | +| transitive_modules | Depset of values returned from swift_common.create_module. The transitive modules (both Swift and C/Objective-C) emitted by the library that propagated this provider and all of its dependencies. | + + + + +## SwiftProtoInfo + +
+SwiftProtoInfo(module_mappings, pbswift_files)
+
+ +Propagates Swift-specific information about a `proto_library`. + +**FIELDS** + + +| Name | Description | +| :------------- | :------------- | +| module_mappings | Sequence of structs. Each struct contains module_name and proto_file_paths fields that denote the transitive mappings from .proto files to Swift modules. This allows messages that reference messages in other libraries to import those modules in generated code. | +| pbswift_files | Depset of Files. The transitive Swift source files (.pb.swift) generated from the .proto files. | + + + + ## SwiftToolchainInfo +
+SwiftToolchainInfo(action_configs, all_files, cc_toolchain_info, clang_implicit_deps_providers, cpu,
+                   feature_allowlists, generated_header_module_implicit_deps_providers,
+                   implicit_deps_providers, linker_supports_filelist, object_format,
+                   requested_features, root_dir, supports_objc_interop, swift_worker, system_name,
+                   test_configuration, tool_configs, unsupported_features)
+
+ + Propagates information about a Swift toolchain to compilation and linking rules that use the toolchain. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
action_configs

This field is an internal implementation detail of the build rules.

all_files

A depset of Files containing all the Swift toolchain files (tools, -libraries, and other resource files) so they can be passed as tools to actions -using this toolchain.

cc_toolchain_info

The cc_common.CcToolchainInfo provider from the Bazel C++ toolchain that this -Swift toolchain depends on.

command_line_copts

List of strings. Flags that were passed to Bazel using the --swiftcopt -command line flag. These flags have the highest precedence; they are added to -compilation command lines after the toolchain default flags -(SwiftToolchainInfo.swiftc_copts) and after flags specified in the copts -attributes of Swift targets.

cpu

String. The CPU architecture that the toolchain is targeting.

linker_opts_producer

Skylib partial. A partial function that returns the flags that should be -passed to Clang to link a binary or test target with the Swift runtime -libraries.

-

The partial should be called with two arguments:

-
    -
  • is_static: A Boolean value indicating whether to link against the static -or dynamic runtime libraries.
  • -
  • is_test: A Boolean value indicating whether the target being linked is a -test target.
  • -
object_format

String. The object file format of the platform that the toolchain is -targeting. The currently supported values are "elf" and "macho".

optional_implicit_deps

List of Targets. Library targets that should be added as implicit -dependencies of any swift_library, swift_binary, or swift_test target that -does not have the feature swift.minimal_deps applied.

requested_features

List of strings. Features that should be implicitly enabled by default for -targets built using this toolchain, unless overridden by the user by listing -their negation in the features attribute of a target/package or in the ---features command line flag.

-

These features determine various compilation and debugging behaviors of the -Swift build rules, and they are also passed to the C++ APIs used when linking -(so features defined in CROSSTOOL may be used here).

required_implicit_deps

List of Targets. Library targets that should be unconditionally added as -implicit dependencies of any swift_library, swift_binary, or swift_test -target.

root_dir

String. The workspace-relative root directory of the toolchain.

supports_objc_interop

Boolean. Indicates whether or not the toolchain supports Objective-C interop.

swift_worker

File. The executable representing the worker executable used to invoke the -compiler and other Swift tools (for both incremental and non-incremental -compiles).

system_name

String. The name of the operating system that the toolchain is targeting.

test_configuration

Struct containing two fields:

-
    -
  • env: A dict of environment variables to be set when running tests -that were built with this toolchain.
  • -
  • execution_requirements: A dict of execution requirements for tests -that were built with this toolchain.
  • -
-

This is used, for example, with Xcode-based toolchains to ensure that the -xctest helper and coverage tools are found in the correct developer -directory when running tests.

tool_configs

This field is an internal implementation detail of the build rules.

unsupported_features

List of strings. Features that should be implicitly disabled by default for -targets built using this toolchain, unless overridden by the user by listing -them in the features attribute of a target/package or in the --features -command line flag.

-

These features determine various compilation and debugging behaviors of the -Swift build rules, and they are also passed to the C++ APIs used when linking -(so features defined in CROSSTOOL may be used here).

- - - -## SwiftProtoInfo -Propagates Swift-specific information about a `proto_library`. +**FIELDS** + + +| Name | Description | +| :------------- | :------------- | +| action_configs | This field is an internal implementation detail of the build rules. | +| all_files | A depset of Files containing all the Swift toolchain files (tools, libraries, and other resource files) so they can be passed as tools to actions using this toolchain. | +| cc_toolchain_info | The cc_common.CcToolchainInfo provider from the Bazel C++ toolchain that this Swift toolchain depends on. | +| clang_implicit_deps_providers | A struct with the following fields, which represent providers from targets that should be added as implicit dependencies of any precompiled explicit C/Objective-C modules:

* cc_infos: A list of CcInfo providers from targets specified as the toolchain's implicit dependencies. * objc_infos: A list of apple_common.Objc providers from targets specified as the toolchain's implicit dependencies. * swift_infos: A list of SwiftInfo providers from targets specified as the toolchain's implicit dependencies.

For ease of use, this field is never None; it will always be a valid struct containing the fields described above, even if those lists are empty. | +| cpu | String. The CPU architecture that the toolchain is targeting. | +| feature_allowlists | A list of SwiftFeatureAllowlistInfo providers that allow or prohibit packages from requesting or disabling features. | +| generated_header_module_implicit_deps_providers | A struct with the following fields, which are providers from targets that should be treated as compile-time inputs to actions that precompile the explicit module for the generated Objective-C header of a Swift module:

* cc_infos: A list of CcInfo providers from targets specified as the toolchain's implicit dependencies. * objc_infos: A list of apple_common.Objc providers from targets specified as the toolchain's implicit dependencies. * swift_infos: A list of SwiftInfo providers from targets specified as the toolchain's implicit dependencies.

This is used to provide modular dependencies for the fixed inclusions (Darwin, Foundation) that are unconditionally emitted in those files.

For ease of use, this field is never None; it will always be a valid struct containing the fields described above, even if those lists are empty. | +| implicit_deps_providers | A struct with the following fields, which represent providers from targets that should be added as implicit dependencies of any Swift compilation or linking target (but not to precompiled explicit C/Objective-C modules):

* cc_infos: A list of CcInfo providers from targets specified as the toolchain's implicit dependencies. * objc_infos: A list of apple_common.Objc providers from targets specified as the toolchain's implicit dependencies. * swift_infos: A list of SwiftInfo providers from targets specified as the toolchain's implicit dependencies.

For ease of use, this field is never None; it will always be a valid struct containing the fields described above, even if those lists are empty. | +| linker_supports_filelist | Boolean. Indicates whether or not the toolchain's linker supports the input files passed to it via a file list. | +| object_format | String. The object file format of the platform that the toolchain is targeting. The currently supported values are "elf" and "macho". | +| requested_features | List of strings. Features that should be implicitly enabled by default for targets built using this toolchain, unless overridden by the user by listing their negation in the features attribute of a target/package or in the --features command line flag.

These features determine various compilation and debugging behaviors of the Swift build rules, and they are also passed to the C++ APIs used when linking (so features defined in CROSSTOOL may be used here). | +| root_dir | String. The workspace-relative root directory of the toolchain. | +| supports_objc_interop | Boolean. Indicates whether or not the toolchain supports Objective-C interop. | +| swift_worker | File. The executable representing the worker executable used to invoke the compiler and other Swift tools (for both incremental and non-incremental compiles). | +| system_name | String. The name of the operating system that the toolchain is targeting. | +| test_configuration | Struct containing two fields:

* env: A dict of environment variables to be set when running tests that were built with this toolchain.

* execution_requirements: A dict of execution requirements for tests that were built with this toolchain.

This is used, for example, with Xcode-based toolchains to ensure that the xctest helper and coverage tools are found in the correct developer directory when running tests. | +| tool_configs | This field is an internal implementation detail of the build rules. | +| unsupported_features | List of strings. Features that should be implicitly disabled by default for targets built using this toolchain, unless overridden by the user by listing them in the features attribute of a target/package or in the --features command line flag.

These features determine various compilation and debugging behaviors of the Swift build rules, and they are also passed to the C++ APIs used when linking (so features defined in CROSSTOOL may be used here). | + + + - - - - - - - - - - - - - - - -
module_mappings

Sequence of structs. Each struct contains module_name and -proto_file_paths fields that denote the transitive mappings from .proto -files to Swift modules. This allows messages that reference messages in other -libraries to import those modules in generated code.

pbswift_files

Depset of Files. The transitive Swift source files (.pb.swift) generated -from the .proto files.

- - - ## SwiftUsageInfo +
+SwiftUsageInfo(toolchain)
+
+ A provider that indicates that Swift was used by a target or any target that it depends on, and specifically which toolchain was used. - - - - - - - - - - - -
toolchain

The Swift toolchain that was used to build the targets propagating this -provider.

+ +**FIELDS** + + +| Name | Description | +| :------------- | :------------- | +| toolchain | The Swift toolchain that was used to build the targets propagating this provider. | + diff --git a/doc/rules.md b/doc/rules.md index 4ad65f182..7117649fa 100644 --- a/doc/rules.md +++ b/doc/rules.md @@ -1,8 +1,9 @@ -# BUILD Rule Reference - - + +BUILD rules to define Swift libraries and executable binaries. +This file is the public interface that users should import to use the Swift +rules. Do not import definitions from the `internal` subdirectory directly. To use the Swift build rules in your BUILD files, load them from `@build_bazel_rules_swift//swift:swift.bzl`. @@ -17,19 +18,21 @@ On this page: * [swift_binary](#swift_binary) * [swift_c_module](#swift_c_module) + * [swift_feature_allowlist](#swift_feature_allowlist) * [swift_grpc_library](#swift_grpc_library) * [swift_import](#swift_import) * [swift_library](#swift_library) * [swift_module_alias](#swift_module_alias) * [swift_proto_library](#swift_proto_library) * [swift_test](#swift_test) - + + + ## swift_binary -
-swift_binary(name, deps, srcs, data, compatible_with, copts, defines, deprecation, distribs,
-features, licenses, linkopts, malloc, module_name, restricted_to, stamp, swiftc_inputs, tags,
-testonly, visibility)
+
+swift_binary(name, copts, data, defines, deps, linkopts, malloc, module_name, srcs, stamp,
+             swiftc_inputs)
 
Compiles and links Swift code into an executable binary. @@ -46,125 +49,31 @@ please use one of the platform-specific application rules in [rules_apple](https://github.com/bazelbuild/rules_apple) instead of `swift_binary`. - -### Attributes - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
name -

Name; required

A unique name for this target.

deps -

List of labels; optional

A list of targets that are dependencies of the target being built, which will be -linked into that target.

-

If the Swift toolchain supports implementation-only imports (private_deps on -swift_library), then targets in deps are treated as regular -(non-implementation-only) imports that are propagated both to their direct and -indirect (transitive) dependents.

-

Allowed kinds of dependencies are:

-
    -
  • swift_c_module, swift_import and swift_library (or anything -propagating SwiftInfo)
  • -
  • cc_library (or anything propagating CcInfo)
  • -
-

Additionally, on platforms that support Objective-C interop, objc_library -targets (or anything propagating the apple_common.Objc provider) are allowed -as dependencies. On platforms that do not support Objective-C interop (such as -Linux), those dependencies will be ignored.

srcs -

List of labels; optional

A list of .swift source files that will be compiled into the library.

data -

List of labels; optional

The list of files needed by this target at runtime.

-

Files and targets named in the data attribute will appear in the *.runfiles -area of this target, if it has one. This may include data files needed by a -binary or library, or other programs needed by it.

copts -

List of strings; optional

Additional compiler options that should be passed to swiftc. These strings are -subject to $(location ...) expansion.

defines -

List of strings; optional

A list of defines to add to the compilation command line.

-

Note that unlike C-family languages, Swift defines do not have values; they are -simply identifiers that are either defined or undefined. So strings in this list -should be simple identifiers, not name=value pairs.

-

Each string is prepended with -D and added to the command line. Unlike -copts, these flags are added for the target and every target that depends on -it, so use this attribute with caution. It is preferred that you add defines -directly to copts, only using this feature in the rare case that a library -needs to propagate a symbol up to those that depend on it.

linkopts -

List of strings; optional

Additional linker options that should be passed to clang. These strings are -subject to $(location ...) expansion.

malloc -

Label; optional; default is @bazel_tools//tools/cpp:malloc

Override the default dependency on malloc.

-

By default, Swift binaries are linked against @bazel_tools//tools/cpp:malloc", -which is an empty library and the resulting binary will use libc's malloc. -This label must refer to a cc_library rule.

module_name -

String; optional

The name of the Swift module being built.

-

If left unspecified, the module name will be computed based on the target's -build label, by stripping the leading // and replacing /, :, and other -non-identifier characters with underscores.

stamp -

Integer; optional; default is -1

Enable or disable link stamping; that is, whether to encode build information -into the binary. Possible values are:

-
    -
  • stamp = 1: Stamp the build information into the binary. Stamped binaries are -only rebuilt when their dependencies change. Use this if there are tests that -depend on the build information.
  • -
  • stamp = 0: Always replace build information by constant values. This gives -good build result caching.
  • -
  • stamp = -1: Embedding of build information is controlled by the ---[no]stamp flag.
  • -
swiftc_inputs -

List of labels; optional

Additional files that are referenced using $(location ...) in attributes that -support location expansion.

- - - + +**ATTRIBUTES** + + +| Name | Description | Type | Mandatory | Default | +| :------------- | :------------- | :------------- | :------------- | :------------- | +| name | A unique name for this target. | Name | required | | +| copts | Additional compiler options that should be passed to swiftc. These strings are subject to $(location ...) and ["Make" variable](https://docs.bazel.build/versions/master/be/make-variables.html) expansion. | List of strings | optional | [] | +| data | The list of files needed by this target at runtime.

Files and targets named in the data attribute will appear in the *.runfiles area of this target, if it has one. This may include data files needed by a binary or library, or other programs needed by it. | List of labels | optional | [] | +| defines | A list of defines to add to the compilation command line.

Note that unlike C-family languages, Swift defines do not have values; they are simply identifiers that are either defined or undefined. So strings in this list should be simple identifiers, **not** name=value pairs.

Each string is prepended with -D and added to the command line. Unlike copts, these flags are added for the target and every target that depends on it, so use this attribute with caution. It is preferred that you add defines directly to copts, only using this feature in the rare case that a library needs to propagate a symbol up to those that depend on it. | List of strings | optional | [] | +| deps | A list of targets that are dependencies of the target being built, which will be linked into that target.

If the Swift toolchain supports implementation-only imports (private_deps on swift_library), then targets in deps are treated as regular (non-implementation-only) imports that are propagated both to their direct and indirect (transitive) dependents.

Allowed kinds of dependencies are:

* swift_c_module, swift_import and swift_library (or anything propagating SwiftInfo)

* cc_library (or anything propagating CcInfo)

Additionally, on platforms that support Objective-C interop, objc_library targets (or anything propagating the apple_common.Objc provider) are allowed as dependencies. On platforms that do not support Objective-C interop (such as Linux), those dependencies will be **ignored.** | List of labels | optional | [] | +| linkopts | Additional linker options that should be passed to clang. These strings are subject to $(location ...) expansion. | List of strings | optional | [] | +| malloc | Override the default dependency on malloc.

By default, Swift binaries are linked against @bazel_tools//tools/cpp:malloc", which is an empty library and the resulting binary will use libc's malloc. This label must refer to a cc_library rule. | Label | optional | @bazel_tools//tools/cpp:malloc | +| module_name | The name of the Swift module being built.

If left unspecified, the module name will be computed based on the target's build label, by stripping the leading // and replacing /, :, and other non-identifier characters with underscores. | String | optional | "" | +| srcs | A list of .swift source files that will be compiled into the library. | List of labels | optional | [] | +| stamp | Enable or disable link stamping; that is, whether to encode build information into the binary. Possible values are:

* stamp = 1: Stamp the build information into the binary. Stamped binaries are only rebuilt when their dependencies change. Use this if there are tests that depend on the build information.

* stamp = 0: Always replace build information by constant values. This gives good build result caching.

* stamp = -1: Embedding of build information is controlled by the --[no]stamp flag. | Integer | optional | -1 | +| swiftc_inputs | Additional files that are referenced using $(location ...) in attributes that support location expansion. | List of labels | optional | [] | + + + + ## swift_c_module -
-swift_c_module(name, deps, compatible_with, deprecation, distribs, features, licenses, module_map,
-module_name, restricted_to, tags, testonly, visibility)
+
+swift_c_module(name, deps, module_map, module_name)
 
Wraps one or more C targets in a new module map that allows it to be imported @@ -197,55 +106,55 @@ referenced by a module map that is imported into Swift must have only C features visible, often by using preprocessor conditions like `#if __cplusplus` to hide any C++ declarations. - -### Attributes - - - - - - - - - - - - - - - - - - - - - - - - -
name -

Name; required

A unique name for this target.

deps -

List of labels; required

A list of C targets (or anything propagating CcInfo) that are dependencies of -this target and whose headers may be referenced by the module map.

module_map -

Label; required

The module map file that should be loaded to import the C library dependency -into Swift.

module_name -

String; required

The name of the top-level module in the module map that this target represents.

-

A single module.modulemap file can define multiple top-level modules. When -building with implicit modules, the presence of that module map allows any of -the modules defined in it to be imported. When building explicit modules, -however, there is a one-to-one correspondence between top-level modules and -BUILD targets and the module name must be known without reading the module map -file, so it must be provided directly. Therefore, one may have multiple -swift_c_module targets that reference the same module.modulemap file but -with different module names and headers.

- - - + +**ATTRIBUTES** + + +| Name | Description | Type | Mandatory | Default | +| :------------- | :------------- | :------------- | :------------- | :------------- | +| name | A unique name for this target. | Name | required | | +| deps | A list of C targets (or anything propagating CcInfo) that are dependencies of this target and whose headers may be referenced by the module map. | List of labels | required | | +| module_map | The module map file that should be loaded to import the C library dependency into Swift. | Label | required | | +| module_name | The name of the top-level module in the module map that this target represents.

A single module.modulemap file can define multiple top-level modules. When building with implicit modules, the presence of that module map allows any of the modules defined in it to be imported. When building explicit modules, however, there is a one-to-one correspondence between top-level modules and BUILD targets and the module name must be known without reading the module map file, so it must be provided directly. Therefore, one may have multiple swift_c_module targets that reference the same module.modulemap file but with different module names and headers. | String | required | | + + + + +## swift_feature_allowlist + +
+swift_feature_allowlist(name, managed_features, packages)
+
+ +Limits the ability to request or disable certain features to a set of packages +(and possibly subpackages) in the workspace. + +A Swift toolchain target can reference any number (zero or more) of +`swift_feature_allowlist` targets. The features managed by these allowlists may +overlap. For some package _P_, a feature is allowed to be used by targets in +that package if _P_ matches the `packages` patterns in *all* of the allowlists +that manage that feature. + +A feature that is not managed by any allowlist is allowed to be used by any +package. + + +**ATTRIBUTES** + + +| Name | Description | Type | Mandatory | Default | +| :------------- | :------------- | :------------- | :------------- | :------------- | +| name | A unique name for this target. | Name | required | | +| managed_features | A list of feature strings that are permitted to be specified by the targets in the packages matched by the packages attribute. This list may include both feature names and/or negations (a name with a leading -); a regular feature name means that the targets in the matching packages may explicitly request that the feature be enabled, and a negated feature means that the target may explicitly request that the feature be disabled.

For example, managed_features = ["foo", "-bar"] means that targets in the allowlist's packages may request that feature "foo" be enabled and that feature "bar" be disabled. | List of strings | optional | [] | +| packages | A list of strings representing packages (possibly recursive) whose targets are allowed to enable/disable the features in managed_features. Each package pattern is written in the syntax used by the package_group function:

* //foo/bar: Targets in the package //foo/bar but not in subpackages. * //foo/bar/...: Targets in the package //foo/bar and any of its subpackages. * A leading - excludes packages that would otherwise have been included by the patterns in the list.

Exclusions always take priority over inclusions; order in the list is irrelevant. | List of strings | required | | + + + + ## swift_grpc_library -
-swift_grpc_library(name, deps, srcs, compatible_with, deprecation, distribs, features, flavor,
-licenses, restricted_to, tags, testonly, visibility)
+
+swift_grpc_library(name, deps, flavor, srcs)
 
Generates a Swift library from gRPC services defined in protocol buffer sources. @@ -313,270 +222,82 @@ swift_grpc_library( ) ``` - -### Attributes - - - - - - - - - - - - - - - - - - - - - - - - -
name -

Name; required

A unique name for this target.

deps -

List of labels; optional

Exactly one swift_proto_library or swift_grpc_library target that contains -the Swift protos used by the services being generated. Test stubs should depend -on the swift_grpc_library implementing the service.

srcs -

List of labels; optional

Exactly one proto_library target that defines the services being generated.

flavor -

String; required; valid values are ['client', 'client_stubs', 'server']

The kind of definitions that should be generated:

-
    -
  • "client" to generate client definitions.
  • -
  • "client_stubs" to generate client test stubs.
  • -
  • "server" to generate server definitions.
  • -
- - - + +**ATTRIBUTES** + + +| Name | Description | Type | Mandatory | Default | +| :------------- | :------------- | :------------- | :------------- | :------------- | +| name | A unique name for this target. | Name | required | | +| deps | Exactly one swift_proto_library or swift_grpc_library target that contains the Swift protos used by the services being generated. Test stubs should depend on the swift_grpc_library implementing the service. | List of labels | optional | [] | +| flavor | The kind of definitions that should be generated:

* "client" to generate client definitions.

* "client_stubs" to generate client test stubs.

* "server" to generate server definitions. | String | required | | +| srcs | Exactly one proto_library target that defines the services being generated. | List of labels | optional | [] | + + + + ## swift_import -
-swift_import(name, deps, data, archives, compatible_with, deprecation, distribs, features, licenses,
-module_name, restricted_to, swiftdoc, swiftmodule, tags, testonly, visibility)
+
+swift_import(name, archives, data, deps, module_name, swiftdoc, swiftmodule)
 
Allows for the use of precompiled Swift modules as dependencies in other `swift_library` and `swift_binary` targets. - -### Attributes - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
name -

Name; required

A unique name for this target.

deps -

List of labels; optional

A list of targets that are dependencies of the target being built, which will be -linked into that target.

-

If the Swift toolchain supports implementation-only imports (private_deps on -swift_library), then targets in deps are treated as regular -(non-implementation-only) imports that are propagated both to their direct and -indirect (transitive) dependents.

-

Allowed kinds of dependencies are:

-
    -
  • swift_c_module, swift_import and swift_library (or anything -propagating SwiftInfo)
  • -
  • cc_library (or anything propagating CcInfo)
  • -
-

Additionally, on platforms that support Objective-C interop, objc_library -targets (or anything propagating the apple_common.Objc provider) are allowed -as dependencies. On platforms that do not support Objective-C interop (such as -Linux), those dependencies will be ignored.

data -

List of labels; optional

The list of files needed by this target at runtime.

-

Files and targets named in the data attribute will appear in the *.runfiles -area of this target, if it has one. This may include data files needed by a -binary or library, or other programs needed by it.

archives -

List of labels; required

The list of .a files provided to Swift targets that depend on this target.

module_name -

String; required

The name of the module represented by this target.

swiftdoc -

Label; optional

The .swiftdoc file provided to Swift targets that depend on this target.

swiftmodule -

Label; required

The .swiftmodule file provided to Swift targets that depend on this target.

- - - + +**ATTRIBUTES** + + +| Name | Description | Type | Mandatory | Default | +| :------------- | :------------- | :------------- | :------------- | :------------- | +| name | A unique name for this target. | Name | required | | +| archives | The list of .a files provided to Swift targets that depend on this target. | List of labels | required | | +| data | The list of files needed by this target at runtime.

Files and targets named in the data attribute will appear in the *.runfiles area of this target, if it has one. This may include data files needed by a binary or library, or other programs needed by it. | List of labels | optional | [] | +| deps | A list of targets that are dependencies of the target being built, which will be linked into that target.

If the Swift toolchain supports implementation-only imports (private_deps on swift_library), then targets in deps are treated as regular (non-implementation-only) imports that are propagated both to their direct and indirect (transitive) dependents.

Allowed kinds of dependencies are:

* swift_c_module, swift_import and swift_library (or anything propagating SwiftInfo)

* cc_library (or anything propagating CcInfo)

Additionally, on platforms that support Objective-C interop, objc_library targets (or anything propagating the apple_common.Objc provider) are allowed as dependencies. On platforms that do not support Objective-C interop (such as Linux), those dependencies will be **ignored.** | List of labels | optional | [] | +| module_name | The name of the module represented by this target. | String | required | | +| swiftdoc | The .swiftdoc file provided to Swift targets that depend on this target. | Label | optional | None | +| swiftmodule | The .swiftmodule file provided to Swift targets that depend on this target. | Label | required | | + + + + ## swift_library -
-swift_library(name, deps, srcs, data, alwayslink, compatible_with, copts, defines, deprecation,
-distribs, features, generated_header_name, licenses, linkopts, module_name, private_deps,
-restricted_to, swiftc_inputs, tags, testonly, visibility)
+
+swift_library(name, alwayslink, copts, data, defines, deps, generated_header_name, generates_header,
+              linkopts, module_name, private_deps, srcs, swiftc_inputs)
 
Compiles and links Swift code into a static library and Swift module. - -### Attributes - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
name -

Name; required

A unique name for this target.

deps -

List of labels; optional

A list of targets that are dependencies of the target being built, which will be -linked into that target.

-

If the Swift toolchain supports implementation-only imports (private_deps on -swift_library), then targets in deps are treated as regular -(non-implementation-only) imports that are propagated both to their direct and -indirect (transitive) dependents.

-

Allowed kinds of dependencies are:

-
    -
  • swift_c_module, swift_import and swift_library (or anything -propagating SwiftInfo)
  • -
  • cc_library (or anything propagating CcInfo)
  • -
-

Additionally, on platforms that support Objective-C interop, objc_library -targets (or anything propagating the apple_common.Objc provider) are allowed -as dependencies. On platforms that do not support Objective-C interop (such as -Linux), those dependencies will be ignored.

srcs -

List of labels; optional

A list of .swift source files that will be compiled into the library.

data -

List of labels; optional

The list of files needed by this target at runtime.

-

Files and targets named in the data attribute will appear in the *.runfiles -area of this target, if it has one. This may include data files needed by a -binary or library, or other programs needed by it.

copts -

List of strings; optional

Additional compiler options that should be passed to swiftc. These strings are -subject to $(location ...) expansion.

defines -

List of strings; optional

A list of defines to add to the compilation command line.

-

Note that unlike C-family languages, Swift defines do not have values; they are -simply identifiers that are either defined or undefined. So strings in this list -should be simple identifiers, not name=value pairs.

-

Each string is prepended with -D and added to the command line. Unlike -copts, these flags are added for the target and every target that depends on -it, so use this attribute with caution. It is preferred that you add defines -directly to copts, only using this feature in the rare case that a library -needs to propagate a symbol up to those that depend on it.

generated_header_name -

String; optional

The name of the generated Objective-C interface header. This name must end with -a .h extension and cannot contain any path separators.

-

If this attribute is not specified, then the default behavior is to name the -header ${target_name}-Swift.h.

-

This attribute is ignored if the toolchain does not support generating headers -or if the target has the swift.no_generated_header feature enabled.

linkopts -

List of strings; optional

Additional linker options that should be passed to the linker for the binary -that depends on this target. These strings are subject to $(location ...) -expansion.

module_name -

String; optional

The name of the Swift module being built.

-

If left unspecified, the module name will be computed based on the target's -build label, by stripping the leading // and replacing /, :, and other -non-identifier characters with underscores.

private_deps -

List of labels; optional

A list of targets that are implementation-only dependencies of the target being -built. Libraries/linker flags from these dependencies will be propagated to -dependent for linking, but artifacts/flags required for compilation (such as -.swiftmodule files, C headers, and search paths) will not be propagated.

-

Allowed kinds of dependencies are:

-
    -
  • swift_c_module, swift_import and swift_library (or anything -propagating SwiftInfo)
  • -
  • cc_library (or anything propagating CcInfo)
  • -
-

Additionally, on platforms that support Objective-C interop, objc_library -targets (or anything propagating the apple_common.Objc provider) are allowed -as dependencies. On platforms that do not support Objective-C interop (such as -Linux), those dependencies will be ignored.

swiftc_inputs -

List of labels; optional

Additional files that are referenced using $(location ...) in attributes that -support location expansion.

- - - + +**ATTRIBUTES** + + +| Name | Description | Type | Mandatory | Default | +| :------------- | :------------- | :------------- | :------------- | :------------- | +| name | A unique name for this target. | Name | required | | +| alwayslink | If true, any binary that depends (directly or indirectly) on this Swift module will link in all the object files for the files listed in srcs, even if some contain no symbols referenced by the binary. This is useful if your code isn't explicitly called by code in the binary; for example, if you rely on runtime checks for protocol conformances added in extensions in the library but do not directly reference any other symbols in the object file that adds that conformance. | Boolean | optional | False | +| copts | Additional compiler options that should be passed to swiftc. These strings are subject to $(location ...) and ["Make" variable](https://docs.bazel.build/versions/master/be/make-variables.html) expansion. | List of strings | optional | [] | +| data | The list of files needed by this target at runtime.

Files and targets named in the data attribute will appear in the *.runfiles area of this target, if it has one. This may include data files needed by a binary or library, or other programs needed by it. | List of labels | optional | [] | +| defines | A list of defines to add to the compilation command line.

Note that unlike C-family languages, Swift defines do not have values; they are simply identifiers that are either defined or undefined. So strings in this list should be simple identifiers, **not** name=value pairs.

Each string is prepended with -D and added to the command line. Unlike copts, these flags are added for the target and every target that depends on it, so use this attribute with caution. It is preferred that you add defines directly to copts, only using this feature in the rare case that a library needs to propagate a symbol up to those that depend on it. | List of strings | optional | [] | +| deps | A list of targets that are dependencies of the target being built, which will be linked into that target.

If the Swift toolchain supports implementation-only imports (private_deps on swift_library), then targets in deps are treated as regular (non-implementation-only) imports that are propagated both to their direct and indirect (transitive) dependents.

Allowed kinds of dependencies are:

* swift_c_module, swift_import and swift_library (or anything propagating SwiftInfo)

* cc_library (or anything propagating CcInfo)

Additionally, on platforms that support Objective-C interop, objc_library targets (or anything propagating the apple_common.Objc provider) are allowed as dependencies. On platforms that do not support Objective-C interop (such as Linux), those dependencies will be **ignored.** | List of labels | optional | [] | +| generated_header_name | The name of the generated Objective-C interface header. This name must end with a .h extension and cannot contain any path separators.

If this attribute is not specified, then the default behavior is to name the header ${target_name}-Swift.h.

This attribute is ignored if the toolchain does not support generating headers. | String | optional | "" | +| generates_header | If True, an Objective-C header will be generated for this target, in the same build package where the target is defined. By default, the name of the header is ${target_name}-Swift.h; this can be changed using the generated_header_name attribute.

Targets should only set this attribute to True if they export Objective-C APIs. A header generated for a target that does not export Objective-C APIs will be effectively empty (except for a large amount of prologue and epilogue code) and this is generally wasteful because the extra file needs to be propagated in the build graph and, when explicit modules are enabled, extra actions must be executed to compile the Objective-C module for the generated header. | Boolean | optional | False | +| linkopts | Additional linker options that should be passed to the linker for the binary that depends on this target. These strings are subject to $(location ...) and ["Make" variable](https://docs.bazel.build/versions/master/be/make-variables.html) expansion. | List of strings | optional | [] | +| module_name | The name of the Swift module being built.

If left unspecified, the module name will be computed based on the target's build label, by stripping the leading // and replacing /, :, and other non-identifier characters with underscores. | String | optional | "" | +| private_deps | A list of targets that are implementation-only dependencies of the target being built. Libraries/linker flags from these dependencies will be propagated to dependent for linking, but artifacts/flags required for compilation (such as .swiftmodule files, C headers, and search paths) will not be propagated.

Allowed kinds of dependencies are:

* swift_c_module, swift_import and swift_library (or anything propagating SwiftInfo)

* cc_library (or anything propagating CcInfo)

Additionally, on platforms that support Objective-C interop, objc_library targets (or anything propagating the apple_common.Objc provider) are allowed as dependencies. On platforms that do not support Objective-C interop (such as Linux), those dependencies will be **ignored.** | List of labels | optional | [] | +| srcs | A list of .swift source files that will be compiled into the library. | List of labels | required | | +| swiftc_inputs | Additional files that are referenced using $(location ...) in attributes that support location expansion. | List of labels | optional | [] | + + + + ## swift_module_alias -
-swift_module_alias(name, deps, compatible_with, deprecation, distribs, features, licenses,
-module_name, restricted_to, tags, testonly, visibility)
+
+swift_module_alias(name, deps, module_name)
 
Creates a Swift module that re-exports other modules. @@ -597,45 +318,23 @@ symbol is defined; it is not repeated by the alias module.) > `deps` in the new module. You depend on undocumented features at your own > risk, as they may change in a future version of Swift. - -### Attributes - - - - - - - - - - - - - - - - - - - - -
name -

Name; required

A unique name for this target.

deps -

List of labels; optional

A list of targets that are dependencies of the target being built, which will be -linked into that target. Allowed kinds are swift_import and swift_library -(or anything else propagating SwiftInfo).

module_name -

String; optional

The name of the Swift module being built.

-

If left unspecified, the module name will be computed based on the target's -build label, by stripping the leading // and replacing /, :, and other -non-identifier characters with underscores.

- - - + +**ATTRIBUTES** + + +| Name | Description | Type | Mandatory | Default | +| :------------- | :------------- | :------------- | :------------- | :------------- | +| name | A unique name for this target. | Name | required | | +| deps | A list of targets that are dependencies of the target being built, which will be linked into that target. Allowed kinds are swift_import and swift_library (or anything else propagating SwiftInfo). | List of labels | optional | [] | +| module_name | The name of the Swift module being built.

If left unspecified, the module name will be computed based on the target's build label, by stripping the leading // and replacing /, :, and other non-identifier characters with underscores. | String | optional | "" | + + + + ## swift_proto_library -
-swift_proto_library(name, deps, compatible_with, deprecation, distribs, features, licenses,
-restricted_to, tags, testonly, visibility)
+
+swift_proto_library(name, deps)
 
Generates a Swift library from protocol buffer sources. @@ -713,37 +412,23 @@ has `deps` that aren't needed. Removing those along with the `import` statement(s) will speed up downstream Swift compilation actions, because it prevents unused modules from being loaded by `swiftc`. - -### Attributes - - - - - - - - - - - - - - - - -
name -

Name; required

A unique name for this target.

deps -

List of labels; optional

Exactly one proto_library target (or any target that propagates a proto -provider) from which the Swift library should be generated.

- - - + +**ATTRIBUTES** + + +| Name | Description | Type | Mandatory | Default | +| :------------- | :------------- | :------------- | :------------- | :------------- | +| name | A unique name for this target. | Name | required | | +| deps | Exactly one proto_library target (or any target that propagates a proto provider) from which the Swift library should be generated. | List of labels | optional | [] | + + + + ## swift_test -
-swift_test(name, deps, srcs, data, args, compatible_with, copts, defines, deprecation, distribs,
-features, flaky, licenses, linkopts, local, malloc, module_name, restricted_to, shardcount, size,
-stamp, swiftc_inputs, tags, testonly, timeout, visibility)
+
+swift_test(name, copts, data, defines, deps, linkopts, malloc, module_name, srcs, stamp,
+           swiftc_inputs)
 
Compiles and links Swift code into an executable test target. @@ -778,115 +463,22 @@ swift_test( You can also disable this feature for all the tests in a package by applying it to your BUILD file's `package()` declaration instead of the individual targets. - -### Attributes - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
name -

Name; required

A unique name for this target.

deps -

List of labels; optional

A list of targets that are dependencies of the target being built, which will be -linked into that target.

-

If the Swift toolchain supports implementation-only imports (private_deps on -swift_library), then targets in deps are treated as regular -(non-implementation-only) imports that are propagated both to their direct and -indirect (transitive) dependents.

-

Allowed kinds of dependencies are:

-
    -
  • swift_c_module, swift_import and swift_library (or anything -propagating SwiftInfo)
  • -
  • cc_library (or anything propagating CcInfo)
  • -
-

Additionally, on platforms that support Objective-C interop, objc_library -targets (or anything propagating the apple_common.Objc provider) are allowed -as dependencies. On platforms that do not support Objective-C interop (such as -Linux), those dependencies will be ignored.

srcs -

List of labels; optional

A list of .swift source files that will be compiled into the library.

data -

List of labels; optional

The list of files needed by this target at runtime.

-

Files and targets named in the data attribute will appear in the *.runfiles -area of this target, if it has one. This may include data files needed by a -binary or library, or other programs needed by it.

copts -

List of strings; optional

Additional compiler options that should be passed to swiftc. These strings are -subject to $(location ...) expansion.

defines -

List of strings; optional

A list of defines to add to the compilation command line.

-

Note that unlike C-family languages, Swift defines do not have values; they are -simply identifiers that are either defined or undefined. So strings in this list -should be simple identifiers, not name=value pairs.

-

Each string is prepended with -D and added to the command line. Unlike -copts, these flags are added for the target and every target that depends on -it, so use this attribute with caution. It is preferred that you add defines -directly to copts, only using this feature in the rare case that a library -needs to propagate a symbol up to those that depend on it.

linkopts -

List of strings; optional

Additional linker options that should be passed to clang. These strings are -subject to $(location ...) expansion.

malloc -

Label; optional; default is @bazel_tools//tools/cpp:malloc

Override the default dependency on malloc.

-

By default, Swift binaries are linked against @bazel_tools//tools/cpp:malloc", -which is an empty library and the resulting binary will use libc's malloc. -This label must refer to a cc_library rule.

module_name -

String; optional

The name of the Swift module being built.

-

If left unspecified, the module name will be computed based on the target's -build label, by stripping the leading // and replacing /, :, and other -non-identifier characters with underscores.

stamp -

Integer; optional; default is 0

Enable or disable link stamping; that is, whether to encode build information -into the binary. Possible values are:

-
    -
  • stamp = 1: Stamp the build information into the binary. Stamped binaries are -only rebuilt when their dependencies change. Use this if there are tests that -depend on the build information.
  • -
  • stamp = 0: Always replace build information by constant values. This gives -good build result caching.
  • -
  • stamp = -1: Embedding of build information is controlled by the ---[no]stamp flag.
  • -
swiftc_inputs -

List of labels; optional

Additional files that are referenced using $(location ...) in attributes that -support location expansion.

+ +**ATTRIBUTES** + + +| Name | Description | Type | Mandatory | Default | +| :------------- | :------------- | :------------- | :------------- | :------------- | +| name | A unique name for this target. | Name | required | | +| copts | Additional compiler options that should be passed to swiftc. These strings are subject to $(location ...) and ["Make" variable](https://docs.bazel.build/versions/master/be/make-variables.html) expansion. | List of strings | optional | [] | +| data | The list of files needed by this target at runtime.

Files and targets named in the data attribute will appear in the *.runfiles area of this target, if it has one. This may include data files needed by a binary or library, or other programs needed by it. | List of labels | optional | [] | +| defines | A list of defines to add to the compilation command line.

Note that unlike C-family languages, Swift defines do not have values; they are simply identifiers that are either defined or undefined. So strings in this list should be simple identifiers, **not** name=value pairs.

Each string is prepended with -D and added to the command line. Unlike copts, these flags are added for the target and every target that depends on it, so use this attribute with caution. It is preferred that you add defines directly to copts, only using this feature in the rare case that a library needs to propagate a symbol up to those that depend on it. | List of strings | optional | [] | +| deps | A list of targets that are dependencies of the target being built, which will be linked into that target.

If the Swift toolchain supports implementation-only imports (private_deps on swift_library), then targets in deps are treated as regular (non-implementation-only) imports that are propagated both to their direct and indirect (transitive) dependents.

Allowed kinds of dependencies are:

* swift_c_module, swift_import and swift_library (or anything propagating SwiftInfo)

* cc_library (or anything propagating CcInfo)

Additionally, on platforms that support Objective-C interop, objc_library targets (or anything propagating the apple_common.Objc provider) are allowed as dependencies. On platforms that do not support Objective-C interop (such as Linux), those dependencies will be **ignored.** | List of labels | optional | [] | +| linkopts | Additional linker options that should be passed to clang. These strings are subject to $(location ...) expansion. | List of strings | optional | [] | +| malloc | Override the default dependency on malloc.

By default, Swift binaries are linked against @bazel_tools//tools/cpp:malloc", which is an empty library and the resulting binary will use libc's malloc. This label must refer to a cc_library rule. | Label | optional | @bazel_tools//tools/cpp:malloc | +| module_name | The name of the Swift module being built.

If left unspecified, the module name will be computed based on the target's build label, by stripping the leading // and replacing /, :, and other non-identifier characters with underscores. | String | optional | "" | +| srcs | A list of .swift source files that will be compiled into the library. | List of labels | optional | [] | +| stamp | Enable or disable link stamping; that is, whether to encode build information into the binary. Possible values are:

* stamp = 1: Stamp the build information into the binary. Stamped binaries are only rebuilt when their dependencies change. Use this if there are tests that depend on the build information.

* stamp = 0: Always replace build information by constant values. This gives good build result caching.

* stamp = -1: Embedding of build information is controlled by the --[no]stamp flag. | Integer | optional | 0 | +| swiftc_inputs | Additional files that are referenced using $(location ...) in attributes that support location expansion. | List of labels | optional | [] | + diff --git a/doc/setup.md b/doc/setup.md index 249170d21..4df2072d4 100644 --- a/doc/setup.md +++ b/doc/setup.md @@ -1,16 +1,19 @@ + # Workspace Setup + - - ## swift_rules_dependencies -
+
 swift_rules_dependencies()
 
-Fetches repositories that are dependencies of the `rules_swift` workspace. +Fetches repositories that are dependencies of `rules_swift`. Users should call this macro in their `WORKSPACE` to ensure that all of the -dependencies of the Swift rules are downloaded and that they are isolated from -changes to those dependencies. +dependencies of the Swift rules are downloaded and that they are isolated +from changes to those dependencies. + + + diff --git a/doc/stardoc.pr103.patch b/doc/stardoc.pr103.patch new file mode 100644 index 000000000..2de970b47 --- /dev/null +++ b/doc/stardoc.pr103.patch @@ -0,0 +1,14 @@ +diff stardoc/templates/markdown_tables/func.vm stardoc/templates/markdown_tables/func.vm +index e53e639..1b7bd7a 100644 +--- stardoc/templates/markdown_tables/func.vm ++++ stardoc/templates/markdown_tables/func.vm +@@ -18,3 +18,9 @@ ${funcInfo.docString} + | $param.name | #if(!$param.docString.isEmpty()) ${util.markdownCellFormat($param.docString)} #else

-

#end | #if(!$param.getDefaultValue().isEmpty()) $param.getDefaultValue() #else none #end| + #end + #end ++ ++#if (!$funcInfo.return.docString.isEmpty()) ++**RETURNS** ++ ++${funcInfo.return.docString} ++#end \ No newline at end of file diff --git a/examples/apple/objc_interop/BUILD b/examples/apple/objc_interop/BUILD index 8cd5252d9..bb2d2fa71 100644 --- a/examples/apple/objc_interop/BUILD +++ b/examples/apple/objc_interop/BUILD @@ -20,6 +20,7 @@ objc_library( swift_library( name = "Printer", srcs = ["Printer.swift"], + generates_header = True, deps = [":PrintStream"], ) diff --git a/examples/apple/objc_interop/OIPrintStream.h b/examples/apple/objc_interop/OIPrintStream.h index ff79a7280..2873ba84f 100644 --- a/examples/apple/objc_interop/OIPrintStream.h +++ b/examples/apple/objc_interop/OIPrintStream.h @@ -15,7 +15,7 @@ #import /** A very contrived interface for writing strings to a file handle. */ -@interface OIPrintStream: NSObject +@interface OIPrintStream : NSObject - (nonnull instancetype)initWithFileHandle:(nonnull NSFileHandle *)fileHandle; diff --git a/examples/xplatform/c_from_swift/c_counter.cc b/examples/xplatform/c_from_swift/c_counter.cc index c8cb23819..e95a0b155 100644 --- a/examples/xplatform/c_from_swift/c_counter.cc +++ b/examples/xplatform/c_from_swift/c_counter.cc @@ -13,6 +13,7 @@ // limitations under the License. #include "examples/xplatform/c_from_swift/c_counter.h" + #include "examples/xplatform/c_from_swift/counter.h" using ::swiftexample::Counter; diff --git a/swift/BUILD b/swift/BUILD index ad1437b20..03429e5fe 100644 --- a/swift/BUILD +++ b/swift/BUILD @@ -1,33 +1,43 @@ load("@bazel_skylib//:bzl_library.bzl", "bzl_library") load("@bazel_skylib//rules:common_settings.bzl", "bool_setting") +load( + "//swift/internal:build_settings.bzl", + "per_module_swiftcopt_flag", +) package(default_visibility = ["//visibility:public"]) licenses(["notice"]) bzl_library( - name = "swift", - srcs = ["swift.bzl"], - deps = [ - "//swift/internal:build_defs", - "//swift/internal:rules", - "//swift/internal:swift_common", - ], + name = "bazel_tools_bzl", + srcs = ["@bazel_tools//tools:bzl_srcs"], ) bzl_library( - name = "stats", - srcs = ["stats.bzl"], + name = "repositories", + srcs = ["repositories.bzl"], deps = [ - "//swift/internal:build_defs", + ":bazel_tools_bzl", + "//swift/internal:swift_autoconfiguration", ], ) bzl_library( - name = "repositories", - srcs = ["repositories.bzl"], + name = "swift", + srcs = ["swift.bzl"], deps = [ - "//swift/internal:toolchain_support", + "//swift/internal:providers", + "//swift/internal:swift_binary_test", + "//swift/internal:swift_c_module", + "//swift/internal:swift_common", + "//swift/internal:swift_feature_allowlist", + "//swift/internal:swift_grpc_library", + "//swift/internal:swift_import", + "//swift/internal:swift_library", + "//swift/internal:swift_module_alias", + "//swift/internal:swift_proto_library", + "//swift/internal:swift_usage_aspect", ], ) @@ -43,12 +53,27 @@ filegroup( ], ) +# User settable flag that specifies additional Swift copts on a per-swiftmodule basis. +per_module_swiftcopt_flag( + name = "per_module_swiftcopt", + build_setting_default = "", + visibility = ["//visibility:public"], +) + # Configuration setting for enabling the generation of swiftinterface files. bool_setting( name = "emit_swiftinterface", build_setting_default = False, ) +# Configuration setting for forcing generation of Apple targets. +# NOTE: this is only intended for use with transitions that want to force +# building of an Apple target when building for Linux. +bool_setting( + name = "force_apple_target", + build_setting_default = False, +) + # Allows a user to override the default Swift driver during a build, if the # toolchain is using the default. label_flag( diff --git a/swift/extras.bzl b/swift/extras.bzl new file mode 100644 index 000000000..7ab8af518 --- /dev/null +++ b/swift/extras.bzl @@ -0,0 +1,39 @@ +# Copyright 2018 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 handling Bazel transitive repositories used by the +dependencies of the Swift rules. +""" + +load("@build_bazel_apple_support//lib:repositories.bzl", "apple_support_dependencies") +load( + "@rules_proto//proto:repositories.bzl", + "rules_proto_dependencies", + "rules_proto_toolchains", +) + +def swift_rules_extra_dependencies(): + """Fetches transitive repositories of the dependencies of `rules_swift`. + + Users should call this macro in their `WORKSPACE` following the use of + `swift_rules_dependencies` to ensure that all of the dependencies of + the Swift rules are downloaded and that they are isolated from changes + to those dependencies. + """ + + apple_support_dependencies() + + rules_proto_dependencies() + + rules_proto_toolchains() diff --git a/swift/internal/BUILD b/swift/internal/BUILD index 7d5295eef..f63b536e6 100644 --- a/swift/internal/BUILD +++ b/swift/internal/BUILD @@ -3,96 +3,383 @@ load("@bazel_skylib//:bzl_library.bzl", "bzl_library") licenses(["notice"]) bzl_library( - name = "rules", - srcs = [ - "swift_binary_test.bzl", - "swift_c_module.bzl", - "swift_grpc_library.bzl", - "swift_import.bzl", - "swift_library.bzl", - "swift_module_alias.bzl", - "swift_proto_library.bzl", - "swift_protoc_gen_aspect.bzl", - ], - visibility = [ - "//swift:__pkg__", + name = "actions", + srcs = ["actions.bzl"], + visibility = ["//swift:__subpackages__"], + deps = [ + ":features", + ":toolchain_config", + "@bazel_skylib//lib:partial", + "@bazel_skylib//lib:types", ], +) + +bzl_library( + name = "attrs", + srcs = ["attrs.bzl"], + visibility = ["//swift:__subpackages__"], deps = [ - ":build_defs", - ":swift_common", + ":providers", "@bazel_skylib//lib:dicts", - "@bazel_skylib//lib:new_sets", + ], +) + +bzl_library( + name = "autolinking", + srcs = ["autolinking.bzl"], + visibility = ["//swift:__subpackages__"], + deps = [ + ":actions", + ":toolchain_config", + ], +) + +bzl_library( + name = "compiling", + srcs = ["compiling.bzl"], + visibility = ["//swift:__subpackages__"], + deps = [ + ":actions", + ":autolinking", + ":debugging", + ":derived_files", + ":feature_names", + ":features", + ":providers", + ":toolchain_config", + ":utils", + ":vfsoverlay", + "@bazel_skylib//lib:collections", "@bazel_skylib//lib:partial", "@bazel_skylib//lib:paths", - "@bazel_skylib//rules:common_settings", + "@bazel_skylib//lib:types", ], ) bzl_library( - name = "swift_common", - srcs = ["swift_common.bzl"], - visibility = [ - "//swift:__pkg__", - ], - deps = [":build_defs"], -) - -bzl_library( - name = "build_defs", - srcs = [ - "actions.bzl", - "attrs.bzl", - "autolinking.bzl", - "compiling.bzl", - "debugging.bzl", - "derived_files.bzl", - "feature_names.bzl", - "features.bzl", - "linking.bzl", - "module_maps.bzl", - "proto_gen_utils.bzl", - "providers.bzl", - "swift_clang_module_aspect.bzl", - "swift_usage_aspect.bzl", - "toolchain_config.bzl", - "utils.bzl", - "vfsoverlay.bzl", + name = "debugging", + srcs = ["debugging.bzl"], + visibility = ["//swift:__subpackages__"], + deps = [ + ":actions", + ":derived_files", + ":toolchain_config", ], - visibility = [ - "//swift:__pkg__", +) + +bzl_library( + name = "derived_files", + srcs = ["derived_files.bzl"], + visibility = ["//swift:__subpackages__"], + deps = [ + ":utils", + "@bazel_skylib//lib:paths", ], +) + +bzl_library( + name = "features", + srcs = ["features.bzl"], + visibility = ["//swift:__subpackages__"], deps = [ + ":feature_names", "@bazel_skylib//lib:collections", - "@bazel_skylib//lib:dicts", "@bazel_skylib//lib:new_sets", + ], +) + +bzl_library( + name = "linking", + srcs = ["linking.bzl"], + visibility = ["//swift:__subpackages__"], + deps = [ + ":derived_files", + "@bazel_skylib//lib:collections", "@bazel_skylib//lib:partial", + "@bazel_tools//tools/build_defs/cc:action_names.bzl", + ], +) + +bzl_library( + name = "module_maps", + srcs = ["module_maps.bzl"], + visibility = ["//swift:__subpackages__"], + deps = ["@bazel_skylib//lib:paths"], +) + +bzl_library( + name = "proto_gen_utils", + srcs = ["proto_gen_utils.bzl"], + visibility = ["//swift:__subpackages__"], + deps = [ + ":utils", "@bazel_skylib//lib:paths", + ], +) + +bzl_library( + name = "providers", + srcs = ["providers.bzl"], + visibility = ["//swift:__subpackages__"], + deps = [ "@bazel_skylib//lib:sets", "@bazel_skylib//lib:types", + ], +) + +bzl_library( + name = "swift_autoconfiguration", + srcs = ["swift_autoconfiguration.bzl"], + visibility = ["//swift:__subpackages__"], + deps = ["@build_bazel_rules_swift//swift/internal:feature_names"], +) + +bzl_library( + name = "swift_c_module", + srcs = ["swift_c_module.bzl"], + visibility = ["//swift:__subpackages__"], + deps = [ + ":swift_common", + ":utils", + ], +) + +bzl_library( + name = "swift_feature_allowlist", + srcs = ["swift_feature_allowlist.bzl"], + visibility = ["//swift:__subpackages__"], + deps = [ + ":providers", + ], +) + +bzl_library( + name = "swift_binary_test", + srcs = ["swift_binary_test.bzl"], + visibility = ["//swift:__subpackages__"], + deps = [ + ":compiling", + ":derived_files", + ":feature_names", + ":linking", + ":providers", + ":swift_common", + ":utils", + "@bazel_skylib//lib:dicts", + "@bazel_skylib//lib:partial", + ], +) + +bzl_library( + name = "swift_clang_module_aspect", + srcs = ["swift_clang_module_aspect.bzl"], + visibility = ["//swift:__subpackages__"], + deps = [ + ":attrs", + ":compiling", + ":derived_files", + ":feature_names", + ":features", + ":module_maps", + ":providers", + ":utils", + ], +) + +bzl_library( + name = "swift_common", + srcs = ["swift_common.bzl"], + visibility = ["//swift:__subpackages__"], + deps = [ + ":attrs", + ":compiling", + ":features", + ":linking", + ":providers", + ":swift_clang_module_aspect", + ], +) + +bzl_library( + name = "swift_grpc_library", + srcs = ["swift_grpc_library.bzl"], + visibility = ["//swift:__subpackages__"], + deps = [ + ":compiling", + ":feature_names", + ":linking", + ":proto_gen_utils", + ":providers", + ":swift_common", + ":utils", + "@bazel_skylib//lib:dicts", + "@rules_proto//proto:defs", + ], +) + +bzl_library( + name = "swift_import", + srcs = ["swift_import.bzl"], + visibility = ["//swift:__subpackages__"], + deps = [ + ":attrs", + ":compiling", + ":providers", + ":swift_common", + ":utils", + "@bazel_skylib//lib:dicts", + ], +) + +bzl_library( + name = "swift_library", + srcs = ["swift_library.bzl"], + visibility = ["//swift:__subpackages__"], + deps = [ + ":attrs", + ":build_settings", + ":compiling", + ":feature_names", + ":linking", + ":providers", + ":swift_common", + ":utils", + "@bazel_skylib//lib:dicts", + "@bazel_skylib//lib:sets", "@bazel_skylib//rules:common_settings", - "@bazel_tools//tools/build_defs/cc:action_names.bzl", ], ) bzl_library( - name = "toolchain_support", - srcs = [ - "swift_autoconfiguration.bzl", - "swift_toolchain.bzl", - "xcode_swift_toolchain.bzl", + name = "swift_module_alias", + srcs = ["swift_module_alias.bzl"], + visibility = ["//swift:__subpackages__"], + deps = [ + ":compiling", + ":derived_files", + ":linking", + ":providers", + ":swift_common", + ":utils", + "@bazel_skylib//lib:dicts", ], - visibility = [ - "//swift:__pkg__", +) + +bzl_library( + name = "swift_proto_library", + srcs = ["swift_proto_library.bzl"], + visibility = ["//swift:__subpackages__"], + deps = [ + ":providers", + ":swift_protoc_gen_aspect", + "@rules_proto//proto:defs", ], +) + +bzl_library( + name = "swift_protoc_gen_aspect", + srcs = ["swift_protoc_gen_aspect.bzl"], + visibility = ["//swift:__subpackages__"], + deps = [ + ":attrs", + ":compiling", + ":feature_names", + ":linking", + ":proto_gen_utils", + ":providers", + ":swift_common", + ":utils", + "@bazel_skylib//lib:dicts", + "@bazel_skylib//rules:common_settings", + "@rules_proto//proto:defs", + ], +) + +bzl_library( + name = "swift_toolchain", + srcs = ["swift_toolchain.bzl"], + visibility = ["//swift:__subpackages__"], + deps = [ + ":actions", + ":attrs", + ":autolinking", + ":compiling", + ":debugging", + ":feature_names", + ":features", + ":providers", + ":toolchain_config", + ":utils", + "@bazel_skylib//lib:dicts", + "@bazel_skylib//lib:partial", + "@bazel_tools//tools/cpp:toolchain_utils.bzl", + ], +) + +bzl_library( + name = "swift_usage_aspect", + srcs = ["swift_usage_aspect.bzl"], + visibility = ["//swift:__subpackages__"], + deps = [":providers"], +) + +bzl_library( + name = "toolchain_config", + srcs = ["toolchain_config.bzl"], + visibility = ["//swift:__subpackages__"], + deps = [ + "@bazel_skylib//lib:partial", + "@bazel_skylib//lib:paths", + "@bazel_skylib//lib:types", + ], +) + +bzl_library( + name = "utils", + srcs = ["utils.bzl"], + visibility = ["//swift:__subpackages__"], + deps = ["@bazel_skylib//lib:paths"], +) + +bzl_library( + name = "xcode_swift_toolchain", + srcs = ["xcode_swift_toolchain.bzl"], + visibility = ["//swift:__subpackages__"], deps = [ - ":build_defs", + ":actions", + ":attrs", + ":compiling", + ":feature_names", + ":features", + ":providers", + ":toolchain_config", + ":utils", "@bazel_skylib//lib:collections", "@bazel_skylib//lib:dicts", "@bazel_skylib//lib:partial", "@bazel_skylib//lib:paths", + "@bazel_skylib//rules:common_settings", + "@bazel_tools//tools/cpp:toolchain_utils.bzl", ], ) +bzl_library( + name = "feature_names", + srcs = ["feature_names.bzl"], + visibility = ["//swift:__subpackages__"], +) + +bzl_library( + name = "vfsoverlay", + srcs = ["vfsoverlay.bzl"], + visibility = ["//swift:__subpackages__"], +) + +bzl_library( + name = "build_settings", + srcs = ["build_settings.bzl"], +) + # Consumed by Bazel integration tests. filegroup( name = "for_bazel_tests", diff --git a/swift/internal/actions.bzl b/swift/internal/actions.bzl index 931d22f51..9066cd758 100644 --- a/swift/internal/actions.bzl +++ b/swift/internal/actions.bzl @@ -37,6 +37,10 @@ swift_action_names = struct( # Precompiles an explicit module for a C/Objective-C module map and its # headers, emitting a `.pcm` file. PRECOMPILE_C_MODULE = "SwiftPrecompileCModule", + + # Produces files that are usually fallout of the compilation such as + # .swiftmodule, -Swift.h and more. + DERIVE_FILES = "SwiftDeriveFiles", ) def _apply_configurator(configurator, prerequisites, args): @@ -131,7 +135,17 @@ def _apply_action_configs( prerequisites, args, ) - if action_inputs: + + # If we create an action configurator from a lambda that calls + # `Args.add*`, the result will be the `Args` objects (rather + # than `None`) because those methods return the same `Args` + # object for chaining. We can guard against this (and possibly + # other errors) by checking that the value is a struct. If it + # is, then it's not `None` and it probably came from the + # provider used by `swift_toolchain_config.config_result`. If + # it's some other kind of struct, then we'll error out trying to + # access the fields. + if type(action_inputs) == "struct": inputs.extend(action_inputs.inputs) transitive_inputs.extend(action_inputs.transitive_inputs) diff --git a/swift/internal/attrs.bzl b/swift/internal/attrs.bzl index 52f76f510..bf1785110 100644 --- a/swift/internal/attrs.bzl +++ b/swift/internal/attrs.bzl @@ -43,7 +43,9 @@ indirect (transitive) dependents. ), } -def swift_compilation_attrs(additional_deps_aspects = []): +def swift_compilation_attrs( + additional_deps_aspects = [], + requires_srcs = True): """Returns an attribute dictionary for rules that compile Swift code. The returned dictionary contains the subset of attributes that are shared by @@ -76,6 +78,8 @@ def swift_compilation_attrs(additional_deps_aspects = []): by the individual rules to avoid potential circular dependencies between the API and the aspects; the API loaded the aspects directly, then those aspects would not be able to load the API. + requires_srcs: Indicates whether the `srcs` attribute should be marked + as mandatory and non-empty. Defaults to `True`. Returns: A new attribute dictionary that can be added to the attributes of a @@ -89,16 +93,18 @@ def swift_compilation_attrs(additional_deps_aspects = []): swift_toolchain_attrs(), { "srcs": attr.label_list( - flags = ["DIRECT_COMPILE_TIME_INPUT"], + allow_empty = not requires_srcs, allow_files = ["swift"], doc = """\ A list of `.swift` source files that will be compiled into the library. """, + flags = ["DIRECT_COMPILE_TIME_INPUT"], + mandatory = requires_srcs, ), "copts": attr.string_list( doc = """\ Additional compiler options that should be passed to `swiftc`. These strings are -subject to `$(location ...)` expansion. +subject to `$(location ...)` and ["Make" variable](https://docs.bazel.build/versions/master/be/make-variables.html) expansion. """, ), "defines": attr.string_list( @@ -146,6 +152,9 @@ def swift_config_attrs(): "_config_emit_swiftinterface": attr.label( default = "@build_bazel_rules_swift//swift:emit_swiftinterface", ), + "_per_module_swiftcopt": attr.label( + default = "@build_bazel_rules_swift//swift:per_module_swiftcopt", + ), } def swift_deps_attr(doc, **kwargs): @@ -171,6 +180,7 @@ Allowed kinds of dependencies are: * `swift_c_module`, `swift_import` and `swift_library` (or anything propagating `SwiftInfo`) + * `cc_library` (or anything propagating `CcInfo`) Additionally, on platforms that support Objective-C interop, `objc_library` @@ -186,7 +196,9 @@ Linux), those dependencies will be **ignored.** **kwargs ) -def swift_library_rule_attrs(additional_deps_aspects = []): +def swift_library_rule_attrs( + additional_deps_aspects = [], + requires_srcs = True): """Returns an attribute dictionary for `swift_library`-like rules. The returned dictionary contains the same attributes that are defined by the @@ -221,6 +233,8 @@ def swift_library_rule_attrs(additional_deps_aspects = []): by the individual rules to avoid potential circular dependencies between the API and the aspects; the API loaded the aspects directly, then those aspects would not be able to load the API. + requires_srcs: Indicates whether the `srcs` attribute should be marked + as mandatory and non-empty. Defaults to `True`. Returns: A new attribute dictionary that can be added to the attributes of a @@ -229,6 +243,7 @@ def swift_library_rule_attrs(additional_deps_aspects = []): return dicts.add( swift_compilation_attrs( additional_deps_aspects = additional_deps_aspects, + requires_srcs = requires_srcs, ), swift_config_attrs(), { @@ -236,7 +251,7 @@ def swift_library_rule_attrs(additional_deps_aspects = []): doc = """\ Additional linker options that should be passed to the linker for the binary that depends on this target. These strings are subject to `$(location ...)` -expansion. +and ["Make" variable](https://docs.bazel.build/versions/master/be/make-variables.html) expansion. """, ), "alwayslink": attr.bool( @@ -259,8 +274,24 @@ a `.h` extension and cannot contain any path separators. If this attribute is not specified, then the default behavior is to name the header `${target_name}-Swift.h`. -This attribute is ignored if the toolchain does not support generating headers -or if the target has the `swift.no_generated_header` feature enabled. +This attribute is ignored if the toolchain does not support generating headers. +""", + mandatory = False, + ), + "generates_header": attr.bool( + default = False, + doc = """\ +If True, an Objective-C header will be generated for this target, in the same +build package where the target is defined. By default, the name of the header is +`${target_name}-Swift.h`; this can be changed using the `generated_header_name` +attribute. + +Targets should only set this attribute to True if they export Objective-C APIs. +A header generated for a target that does not export Objective-C APIs will be +effectively empty (except for a large amount of prologue and epilogue code) and +this is generally wasteful because the extra file needs to be propagated in the +build graph and, when explicit modules are enabled, extra actions must be +executed to compile the Objective-C module for the generated header. """, mandatory = False, ), @@ -325,7 +356,7 @@ def swift_toolchain_driver_attrs(): return { "swift_executable": attr.label( allow_single_file = True, - cfg = "host", + cfg = "exec", doc = """\ A replacement Swift driver executable. @@ -337,7 +368,7 @@ that it is invoked in the correct mode (i.e., `swift`, `swiftc`, ), "_default_swift_executable": attr.label( allow_files = True, - cfg = "host", + cfg = "exec", default = Label( "@build_bazel_rules_swift//swift:default_swift_executable", ), diff --git a/swift/internal/build_settings.bzl b/swift/internal/build_settings.bzl new file mode 100644 index 000000000..858496fdf --- /dev/null +++ b/swift/internal/build_settings.bzl @@ -0,0 +1,86 @@ +# Copyright 2021 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. + +"""Custom build settings rules for Swift rules.""" + +PerModuleSwiftCoptSettingInfo = provider( + doc = "A provider for the parsed per-swiftmodule swift copts.", + fields = { + "value": "A map of target labels to lists of additional Swift copts to apply to" + + "the target.", + }, +) + +def additional_per_module_swiftcopts(label, provider): + """Returns additional swiftcopts to apply to a target named `label`. + + Args: + label: The label of the target. + provider: The `PerModuleSwiftCoptsSettingInfo` provider from the calling + context. + Returns: + A list of additional swiftcopts to use when builing the target. + """ + per_module_copts = provider.value + if per_module_copts: + target_label = str(label) + return per_module_copts.get(target_label, []) + return [] + +def _per_module_swiftcopt_flag_impl(ctx): + # Each item in this list should of the form + # "=". + # TODO: Remove extra list once allow_multiple is enabled + module_and_copts_list = [ctx.build_setting_value] + value = dict() + for item in module_and_copts_list: + if not item: + continue + contents = item.split("=", 1) + if len(contents) != 2: + fail("""\ +--per_module_swiftcopt must be written as a target label, an equal sign \ +("="), and a comma-delimited list of compiler flags. For example, \ +"//package:target=-copt,-copt,...".""") + + # Canonicalize the label using the `Label` constructor. + label = str(Label(contents[0])) + raw_copts = contents[1] + + # TODO(b/186875113): This breaks if any of the copts actually use + # commas. Switch to a more selective approach to splitting, + # respecting an escape sequence for commas inside of copts. + copts = raw_copts.split(",") + if len(copts) > 0: + existing_copts = value.get(label) + if existing_copts: + existing_copts.extend(copts) + else: + value[label] = copts + return PerModuleSwiftCoptSettingInfo(value = value) + +per_module_swiftcopt_flag = rule( + build_setting = config.string( + flag = True, + # allow_multiple = True, # TODO: Enable once bazel supports it + ), + # TODO(b/186869451): Support adding swiftcopts by module name in addition + # to the target label. + doc = """\ +A string list build setting that can be set on the command line. Each item in +the list is expected to be of the form: = where +copts is a colon separated list of Swift copts. +""", + implementation = _per_module_swiftcopt_flag_impl, +) diff --git a/swift/internal/compiling.bzl b/swift/internal/compiling.bzl index 74d8307d6..fba2971d4 100644 --- a/swift/internal/compiling.bzl +++ b/swift/internal/compiling.bzl @@ -17,6 +17,7 @@ load("@bazel_skylib//lib:collections.bzl", "collections") load("@bazel_skylib//lib:partial.bzl", "partial") load("@bazel_skylib//lib:paths.bzl", "paths") +load("@bazel_skylib//lib:sets.bzl", "sets") load("@bazel_skylib//lib:types.bzl", "types") load( ":actions.bzl", @@ -30,39 +31,48 @@ load(":derived_files.bzl", "derived_files") load( ":feature_names.bzl", "SWIFT_FEATURE_CACHEABLE_SWIFTMODULES", - "SWIFT_FEATURE_COMPILE_STATS", "SWIFT_FEATURE_COVERAGE", + "SWIFT_FEATURE_COVERAGE_PREFIX_MAP", "SWIFT_FEATURE_DBG", "SWIFT_FEATURE_DEBUG_PREFIX_MAP", + "SWIFT_FEATURE_DISABLE_SYSTEM_INDEX", "SWIFT_FEATURE_EMIT_C_MODULE", "SWIFT_FEATURE_EMIT_SWIFTINTERFACE", "SWIFT_FEATURE_ENABLE_BATCH_MODE", "SWIFT_FEATURE_ENABLE_LIBRARY_EVOLUTION", + "SWIFT_FEATURE_ENABLE_SKIP_FUNCTION_BODIES", "SWIFT_FEATURE_ENABLE_TESTING", "SWIFT_FEATURE_FASTBUILD", "SWIFT_FEATURE_FULL_DEBUG_INFO", - "SWIFT_FEATURE_IMPLICIT_MODULES", + "SWIFT_FEATURE_GLOBAL_MODULE_CACHE_USES_TMPDIR", "SWIFT_FEATURE_INDEX_WHILE_BUILDING", - "SWIFT_FEATURE_MINIMAL_DEPS", + "SWIFT_FEATURE_LAYERING_CHECK", "SWIFT_FEATURE_MODULE_MAP_HOME_IS_CWD", "SWIFT_FEATURE_NO_EMBED_DEBUG_MODULE", - "SWIFT_FEATURE_NO_GENERATED_HEADER", "SWIFT_FEATURE_NO_GENERATED_MODULE_MAP", "SWIFT_FEATURE_OPT", "SWIFT_FEATURE_OPT_USES_OSIZE", "SWIFT_FEATURE_OPT_USES_WMO", + "SWIFT_FEATURE_SPLIT_DERIVED_FILES_GENERATION", "SWIFT_FEATURE_SUPPORTS_LIBRARY_EVOLUTION", + "SWIFT_FEATURE_SUPPORTS_SYSTEM_MODULE_FLAG", + "SWIFT_FEATURE_SYSTEM_MODULE", "SWIFT_FEATURE_USE_C_MODULES", + "SWIFT_FEATURE_USE_GLOBAL_INDEX_STORE", "SWIFT_FEATURE_USE_GLOBAL_MODULE_CACHE", "SWIFT_FEATURE_VFSOVERLAY", + "SWIFT_FEATURE__NUM_THREADS_0_IN_SWIFTCOPTS", + "SWIFT_FEATURE__WMO_IN_SWIFTCOPTS", ) load(":features.bzl", "are_all_features_enabled", "is_feature_enabled") +load(":module_maps.bzl", "write_module_map") load(":providers.bzl", "SwiftInfo", "create_swift_info") load(":toolchain_config.bzl", "swift_toolchain_config") load( ":utils.bzl", "collect_cc_libraries", "compact", + "compilation_context_for_explicit_module_compilation", "get_providers", "struct_fields", ) @@ -78,12 +88,36 @@ _SWIFTMODULES_VFS_ROOT = "/__build_bazel_rules_swift/swiftmodules" # when an API to obtain this is available. _DEFAULT_WMO_THREAD_COUNT = 12 -def compile_action_configs(): +# Swift command line flags that enable whole module optimization. (This +# dictionary is used as a set for quick lookup; the values are irrelevant.) +_WMO_FLAGS = { + "-wmo": True, + "-whole-module-optimization": True, + "-force-single-frontend-invocation": True, +} + +def compile_action_configs( + *, + additional_objc_copts = [], + additional_swiftc_copts = [], + generated_header_rewriter = None): """Returns the list of action configs needed to perform Swift compilation. Toolchains must add these to their own list of action configs so that compilation actions will be correctly configured. + Args: + additional_objc_copts: An optional list of additional Objective-C + compiler flags that should be passed (preceded by `-Xcc`) to Swift + compile actions *and* Swift explicit module precompile actions after + any other toolchain- or user-provided flags. + additional_swiftc_copts: An optional list of additional Swift compiler + flags that should be passed to Swift compile actions only after any + other toolchain- or user-provided flags. + generated_header_rewriter: An executable that will be invoked after + compilation to rewrite the generated header, or None if this is not + desired. + Returns: The list of action configs needed to perform compilation. """ @@ -103,6 +137,10 @@ def compile_action_configs(): actions = [swift_action_names.COMPILE], configurators = [_output_object_or_file_map_configurator], ), + swift_toolchain_config.action_config( + actions = [swift_action_names.DERIVE_FILES], + configurators = [_output_swiftmodule_or_file_map_configurator], + ), # Emit precompiled Clang modules, and embed all files that were read # during compilation into the PCM. @@ -118,6 +156,17 @@ def compile_action_configs(): ], ), + # Don't embed Clang module breadcrumbs in debug info. + swift_toolchain_config.action_config( + actions = [swift_action_names.COMPILE], + configurators = [ + swift_toolchain_config.add_arg( + "-Xfrontend", + "-no-clang-module-breadcrumbs", + ), + ], + ), + # Add the output precompiled module file path to the command line. swift_toolchain_config.action_config( actions = [swift_action_names.PRECOMPILE_C_MODULE], @@ -128,11 +177,19 @@ def compile_action_configs(): swift_toolchain_config.action_config( actions = [swift_action_names.COMPILE], configurators = [_emit_module_path_configurator], + not_features = [SWIFT_FEATURE_SPLIT_DERIVED_FILES_GENERATION], + ), + swift_toolchain_config.action_config( + actions = [swift_action_names.DERIVE_FILES], + configurators = [_emit_module_path_configurator], ), # Configure library evolution and the path to the .swiftinterface file. swift_toolchain_config.action_config( - actions = [swift_action_names.COMPILE], + actions = [ + swift_action_names.COMPILE, + swift_action_names.DERIVE_FILES, + ], configurators = [ swift_toolchain_config.add_arg("-enable-library-evolution"), ], @@ -142,28 +199,51 @@ def compile_action_configs(): ], ), swift_toolchain_config.action_config( - actions = [swift_action_names.COMPILE], + actions = [ + swift_action_names.COMPILE, + swift_action_names.DERIVE_FILES, + ], configurators = [_emit_module_interface_path_configurator], features = [ SWIFT_FEATURE_SUPPORTS_LIBRARY_EVOLUTION, SWIFT_FEATURE_EMIT_SWIFTINTERFACE, ], ), + + # Configure the path to the emitted *-Swift.h file. swift_toolchain_config.action_config( actions = [swift_action_names.COMPILE], configurators = [_emit_objc_header_path_configurator], - not_features = [SWIFT_FEATURE_NO_GENERATED_HEADER], + not_features = [ + SWIFT_FEATURE_SPLIT_DERIVED_FILES_GENERATION, + ], ), - - # Configure the location where compiler performance statistics are - # dumped. swift_toolchain_config.action_config( - actions = [swift_action_names.COMPILE], - configurators = [_stats_output_dir_configurator], - features = [SWIFT_FEATURE_COMPILE_STATS], + actions = [swift_action_names.DERIVE_FILES], + configurators = [_emit_objc_header_path_configurator], ), ] + # TODO: Enable once bazel supports nested functions + # if generated_header_rewriter: + # # Only add the generated header rewriter to the command line only if the + # # toolchain provides one, the relevant feature is requested, and the + # # particular compilation action is generating a header. + # def generated_header_rewriter_configurator(prerequisites, args): + # if prerequisites.generated_header_file: + # args.add( + # generated_header_rewriter, + # format = "-Xwrapped-swift=-generated-header-rewriter=%s", + # ) + + # action_configs.append( + # swift_toolchain_config.action_config( + # actions = [swift_action_names.COMPILE], + # configurators = [generated_header_rewriter_configurator], + # features = [SWIFT_FEATURE_REWRITE_GENERATED_HEADER], + # ), + # ) + #### Compilation-mode-related flags # # These configs set flags based on the current compilation mode. They mirror @@ -174,14 +254,20 @@ def compile_action_configs(): # Define appropriate conditional compilation symbols depending on the # build mode. swift_toolchain_config.action_config( - actions = [swift_action_names.COMPILE], + actions = [ + swift_action_names.COMPILE, + swift_action_names.DERIVE_FILES, + ], configurators = [ swift_toolchain_config.add_arg("-DDEBUG"), ], features = [[SWIFT_FEATURE_DBG], [SWIFT_FEATURE_FASTBUILD]], ), swift_toolchain_config.action_config( - actions = [swift_action_names.COMPILE], + actions = [ + swift_action_names.COMPILE, + swift_action_names.DERIVE_FILES, + ], configurators = [ swift_toolchain_config.add_arg("-DNDEBUG"), ], @@ -192,14 +278,20 @@ def compile_action_configs(): # `-O` unless the `swift.opt_uses_osize` feature is enabled, then use # `-Osize`. swift_toolchain_config.action_config( - actions = [swift_action_names.COMPILE], + actions = [ + swift_action_names.COMPILE, + swift_action_names.DERIVE_FILES, + ], configurators = [ swift_toolchain_config.add_arg("-Onone"), ], features = [[SWIFT_FEATURE_DBG], [SWIFT_FEATURE_FASTBUILD]], ), swift_toolchain_config.action_config( - actions = [swift_action_names.COMPILE], + actions = [ + swift_action_names.COMPILE, + swift_action_names.DERIVE_FILES, + ], configurators = [ swift_toolchain_config.add_arg("-O"), ], @@ -207,7 +299,10 @@ def compile_action_configs(): not_features = [SWIFT_FEATURE_OPT_USES_OSIZE], ), swift_toolchain_config.action_config( - actions = [swift_action_names.COMPILE], + actions = [ + swift_action_names.COMPILE, + swift_action_names.DERIVE_FILES, + ], configurators = [ swift_toolchain_config.add_arg("-Osize"), ], @@ -217,17 +312,26 @@ def compile_action_configs(): # If the `swift.opt_uses_wmo` feature is enabled, opt builds should also # automatically imply whole-module optimization. swift_toolchain_config.action_config( - actions = [swift_action_names.COMPILE], + actions = [ + swift_action_names.COMPILE, + swift_action_names.DERIVE_FILES, + ], configurators = [ swift_toolchain_config.add_arg("-whole-module-optimization"), ], - features = [SWIFT_FEATURE_OPT, SWIFT_FEATURE_OPT_USES_WMO], + features = [ + [SWIFT_FEATURE_OPT, SWIFT_FEATURE_OPT_USES_WMO], + [SWIFT_FEATURE__WMO_IN_SWIFTCOPTS], + ], ), # Enable or disable serialization of debugging options into # swiftmodules. swift_toolchain_config.action_config( - actions = [swift_action_names.COMPILE], + actions = [ + swift_action_names.COMPILE, + swift_action_names.DERIVE_FILES, + ], configurators = [ swift_toolchain_config.add_arg( "-Xfrontend", @@ -237,7 +341,10 @@ def compile_action_configs(): features = [SWIFT_FEATURE_CACHEABLE_SWIFTMODULES], ), swift_toolchain_config.action_config( - actions = [swift_action_names.COMPILE], + actions = [ + swift_action_names.COMPILE, + swift_action_names.DERIVE_FILES, + ], configurators = [ swift_toolchain_config.add_arg( "-Xfrontend", @@ -252,7 +359,10 @@ def compile_action_configs(): # Enable testability if requested. swift_toolchain_config.action_config( - actions = [swift_action_names.COMPILE], + actions = [ + swift_action_names.COMPILE, + swift_action_names.DERIVE_FILES, + ], configurators = [ swift_toolchain_config.add_arg("-enable-testing"), ], @@ -264,12 +374,18 @@ def compile_action_configs(): # `dsymutil` produces spurious warnings about symbols in the debug map # when run on DI emitted by `-gline-tables-only`. swift_toolchain_config.action_config( - actions = [swift_action_names.COMPILE], + actions = [ + swift_action_names.COMPILE, + swift_action_names.DERIVE_FILES, + ], configurators = [swift_toolchain_config.add_arg("-g")], features = [[SWIFT_FEATURE_DBG], [SWIFT_FEATURE_FULL_DEBUG_INFO]], ), swift_toolchain_config.action_config( - actions = [swift_action_names.COMPILE], + actions = [ + swift_action_names.COMPILE, + swift_action_names.DERIVE_FILES, + ], configurators = [ swift_toolchain_config.add_arg("-gline-tables-only"), ], @@ -279,7 +395,10 @@ def compile_action_configs(): # Make paths written into debug info workspace-relative. swift_toolchain_config.action_config( - actions = [swift_action_names.COMPILE], + actions = [ + swift_action_names.COMPILE, + swift_action_names.DERIVE_FILES, + ], configurators = [ swift_toolchain_config.add_arg( "-Xwrapped-swift=-debug-prefix-pwd-is-dot", @@ -291,6 +410,22 @@ def compile_action_configs(): [SWIFT_FEATURE_DEBUG_PREFIX_MAP, SWIFT_FEATURE_FULL_DEBUG_INFO], ], ), + + # Make paths written into coverage info workspace-relative. + swift_toolchain_config.action_config( + actions = [ + swift_action_names.COMPILE, + swift_action_names.DERIVE_FILES, + ], + configurators = [ + swift_toolchain_config.add_arg( + "-Xwrapped-swift=-coverage-prefix-pwd-is-dot", + ), + ], + features = [ + [SWIFT_FEATURE_COVERAGE_PREFIX_MAP, SWIFT_FEATURE_COVERAGE], + ], + ), ] #### Coverage and sanitizer instrumentation flags @@ -300,7 +435,10 @@ def compile_action_configs(): # supporting them either. action_configs += [ swift_toolchain_config.action_config( - actions = [swift_action_names.COMPILE], + actions = [ + swift_action_names.COMPILE, + swift_action_names.DERIVE_FILES, + ], configurators = [ swift_toolchain_config.add_arg("-profile-generate"), swift_toolchain_config.add_arg("-profile-coverage-mapping"), @@ -308,28 +446,43 @@ def compile_action_configs(): features = [SWIFT_FEATURE_COVERAGE], ), swift_toolchain_config.action_config( - actions = [swift_action_names.COMPILE], + actions = [ + swift_action_names.COMPILE, + swift_action_names.DERIVE_FILES, + ], configurators = [ swift_toolchain_config.add_arg("-sanitize=address"), ], features = ["asan"], ), swift_toolchain_config.action_config( - actions = [swift_action_names.COMPILE], + actions = [ + swift_action_names.COMPILE, + swift_action_names.DERIVE_FILES, + ], configurators = [ swift_toolchain_config.add_arg("-sanitize=thread"), ], features = ["tsan"], ), + swift_toolchain_config.action_config( + actions = [swift_action_names.COMPILE], + configurators = [ + swift_toolchain_config.add_arg("-sanitize=undefined"), + ], + features = ["ubsan"], + ), ] #### Flags controlling how Swift/Clang modular inputs are processed + action_configs += [ # Treat paths in .modulemap files as workspace-relative, not modulemap- # relative. swift_toolchain_config.action_config( actions = [ swift_action_names.COMPILE, + swift_action_names.DERIVE_FILES, swift_action_names.PRECOMPILE_C_MODULE, ], configurators = [ @@ -345,34 +498,152 @@ def compile_action_configs(): # Configure how implicit modules are handled--either using the module # cache, or disabled completely when using explicit modules. swift_toolchain_config.action_config( - actions = [swift_action_names.COMPILE], + actions = [ + swift_action_names.COMPILE, + swift_action_names.DERIVE_FILES, + ], configurators = [_global_module_cache_configurator], + features = [SWIFT_FEATURE_USE_GLOBAL_MODULE_CACHE], + not_features = [ + [SWIFT_FEATURE_USE_C_MODULES], + [SWIFT_FEATURE_GLOBAL_MODULE_CACHE_USES_TMPDIR], + ], + ), + swift_toolchain_config.action_config( + actions = [ + swift_action_names.COMPILE, + swift_action_names.DERIVE_FILES, + ], + configurators = [_tmpdir_module_cache_configurator], features = [ - SWIFT_FEATURE_IMPLICIT_MODULES, SWIFT_FEATURE_USE_GLOBAL_MODULE_CACHE, + SWIFT_FEATURE_GLOBAL_MODULE_CACHE_USES_TMPDIR, ], + not_features = [SWIFT_FEATURE_USE_C_MODULES], ), swift_toolchain_config.action_config( - actions = [swift_action_names.COMPILE], + actions = [ + swift_action_names.COMPILE, + swift_action_names.DERIVE_FILES, + ], configurators = [ swift_toolchain_config.add_arg( "-Xwrapped-swift=-ephemeral-module-cache", ), ], - features = [SWIFT_FEATURE_IMPLICIT_MODULES], - not_features = [SWIFT_FEATURE_USE_GLOBAL_MODULE_CACHE], + not_features = [ + [SWIFT_FEATURE_USE_C_MODULES], + [SWIFT_FEATURE_USE_GLOBAL_MODULE_CACHE], + ], + ), + # When using C modules, disable the implicit search for module map files + # because all of them, including system dependencies, will be provided + # explicitly. + swift_toolchain_config.action_config( + actions = [ + swift_action_names.COMPILE, + swift_action_names.DERIVE_FILES, + swift_action_names.PRECOMPILE_C_MODULE, + ], + configurators = [ + swift_toolchain_config.add_arg( + "-Xcc", + "-fno-implicit-module-maps", + ), + ], + features = [SWIFT_FEATURE_USE_C_MODULES], + ), + # Do not allow implicit modules to be used at all when emitting an + # explicit C/Objective-C module. Consider the case of two modules A and + # B, where A depends on B. If B does not emit an explicit module, then + # when A is compiled it would contain a hardcoded reference to B via its + # path in the implicit module cache. Thus, A would not be movable; some + # library importing A would try to resolve B at that path, which may no + # longer exist when the upstream library is built. + # + # This implies that for a C/Objective-C library to build as an explicit + # module, all of its dependencies must as well. On the other hand, a + # Swift library can be compiled with some of its Objective-C + # dependencies still using implicit modules, as long as no Objective-C + # library wants to import that Swift library's generated header and + # build itself as an explicit module. + swift_toolchain_config.action_config( + actions = [swift_action_names.PRECOMPILE_C_MODULE], + configurators = [ + swift_toolchain_config.add_arg( + "-Xcc", + "-fno-implicit-modules", + ), + ], + features = [SWIFT_FEATURE_USE_C_MODULES], + ), + swift_toolchain_config.action_config( + actions = [swift_action_names.PRECOMPILE_C_MODULE], + configurators = [_c_layering_check_configurator], + features = [SWIFT_FEATURE_LAYERING_CHECK], + not_features = [SWIFT_FEATURE_SYSTEM_MODULE], + ), + swift_toolchain_config.action_config( + actions = [swift_action_names.PRECOMPILE_C_MODULE], + configurators = [ + # Before Swift 5.4, ClangImporter doesn't currently handle the + # IsSystem bit correctly for the input file and ignores the + # `-fsystem-module` flag, which causes the module map to be + # treated as a user input. We can work around this by disabling + # diagnostics for system modules. However, this also disables + # behavior in ClangImporter that causes system APIs that use + # `UInt` to be imported to use `Int` instead. The only solution + # here is to use Xcode 12.5 or higher. + swift_toolchain_config.add_arg("-Xcc", "-w"), + swift_toolchain_config.add_arg( + "-Xcc", + "-Wno-nullability-declspec", + ), + ], + features = [SWIFT_FEATURE_SYSTEM_MODULE], + not_features = [SWIFT_FEATURE_SUPPORTS_SYSTEM_MODULE_FLAG], + ), + swift_toolchain_config.action_config( + actions = [swift_action_names.PRECOMPILE_C_MODULE], + configurators = [ + # `-Xclang -emit-module` ought to be unnecessary if `-emit-pcm` + # is present because ClangImporter configures the invocation to + # use the `GenerateModule` action. However, it does so *after* + # creating the invocation by parsing the command line via a + # helper shared by `-emit-pcm` and other operations, so the + # changing of the action to `GenerateModule` occurs too late; + # the argument parser doesn't know that this will be the + # intended action and it emits a spurious diagnostic: + # "'-fsystem-module' only allowed with '-emit-module'". So, for + # system modules we'll pass `-emit-module` as well; it gets rid + # of the diagnostic and doesn't appear to cause other issues. + swift_toolchain_config.add_arg("-Xcc", "-Xclang"), + swift_toolchain_config.add_arg("-Xcc", "-emit-module"), + swift_toolchain_config.add_arg("-Xcc", "-Xclang"), + swift_toolchain_config.add_arg("-Xcc", "-fsystem-module"), + ], + features = [ + SWIFT_FEATURE_SUPPORTS_SYSTEM_MODULE_FLAG, + SWIFT_FEATURE_SYSTEM_MODULE, + ], ), ] #### Search paths for Swift module dependencies action_configs.extend([ swift_toolchain_config.action_config( - actions = [swift_action_names.COMPILE], + actions = [ + swift_action_names.COMPILE, + swift_action_names.DERIVE_FILES, + ], configurators = [_dependencies_swiftmodules_configurator], not_features = [SWIFT_FEATURE_VFSOVERLAY], ), swift_toolchain_config.action_config( - actions = [swift_action_names.COMPILE], + actions = [ + swift_action_names.COMPILE, + swift_action_names.DERIVE_FILES, + ], configurators = [ _dependencies_swiftmodules_vfsoverlay_configurator, ], @@ -385,11 +656,20 @@ def compile_action_configs(): swift_toolchain_config.action_config( actions = [ swift_action_names.COMPILE, + swift_action_names.DERIVE_FILES, swift_action_names.PRECOMPILE_C_MODULE, ], configurators = [_framework_search_paths_configurator], ), ) + action_configs.append( + swift_toolchain_config.action_config( + actions = [ + swift_action_names.PRECOMPILE_C_MODULE, + ], + configurators = [_pcm_additional_framework_search_paths_configurator], + ), + ) #### Other ClangImporter flags action_configs.extend([ @@ -397,6 +677,7 @@ def compile_action_configs(): swift_toolchain_config.action_config( actions = [ swift_action_names.COMPILE, + swift_action_names.DERIVE_FILES, swift_action_names.PRECOMPILE_C_MODULE, ], configurators = [ @@ -410,6 +691,7 @@ def compile_action_configs(): swift_toolchain_config.action_config( actions = [ swift_action_names.COMPILE, + swift_action_names.DERIVE_FILES, swift_action_names.PRECOMPILE_C_MODULE, ], configurators = [_dependencies_clang_modules_configurator], @@ -418,6 +700,7 @@ def compile_action_configs(): swift_toolchain_config.action_config( actions = [ swift_action_names.COMPILE, + swift_action_names.DERIVE_FILES, swift_action_names.PRECOMPILE_C_MODULE, ], configurators = [_dependencies_clang_modulemaps_configurator], @@ -432,6 +715,7 @@ def compile_action_configs(): swift_toolchain_config.action_config( actions = [ swift_action_names.COMPILE, + swift_action_names.DERIVE_FILES, swift_action_names.PRECOMPILE_C_MODULE, ], configurators = [ @@ -450,15 +734,25 @@ def compile_action_configs(): # flags themselves, since some Swift users enable it there as a build # performance hack. swift_toolchain_config.action_config( - actions = [swift_action_names.COMPILE], + actions = [ + swift_action_names.COMPILE, + swift_action_names.DERIVE_FILES, + ], configurators = [_batch_mode_configurator], features = [SWIFT_FEATURE_ENABLE_BATCH_MODE], - not_features = [SWIFT_FEATURE_OPT, SWIFT_FEATURE_OPT_USES_WMO], + not_features = [ + [SWIFT_FEATURE_OPT, SWIFT_FEATURE_OPT_USES_WMO], + [SWIFT_FEATURE__WMO_IN_SWIFTCOPTS], + ], ), - # Set the number of threads to use for WMO. + # Set the number of threads to use for WMO. (We can skip this if we know + # we'll already be applying `-num-threads` via `--swiftcopt` flags.) swift_toolchain_config.action_config( - actions = [swift_action_names.COMPILE], + actions = [ + swift_action_names.COMPILE, + swift_action_names.DERIVE_FILES, + ], configurators = [ partial.make( _wmo_thread_count_configurator, @@ -467,10 +761,17 @@ def compile_action_configs(): False, ), ], - features = [SWIFT_FEATURE_OPT, SWIFT_FEATURE_OPT_USES_WMO], + features = [ + [SWIFT_FEATURE_OPT, SWIFT_FEATURE_OPT_USES_WMO], + [SWIFT_FEATURE__WMO_IN_SWIFTCOPTS], + ], + not_features = [SWIFT_FEATURE__NUM_THREADS_0_IN_SWIFTCOPTS], ), swift_toolchain_config.action_config( - actions = [swift_action_names.COMPILE], + actions = [ + swift_action_names.COMPILE, + swift_action_names.DERIVE_FILES, + ], configurators = [ partial.make( _wmo_thread_count_configurator, @@ -479,54 +780,135 @@ def compile_action_configs(): True, ), ], - not_features = [SWIFT_FEATURE_OPT, SWIFT_FEATURE_OPT_USES_WMO], + not_features = [ + [SWIFT_FEATURE_OPT, SWIFT_FEATURE_OPT_USES_WMO], + [SWIFT_FEATURE__NUM_THREADS_0_IN_SWIFTCOPTS], + ], ), # Set the module name. swift_toolchain_config.action_config( actions = [ swift_action_names.COMPILE, + swift_action_names.DERIVE_FILES, swift_action_names.PRECOMPILE_C_MODULE, ], configurators = [_module_name_configurator], ), + # Pass extra flags for swiftmodule only compilations + swift_toolchain_config.action_config( + actions = [swift_action_names.DERIVE_FILES], + configurators = [ + swift_toolchain_config.add_arg( + "-experimental-skip-non-inlinable-function-bodies", + ), + ], + features = [SWIFT_FEATURE_ENABLE_SKIP_FUNCTION_BODIES], + ), + swift_toolchain_config.action_config( + actions = [swift_action_names.COMPILE], + configurators = [_global_index_store_configurator], + features = [ + SWIFT_FEATURE_INDEX_WHILE_BUILDING, + SWIFT_FEATURE_USE_GLOBAL_INDEX_STORE, + ], + ), + # Configure index-while-building. swift_toolchain_config.action_config( actions = [swift_action_names.COMPILE], configurators = [_index_while_building_configurator], features = [SWIFT_FEATURE_INDEX_WHILE_BUILDING], ), + swift_toolchain_config.action_config( + actions = [swift_action_names.COMPILE], + configurators = [ + swift_toolchain_config.add_arg( + "-index-ignore-system-modules", + ), + ], + features = [ + SWIFT_FEATURE_INDEX_WHILE_BUILDING, + SWIFT_FEATURE_DISABLE_SYSTEM_INDEX, + ], + ), # User-defined conditional compilation flags (defined for Swift; those # passed directly to ClangImporter are handled above). swift_toolchain_config.action_config( - actions = [swift_action_names.COMPILE], + actions = [ + swift_action_names.COMPILE, + swift_action_names.DERIVE_FILES, + ], configurators = [_conditional_compilation_flag_configurator], ), # Disable auto-linking for prebuilt static frameworks. swift_toolchain_config.action_config( - actions = [swift_action_names.COMPILE], + actions = [ + swift_action_names.COMPILE, + swift_action_names.DERIVE_FILES, + ], configurators = [_static_frameworks_disable_autolink_configurator], ), ] - # NOTE: The position of this action config in the list is important, because - # it places user compile flags after flags added by the rules, allowing - # `copts` attributes and `--swiftcopt` flag values to override flags set by - # the rule implementations as a last resort. + # NOTE: The positions of these action configs in the list are important, + # because it places the `copts` attribute ("user compile flags") after flags + # added by the rules, and then the "additional objc" and "additional swift" + # flags follow those, which are `--objccopt` and `--swiftcopt` flags from + # the command line that should override even the flags specified in the + # `copts` attribute. action_configs.append( swift_toolchain_config.action_config( - actions = [swift_action_names.COMPILE], + actions = [ + swift_action_names.COMPILE, + swift_action_names.DERIVE_FILES, + ], configurators = [_user_compile_flags_configurator], ), ) + if additional_objc_copts: + action_configs.append( + swift_toolchain_config.action_config( + actions = [ + swift_action_names.COMPILE, + swift_action_names.PRECOMPILE_C_MODULE, + ], + configurators = [ + # TODO(#568): Switch to using lambda when the minimum + # supported Bazel version by rules_swift supports it. + partial.make( + _additional_objc_copts_configurator, + additional_objc_copts, + ), + ], + ), + ) + if additional_swiftc_copts: + action_configs.append( + swift_toolchain_config.action_config( + # TODO(allevato): Determine if there are any uses of + # `-Xcc`-prefixed flags that need to be added to explicit module + # actions, or if we should advise against/forbid that. + actions = [swift_action_names.COMPILE], + configurators = [ + # TODO(#568): Switch to using lambda when the minimum + # supported Bazel version by rules_swift supports it. + partial.make( + _additional_swiftc_copts_configurator, + additional_swiftc_copts, + ), + ], + ), + ) action_configs.append( swift_toolchain_config.action_config( actions = [ swift_action_names.COMPILE, + swift_action_names.DERIVE_FILES, swift_action_names.PRECOMPILE_C_MODULE, ], configurators = [_source_files_configurator], @@ -536,33 +918,50 @@ def compile_action_configs(): # Add additional input files to the sandbox (does not modify flags). action_configs.append( swift_toolchain_config.action_config( - actions = [swift_action_names.COMPILE], + actions = [ + swift_action_names.COMPILE, + swift_action_names.DERIVE_FILES, + ], configurators = [_additional_inputs_configurator], ), ) return action_configs -def _output_object_or_file_map_configurator(prerequisites, args): - """Adds the output file map or single object file to the command line.""" - output_file_map = prerequisites.output_file_map +def _output_or_file_map(output_file_map, outputs, args): + """Adds the output file map or single file to the command line.""" if output_file_map: args.add("-output-file-map", output_file_map) return swift_toolchain_config.config_result( inputs = [output_file_map], ) - object_files = prerequisites.object_files - if len(object_files) != 1: + if len(outputs) != 1: fail( "Internal error: If not using an output file map, there should " + "only be a single object file expected as the output, but we " + - "found: {}".format(object_files), + "found: {}".format(outputs), ) - args.add("-o", object_files[0]) + args.add("-o", outputs[0]) return None +def _output_object_or_file_map_configurator(prerequisites, args): + """Adds the output file map or single object file to the command line.""" + return _output_or_file_map( + output_file_map = prerequisites.output_file_map, + outputs = prerequisites.object_files, + args = args, + ) + +def _output_swiftmodule_or_file_map_configurator(prerequisites, args): + """Adds the output file map or single object file to the command line.""" + return _output_or_file_map( + output_file_map = prerequisites.output_file_map, + outputs = [prerequisites.swiftmodule_file], + args = args, + ) + def _output_pcm_file_configurator(prerequisites, args): """Adds the `.pcm` output path to the command line.""" args.add("-o", prerequisites.pcm_file) @@ -577,7 +976,8 @@ def _emit_module_interface_path_configurator(prerequisites, args): def _emit_objc_header_path_configurator(prerequisites, args): """Adds the generated header output path to the command line.""" - args.add("-emit-objc-header-path", prerequisites.generated_header_file) + if prerequisites.generated_header_file: + args.add("-emit-objc-header-path", prerequisites.generated_header_file) def _global_module_cache_configurator(prerequisites, args): """Adds flags to enable the global module cache.""" @@ -591,11 +991,33 @@ def _global_module_cache_configurator(prerequisites, args): paths.join(prerequisites.bin_dir.path, "_swift_module_cache"), ) +def _tmpdir_module_cache_configurator(prerequisites, args): + """Adds flags to enable a stable tmp directory module cache.""" + + args.add( + "-module-cache-path", + paths.join( + "/tmp/__build_bazel_rules_swift", + "swift_module_cache", + prerequisites.workspace_name, + ), + ) + def _batch_mode_configurator(prerequisites, args): """Adds flags to enable batch compilation mode.""" if not _is_wmo_manually_requested(prerequisites.user_compile_flags): args.add("-enable-batch-mode") +def _c_layering_check_configurator(prerequisites, args): + # We do not enforce layering checks for the Objective-C header generated by + # Swift, because we don't have predictable control over the imports that it + # generates. Due to modular re-exports (which are especially common among + # system frameworks), it may generate an import declaration for a particular + # symbol from a different module than the Swift code imported it from. + if not prerequisites.is_swift_generated_header: + args.add("-Xcc", "-fmodules-strict-decluse") + return None + def _clang_search_paths_configurator(prerequisites, args): """Adds Clang search paths to the command line.""" args.add_all( @@ -679,89 +1101,125 @@ def _collect_clang_module_inputs( `swift_toolchain_config.config_result`) that contains the input artifacts for the action. """ - module_inputs = [] - header_depsets = [] - - # Swift compiles (not Clang module compiles) that prefer precompiled modules - # do not need the full set of transitive headers. - if cc_info and not (is_swift and prefer_precompiled_modules): - header_depsets.append(cc_info.compilation_context.headers) + direct_inputs = [] + transitive_inputs = [] + + if cc_info: + # The headers stored in the `cc_info` argument's compilation context + # differ depending on the kind of action we're invoking: + if (is_swift and not prefer_precompiled_modules) or not is_swift: + # If this is a `SwiftCompile` with explicit modules disabled, the + # `headers` field is an already-computed set of the transitive + # headers of all the deps. (For an explicit module build, we skip it + # and will more selectively pick subsets for any individual modules + # that need to fallback to implicit modules in the loop below). + # + # If this is a `SwiftPrecompileCModule`, then by definition we're + # only here in a build with explicit modules enabled. We should only + # need the direct headers of the module being compiled and its + # direct dependencies (the latter because Clang needs them present + # on the file system to map them to the module that contains them.) + # However, we may also need some of the transitive headers, if the + # module has dependencies that aren't recognized as modules (e.g., + # `cc_library` targets without the `swift_module` tag) and the + # module's headers include those. This will likely over-estimate the + # needed inputs, but we can't do better without include scanning in + # Starlark. + transitive_inputs.append(cc_info.compilation_context.headers) + + # Some rules still use the `umbrella_header` field to propagate a header + # that they don't also include in `CcInfo.compilation_context.headers`, so + # we also need to pull these in for the time being. + # TODO(b/142867898): This can be removed once the Swift rules start + # generating its own module map for these targets. + if objc_info: + transitive_inputs.append(objc_info.umbrella_header) for module in modules: clang_module = module.clang + + # Add the module map, which we use for both implicit and explicit module + # builds. module_map = clang_module.module_map + if not module.is_system and not types.is_string(module_map): + direct_inputs.append(module_map) + if prefer_precompiled_modules: - # If the build prefers precompiled modules, use the .pcm if it - # exists; otherwise, use the textual module map and the headers for - # that module (because we only want to propagate the headers that - # are required, not the full transitive set). precompiled_module = clang_module.precompiled_module if precompiled_module: - module_inputs.append(precompiled_module) + # For builds preferring explicit modules, use it if we have it + # and don't include any headers as inputs. + direct_inputs.append(precompiled_module) else: - module_inputs.extend( - clang_module.compilation_context.direct_headers, - ) - module_inputs.extend( - clang_module.compilation_context.direct_textual_headers, + # If we don't have an explicit module, we need the transitive + # headers from the compilation context associated with the + # module. This will likely overestimate the headers that will + # actually be used in the action, but until we can use include + # scanning from Starlark, we can't compute a more precise input + # set. + transitive_inputs.append( + clang_module.compilation_context.headers, ) - # Add the module map, which we use for both implicit and explicit module - # builds. For implicit module builds, we don't worry about the headers - # above because we collect the full transitive header set below. - module_inputs.append(module_map) - - # If we prefer textual module maps and headers for the build, fall back to - # using the full set of transitive headers. - if not prefer_precompiled_modules: - if objc_info: - header_depsets.append(objc_info.umbrella_header) - return swift_toolchain_config.config_result( - inputs = module_inputs, - transitive_inputs = header_depsets, + inputs = direct_inputs, + transitive_inputs = transitive_inputs, ) -def _clang_modulemap_dependency_args(module): - """Returns `swiftc` arguments for the module map of a Clang module. +def _clang_modulemap_dependency_args(module, ignore_system = True): + """Returns a `swiftc` argument for the module map of a Clang module. Args: module: A struct containing information about the module, as defined by `swift_common.create_module`. + ignore_system: If `True` and the module is a system module, no flag + should be returned. Defaults to `True`. Returns: - A list of arguments to pass to `swiftc`. + A list of arguments, possibly empty, to pass to `swiftc` (without the + `-Xcc` prefix). """ - return [ - "-Xcc", - "-fmodule-map-file={}".format(module.clang.module_map.path), - ] + if module.is_system and ignore_system: + return [] + + module_map = module.clang.module_map + if types.is_string(module_map): + module_map_path = module_map + else: + module_map_path = module_map.path + + return ["-fmodule-map-file={}".format(module_map_path)] def _clang_module_dependency_args(module): """Returns `swiftc` arguments for a precompiled Clang module, if possible. - If no precompiled module was emitted for this module, then this function - falls back to the textual module map. + If a precompiled module is present for this module, then flags for both it + and the module map are returned (the latter is required in order to map + headers to modules in some scenarios, since the precompiled modules are + passed by name). If no precompiled module is present for this module, then + this function falls back to the textual module map alone. Args: module: A struct containing information about the module, as defined by `swift_common.create_module`. Returns: - A list of arguments to pass to `swiftc`. + A list of arguments, possibly empty, to pass to `swiftc` (without the + `-Xcc` prefix). """ - args = [] if module.clang.precompiled_module: - args.extend([ - "-Xcc", + # If we're consuming an explicit module, we must also provide the + # textual module map, whether or not it's a system module. + return [ "-fmodule-file={}={}".format( module.name, module.clang.precompiled_module.path, ), - ]) - if module.clang.module_map: - args.extend(_clang_modulemap_dependency_args(module)) - return args + ] + _clang_modulemap_dependency_args(module, ignore_system = False) + else: + # If we have no explicit module, then only include module maps for + # non-system modules. + return _clang_modulemap_dependency_args(module) def _dependencies_clang_modulemaps_configurator(prerequisites, args): """Configures Clang module maps from dependencies.""" @@ -771,7 +1229,15 @@ def _dependencies_clang_modulemaps_configurator(prerequisites, args): if module.clang ] - args.add_all(modules, map_each = _clang_modulemap_dependency_args) + # Uniquify the arguments because different modules might be defined in the + # same module map file, so it only needs to be present once on the command + # line. + args.add_all( + modules, + before_each = "-Xcc", + map_each = _clang_modulemap_dependency_args, + uniquify = True, + ) return _collect_clang_module_inputs( cc_info = prerequisites.cc_info, @@ -789,7 +1255,15 @@ def _dependencies_clang_modules_configurator(prerequisites, args): if module.clang ] - args.add_all(modules, map_each = _clang_module_dependency_args) + # Uniquify the arguments because different modules might be defined in the + # same module map file, so it only needs to be present once on the command + # line. + args.add_all( + modules, + before_each = "-Xcc", + map_each = _clang_module_dependency_args, + uniquify = True, + ) return _collect_clang_module_inputs( cc_info = prerequisites.cc_info, @@ -806,6 +1280,18 @@ def _framework_search_paths_configurator(prerequisites, args): format_each = "-F%s", ) +def _pcm_additional_framework_search_paths_configurator(prerequisites, args): + """Add search paths for prebuilt frameworks to the command line for pcms. + + This is needed since swiftc doesn't pass the framework search paths to + clang, causing issues with framework style imports in headers. + """ + args.add_all( + prerequisites.cc_info.compilation_context.framework_includes, + before_each = "-Xcc", + format_each = "-F%s", + ) + def _static_frameworks_disable_autolink_configurator(prerequisites, args): """Add flags to disable auto-linking for static prebuilt frameworks. @@ -860,15 +1346,19 @@ def _module_name_configurator(prerequisites, args): """Adds the module name flag to the command line.""" args.add("-module-name", prerequisites.module_name) -def _stats_output_dir_configurator(prerequisites, args): - """Adds the compile stats output directory path to the command line.""" - args.add("-stats-output-dir", prerequisites.stats_directory.path) - def _source_files_configurator(prerequisites, args): """Adds source files to the command line and required inputs.""" args.add_all(prerequisites.source_files) + + # Only add source files to the input file set if they are not strings (for + # example, the module map of a system framework will be passed in as a file + # path relative to the SDK root, not as a `File` object). return swift_toolchain_config.config_result( - inputs = prerequisites.source_files, + inputs = [ + source_file + for source_file in prerequisites.source_files + if not types.is_string(source_file) + ], ) def _user_compile_flags_configurator(prerequisites, args): @@ -904,21 +1394,51 @@ def _is_wmo_manually_requested(user_compile_flags): Returns: True if WMO is enabled in the given list of flags. """ - return ("-wmo" in user_compile_flags or - "-whole-module-optimization" in user_compile_flags or - "-force-single-frontend-invocation" in user_compile_flags) + for copt in user_compile_flags: + if copt in _WMO_FLAGS: + return True + return False + +def features_from_swiftcopts(swiftcopts): + """Returns a list of features to enable based on `--swiftcopt` flags. + + Since `--swiftcopt` flags are hooked into the action configuration when the + toolchain is configured, it's not possible for individual actions to query + them easily if those flags may determine the nature of outputs (for example, + single- vs. multi-threaded WMO). The toolchain can call this function to map + those flags to private features that can be queried instead. + + Args: + swiftcopts: The list of command line flags that were passed using + `--swiftcopt`. + + Returns: + A list (possibly empty) of strings denoting feature names that should be + enabled on the toolchain. + """ + features = [] + if _is_wmo_manually_requested(user_compile_flags = swiftcopts): + features.append(SWIFT_FEATURE__WMO_IN_SWIFTCOPTS) + if _find_num_threads_flag_value(user_compile_flags = swiftcopts) == 0: + features.append(SWIFT_FEATURE__NUM_THREADS_0_IN_SWIFTCOPTS) + return features def _index_while_building_configurator(prerequisites, args): """Adds flags for index-store generation to the command line.""" if not _index_store_path_overridden(prerequisites.user_compile_flags): args.add("-index-store-path", prerequisites.indexstore_directory.path) +def _global_index_store_configurator(prerequisites, args): + """Adds flags for index-store generation to the command line.""" + out_dir = prerequisites.indexstore_directory.dirname.split("/")[0] + path = out_dir + "/_global_index_store" + args.add("-Xwrapped-swift=-global-index-store-import-path=" + path) + def _conditional_compilation_flag_configurator(prerequisites, args): """Adds (non-Clang) conditional compilation flags to the command line.""" all_defines = depset( prerequisites.defines, transitive = [ - prerequisites.transitive_defines, # Take any Swift-compatible defines from Objective-C dependencies # and define them for Swift. prerequisites.cc_info.compilation_context.defines, @@ -942,14 +1462,57 @@ def _additional_inputs_configurator(prerequisites, args): inputs = prerequisites.additional_inputs, ) +def _additional_objc_copts_configurator(additional_objc_copts, prerequisites, args): + """Adds additional Objective-C compiler flags to the command line.""" + _unused = [prerequisites] + args.add_all( + additional_objc_copts, + before_each = "-Xcc", + ) + +def _additional_swiftc_copts_configurator(additional_swiftc_copts, prerequisites, args): + """Adds additional Swift compiler flags to the command line.""" + _unused = [prerequisites] + args.add_all(additional_swiftc_copts) + +def _module_name_safe(string): + """Returns a transformation of `string` that is safe for module names.""" + result = "" + saw_non_identifier_char = False + for ch in string.elems(): + if ch.isalnum() or ch == "_": + # If we're seeing an identifier character after a sequence of + # non-identifier characters, append an underscore and reset our + # tracking state before appending the identifier character. + if saw_non_identifier_char: + result += "_" + saw_non_identifier_char = False + result += ch + elif result: + # Only track this if `result` has content; this ensures that we + # (intentionally) drop leading non-identifier characters instead of + # adding a leading underscore. + saw_non_identifier_char = True + + return result + def derive_module_name(*args): """Returns a derived module name from the given build label. For targets whose module name is not explicitly specified, the module name - is computed by creating an underscore-delimited string from the components - of the label, replacing any non-identifier characters also with underscores. + is computed using the following algorithm: - This mapping is not intended to be reversible. + * The package and name components of the label are considered separately. + All _interior_ sequences of non-identifier characters (anything other + than `a-z`, `A-Z`, `0-9`, and `_`) are replaced by a single underscore + (`_`). Any leading or trailing non-identifier characters are dropped. + * If the package component is non-empty after the above transformation, + it is joined with the transformed name component using an underscore. + Otherwise, the transformed name is used by itself. + * If this would result in a string that begins with a digit (`0-9`), an + underscore is prepended to make it identifier-safe. + + This mapping is intended to be fairly predictable, but not reversible. Args: *args: Either a single argument of type `Label`, or two arguments of @@ -974,12 +1537,15 @@ def derive_module_name(*args): fail("derive_module_name may only be called with a single argument " + "of type 'Label' or two arguments of type 'str'.") - package_part = (package.lstrip("//").replace("/", "_").replace("-", "_") - .replace(".", "_")) - name_part = name.replace("-", "_") + package_part = _module_name_safe(package.lstrip("//")) + name_part = _module_name_safe(name) if package_part: - return package_part + "_" + name_part - return name_part + module_name = package_part + "_" + name_part + else: + module_name = name_part + if module_name[0].isdigit(): + module_name = "_" + module_name + return module_name def compile( *, @@ -989,13 +1555,15 @@ def compile( srcs, swift_toolchain, target_name, + workspace_name, additional_inputs = [], bin_dir = None, copts = [], defines = [], deps = [], generated_header_name = None, - genfiles_dir = None): + genfiles_dir = None, + private_deps = []): """Compiles a Swift module. Args: @@ -1010,6 +1578,9 @@ def compile( target_name: The name of the target for which the code is being compiled, which is used to determine unique file paths for the outputs. + workspace_name: The name of the workspace for which the code is being + compiled, which is used to determine unique file paths for some + outputs. additional_inputs: A list of `File`s representing additional input files that need to be passed to the Swift compile action because they are referenced by compiler flags. @@ -1024,16 +1595,23 @@ def compile( determine whether whole module optimization is being requested, which affects the nature of the output files. defines: Symbols that should be defined by passing `-D` to the compiler. - deps: Dependencies of the target being compiled. These targets must - propagate one of the following providers: `CcInfo`, `SwiftInfo`, or - `apple_common.Objc`. + deps: Non-private dependencies of the target being compiled. These + targets are used as dependencies of both the Swift module being + compiled and the Clang module for the generated header. These + targets must propagate one of the following providers: `CcInfo`, + `SwiftInfo`, or `apple_common.Objc`. generated_header_name: The name of the Objective-C generated header that - should be generated for this module. If omitted, the name - `${target_name}-Swift.h` will be used. + should be generated for this module. If omitted, no header will be + generated. genfiles_dir: The Bazel `*-genfiles` directory root. If provided, its path is added to ClangImporter's header search paths for compatibility with Bazel's C++ and Objective-C rules which support inclusions of generated headers from that location. + private_deps: Private (implementation-only) dependencies of the target + being compiled. These are only used as dependencies of the Swift + module, not of the Clang module for the generated header. These + targets must propagate one of the following providers: `CcInfo`, + `SwiftInfo`, or `apple_common.Objc`. Returns: A `struct` containing the following fields: @@ -1058,9 +1636,9 @@ def compile( never None. * `object_files`: A list of `.o` files that were produced by the compiler. - * `stats_directory`: A `File` representing the directory that contains - the timing statistics emitted by the compiler. If no stats were - requested, this field will be None. + * `precompiled_module`: A `File` representing the explicit module + (`.pcm`) of the Clang module for the generated header, or `None` if + no explicit module was generated. * `swiftdoc`: The `.swiftdoc` file that was produced by the compiler. * `swiftinterface`: The `.swiftinterface` file that was produced by the compiler. If no interface file was produced (because the @@ -1069,39 +1647,71 @@ def compile( * `swiftmodule`: The `.swiftmodule` file that was produced by the compiler. """ + + # Collect the `SwiftInfo` providers that represent the dependencies of the + # Objective-C generated header module -- this includes the dependencies of + # the Swift module, plus any additional dependencies that the toolchain says + # are required for all generated header modules. These are used immediately + # below to write the module map for the header's module (to provide the + # `use` declarations), and later in this function when precompiling the + # module. + generated_module_deps_swift_infos = ( + get_providers(deps, SwiftInfo) + + swift_toolchain.generated_header_module_implicit_deps_providers.swift_infos + ) + compile_outputs, other_outputs = _declare_compile_outputs( + srcs = srcs, actions = actions, - generated_header_name = generated_header_name, feature_configuration = feature_configuration, + generated_header_name = generated_header_name, + generated_module_deps_swift_infos = generated_module_deps_swift_infos, module_name = module_name, - srcs = srcs, target_name = target_name, - user_compile_flags = copts + swift_toolchain.command_line_copts, + user_compile_flags = copts, ) - all_compile_outputs = compact([ - # The `.swiftmodule` file is explicitly listed as the first output - # because it will always exist and because Bazel uses it as a key for - # various things (such as the filename prefix for param files generated - # for that action). This guarantees some predictability. - compile_outputs.swiftmodule_file, - compile_outputs.swiftdoc_file, - compile_outputs.swiftinterface_file, - compile_outputs.generated_header_file, - compile_outputs.indexstore_directory, - compile_outputs.stats_directory, - ]) + compile_outputs.object_files + other_outputs + + split_derived_file_generation = is_feature_enabled( + feature_configuration = feature_configuration, + feature_name = SWIFT_FEATURE_SPLIT_DERIVED_FILES_GENERATION, + ) + + if split_derived_file_generation: + all_compile_outputs = compact([ + compile_outputs.swiftinterface_file, + compile_outputs.indexstore_directory, + ]) + compile_outputs.object_files + all_derived_outputs = compact([ + # The `.swiftmodule` file is explicitly listed as the first output + # because it will always exist and because Bazel uses it as a key for + # various things (such as the filename prefix for param files generated + # for that action). This guarantees some predictability. + compile_outputs.swiftmodule_file, + compile_outputs.swiftdoc_file, + compile_outputs.generated_header_file, + ]) + other_outputs + else: + all_compile_outputs = compact([ + # The `.swiftmodule` file is explicitly listed as the first output + # because it will always exist and because Bazel uses it as a key for + # various things (such as the filename prefix for param files generated + # for that action). This guarantees some predictability. + compile_outputs.swiftmodule_file, + compile_outputs.swiftdoc_file, + compile_outputs.swiftinterface_file, + compile_outputs.generated_header_file, + compile_outputs.indexstore_directory, + ]) + compile_outputs.object_files + other_outputs + all_derived_outputs = [] # Merge the providers from our dependencies so that we have one each for # `SwiftInfo`, `CcInfo`, and `apple_common.Objc`. Then we can pass these # into the action prerequisites so that configurators have easy access to # the full set of values and inputs through a single accessor. - all_deps = deps + get_implicit_deps( - feature_configuration = feature_configuration, - swift_toolchain = swift_toolchain, - ) merged_providers = _merge_targets_providers( + implicit_deps_providers = swift_toolchain.implicit_deps_providers, supports_objc_interop = swift_toolchain.supports_objc_interop, - targets = all_deps, + targets = deps + private_deps, ) # Flattening this `depset` is necessary because we need to extract the @@ -1114,15 +1724,22 @@ def compile( merged_providers.swift_info.transitive_modules.to_list() ) + transitive_swiftmodules = [] + defines_set = sets.make(defines) + for module in transitive_modules: + swift_module = module.swift + if not swift_module: + continue + transitive_swiftmodules.append(swift_module.swiftmodule) + if swift_module.defines: + defines_set = sets.union( + defines_set, + sets.make(swift_module.defines), + ) + # We need this when generating the VFS overlay file and also when # configuring inputs for the compile action, so it's best to precompute it # here. - transitive_swiftmodules = [ - module.swift.swiftmodule - for module in transitive_modules - if module.swift - ] - if is_feature_enabled( feature_configuration = feature_configuration, feature_name = SWIFT_FEATURE_VFSOVERLAY, @@ -1144,7 +1761,7 @@ def compile( additional_inputs = additional_inputs, bin_dir = bin_dir, cc_info = merged_providers.cc_info, - defines = defines, + defines = sets.to_list(defines_set), genfiles_dir = genfiles_dir, is_swift = True, module_name = module_name, @@ -1153,28 +1770,69 @@ def compile( ), objc_info = merged_providers.objc_info, source_files = srcs, - transitive_defines = merged_providers.swift_info.transitive_defines, transitive_modules = transitive_modules, transitive_swiftmodules = transitive_swiftmodules, - user_compile_flags = copts + swift_toolchain.command_line_copts, + user_compile_flags = copts, vfsoverlay_file = vfsoverlay_file, vfsoverlay_search_path = _SWIFTMODULES_VFS_ROOT, + workspace_name = workspace_name, # Merge the compile outputs into the prerequisites. **struct_fields(compile_outputs) ) + if split_derived_file_generation: + run_toolchain_action( + actions = actions, + action_name = swift_action_names.DERIVE_FILES, + feature_configuration = feature_configuration, + outputs = all_derived_outputs, + prerequisites = prerequisites, + progress_message = ( + "Generating derived files for Swift module {}".format(module_name) + ), + swift_toolchain = swift_toolchain, + ) + run_toolchain_action( actions = actions, action_name = swift_action_names.COMPILE, feature_configuration = feature_configuration, outputs = all_compile_outputs, prerequisites = prerequisites, - progress_message = ( - "Compiling Swift module {}".format(module_name) - ), + progress_message = "Compiling Swift module {}".format(module_name), swift_toolchain = swift_toolchain, ) + # If a header and module map were generated for this Swift module, attempt + # to precompile the explicit module for that header as well. + if generated_header_name and not is_feature_enabled( + feature_configuration = feature_configuration, + feature_name = SWIFT_FEATURE_NO_GENERATED_MODULE_MAP, + ): + compilation_context_to_compile = ( + compilation_context_for_explicit_module_compilation( + compilation_contexts = [cc_common.create_compilation_context( + headers = depset([compile_outputs.generated_header_file]), + )], + deps = deps, + ) + ) + precompiled_module = _precompile_clang_module( + actions = actions, + bin_dir = bin_dir, + cc_compilation_context = compilation_context_to_compile, + feature_configuration = feature_configuration, + genfiles_dir = genfiles_dir, + is_swift_generated_header = True, + module_map_file = compile_outputs.generated_module_map_file, + module_name = module_name, + swift_infos = generated_module_deps_swift_infos, + swift_toolchain = swift_toolchain, + target_name = target_name, + ) + else: + precompiled_module = None + # As part of the full compilation flow, register additional post-compile # actions that toolchains may conditionally support for their target # platform, like module-wrap or autolink-extract. @@ -1197,7 +1855,7 @@ def compile( compile_outputs.object_files + post_compile_results.additional_object_files ), - stats_directory = compile_outputs.stats_directory, + precompiled_module = precompiled_module, swiftdoc = compile_outputs.swiftdoc_file, swiftinterface = compile_outputs.swiftinterface_file, swiftmodule = compile_outputs.swiftmodule_file, @@ -1214,7 +1872,7 @@ def precompile_clang_module( target_name, bin_dir = None, genfiles_dir = None, - swift_info = None): + swift_infos = []): """Precompiles an explicit Clang module that is compatible with Swift. Args: @@ -1247,8 +1905,76 @@ def precompile_clang_module( path is added to ClangImporter's header search paths for compatibility with Bazel's C++ and Objective-C rules which support inclusions of generated headers from that location. - swift_info: A `SwiftInfo` provider that contains dependencies required - to compile this module. + swift_infos: A list of `SwiftInfo` providers representing dependencies + required to compile this module. + + Returns: + A `File` representing the precompiled module (`.pcm`) file, or `None` if + the toolchain or target does not support precompiled modules. + """ + return _precompile_clang_module( + actions = actions, + bin_dir = bin_dir, + cc_compilation_context = cc_compilation_context, + feature_configuration = feature_configuration, + genfiles_dir = genfiles_dir, + is_swift_generated_header = False, + module_map_file = module_map_file, + module_name = module_name, + swift_infos = swift_infos, + swift_toolchain = swift_toolchain, + target_name = target_name, + ) + +def _precompile_clang_module( + *, + actions, + cc_compilation_context, + feature_configuration, + is_swift_generated_header, + module_map_file, + module_name, + swift_toolchain, + target_name, + bin_dir = None, + genfiles_dir = None, + swift_infos = []): + """Precompiles an explicit Clang module that is compatible with Swift. + + Args: + actions: The context's `actions` object. + cc_compilation_context: A `CcCompilationContext` that contains headers + and other information needed to compile this module. This + compilation context should contain all headers required to compile + the module, which includes the headers for the module itself *and* + any others that must be present on the file system/in the sandbox + for compilation to succeed. The latter typically refers to the set + of headers of the direct dependencies of the module being compiled, + which Clang needs to be physically present before it detects that + they belong to one of the precompiled module dependencies. + feature_configuration: A feature configuration obtained from + `swift_common.configure_features`. + is_swift_generated_header: If True, the action is compiling the + Objective-C header generated by the Swift compiler for a module. + module_map_file: A textual module map file that defines the Clang module + to be compiled. + module_name: The name of the top-level module in the module map that + will be compiled. + swift_toolchain: The `SwiftToolchainInfo` provider of the toolchain. + target_name: The name of the target for which the code is being + compiled, which is used to determine unique file paths for the + outputs. + bin_dir: The Bazel `*-bin` directory root. If provided, its path is used + to store the cache for modules precompiled by Swift's ClangImporter, + and it is added to ClangImporter's header search paths for + compatibility with Bazel's C++ and Objective-C rules which support + includes of generated headers from that location. + genfiles_dir: The Bazel `*-genfiles` directory root. If provided, its + path is added to ClangImporter's header search paths for + compatibility with Bazel's C++ and Objective-C rules which support + inclusions of generated headers from that location. + swift_infos: A list of `SwiftInfo` providers representing dependencies + required to compile this module. Returns: A `File` representing the precompiled module (`.pcm`) file, or `None` if @@ -1274,8 +2000,20 @@ def precompile_clang_module( target_name = target_name, ) - if swift_info: - transitive_modules = swift_info.transitive_modules.to_list() + if not is_swift_generated_header: + implicit_swift_infos = ( + swift_toolchain.clang_implicit_deps_providers.swift_infos + ) + else: + implicit_swift_infos = [] + + if not is_swift_generated_header and implicit_swift_infos: + swift_infos = list(swift_infos) + swift_infos.extend(implicit_swift_infos) + + if swift_infos: + merged_swift_info = create_swift_info(swift_infos = swift_infos) + transitive_modules = merged_swift_info.transitive_modules.to_list() else: transitive_modules = [] @@ -1284,6 +2022,7 @@ def precompile_clang_module( cc_info = CcInfo(compilation_context = cc_compilation_context), genfiles_dir = genfiles_dir, is_swift = False, + is_swift_generated_header = is_swift_generated_header, module_name = module_name, objc_include_paths_workaround = depset(), objc_info = apple_common.new_objc_provider(), @@ -1304,30 +2043,12 @@ def precompile_clang_module( return precompiled_module -def get_implicit_deps(feature_configuration, swift_toolchain): - """Gets the list of implicit dependencies from the toolchain. - - Args: - feature_configuration: The feature configuration, which determines - whether optional implicit dependencies are included. - swift_toolchain: The Swift toolchain. - - Returns: - A list of targets that should be treated as implicit dependencies of - the toolchain under the given feature configuration. - """ - deps = list(swift_toolchain.required_implicit_deps) - if not is_feature_enabled( - feature_configuration = feature_configuration, - feature_name = SWIFT_FEATURE_MINIMAL_DEPS, - ): - deps.extend(swift_toolchain.optional_implicit_deps) - return deps - def _declare_compile_outputs( + *, actions, - generated_header_name, feature_configuration, + generated_header_name, + generated_module_deps_swift_infos, module_name, srcs, target_name, @@ -1336,10 +2057,13 @@ def _declare_compile_outputs( Args: actions: The object used to register actions. - generated_header_name: The desired name of the generated header for this - module, or `None` to use `${target_name}-Swift.h`. feature_configuration: A feature configuration obtained from `swift_common.configure_features`. + generated_header_name: The desired name of the generated header for this + module, or `None` if no header should be generated. + generated_module_deps_swift_infos: `SwiftInfo` providers from + dependencies of the module for the generated header of the target + being compiled. module_name: The name of the Swift module being compiled. srcs: The list of source files that will be compiled. target_name: The name (excluding package path) of the target being @@ -1383,33 +2107,13 @@ def _declare_compile_outputs( else: swiftinterface_file = None - if is_feature_enabled( - feature_configuration = feature_configuration, - feature_name = SWIFT_FEATURE_COMPILE_STATS, - ): - stats_directory = derived_files.stats_directory( + # If requested, generate the Swift header for this library so that it can be + # included by Objective-C code that depends on it. + if generated_header_name: + generated_header = _declare_validated_generated_header( actions = actions, - target_name = target_name, + generated_header_name = generated_header_name, ) - else: - stats_directory = None - - # If supported, generate the Swift header for this library so that it can be - # included by Objective-C code that depends on it. - if not is_feature_enabled( - feature_configuration = feature_configuration, - feature_name = SWIFT_FEATURE_NO_GENERATED_HEADER, - ): - if generated_header_name: - generated_header = _declare_validated_generated_header( - actions = actions, - generated_header_name = generated_header_name, - ) - else: - generated_header = derived_files.default_generated_header( - actions = actions, - target_name = target_name, - ) else: generated_header = None @@ -1425,27 +2129,34 @@ def _declare_compile_outputs( feature_configuration = feature_configuration, feature_name = SWIFT_FEATURE_NO_GENERATED_MODULE_MAP, ): + # Collect the names of Clang modules that the module being built + # directly depends on. + dependent_module_names = sets.make() + for swift_info in generated_module_deps_swift_infos: + for module in swift_info.direct_modules: + if module.clang: + sets.insert(dependent_module_names, module.name) + generated_module_map = derived_files.module_map( actions = actions, target_name = target_name, ) - _write_objc_header_module_map( + write_module_map( actions = actions, + dependent_module_names = sorted( + sets.to_list(dependent_module_names), + ), + module_map_file = generated_module_map, module_name = module_name, - objc_header = generated_header, - output = generated_module_map, + public_headers = [generated_header], ) else: generated_module_map = None # Now, declare outputs like object files for which there may be one or many, # depending on the compilation mode. - is_wmo_implied_by_features = are_all_features_enabled( - feature_configuration = feature_configuration, - feature_names = [SWIFT_FEATURE_OPT, SWIFT_FEATURE_OPT_USES_WMO], - ) output_nature = _emitted_output_nature( - is_wmo_implied_by_features = is_wmo_implied_by_features, + feature_configuration = feature_configuration, user_compile_flags = user_compile_flags, ) @@ -1496,7 +2207,6 @@ def _declare_compile_outputs( indexstore_directory = indexstore_directory, object_files = object_files, output_file_map = output_file_map, - stats_directory = stats_directory, swiftdoc_file = swiftdoc_file, swiftinterface_file = swiftinterface_file, swiftmodule_file = swiftmodule_file, @@ -1588,8 +2298,7 @@ def _declare_multiple_outputs_and_write_output_file_map( def _declare_validated_generated_header(actions, generated_header_name): """Validates and declares the explicitly named generated header. - If the file does not have a `.h` extension or conatins path separators, the - build will fail. + If the file does not have a `.h` extension, the build will fail. Args: actions: The context's `actions` object. @@ -1598,12 +2307,6 @@ def _declare_validated_generated_header(actions, generated_header_name): Returns: A `File` that should be used as the output for the generated header. """ - if "/" in generated_header_name: - fail( - "The generated header for a Swift module may not contain " + - "directory components (got '{}').".format(generated_header_name), - ) - extension = paths.split_extension(generated_header_name)[1] if extension != ".h": fail( @@ -1613,7 +2316,10 @@ def _declare_validated_generated_header(actions, generated_header_name): return actions.declare_file(generated_header_name) -def _merge_targets_providers(supports_objc_interop, targets): +def _merge_targets_providers( + implicit_deps_providers, + supports_objc_interop, + targets): """Merges the compilation-related providers for the given targets. This function merges the `CcInfo`, `SwiftInfo`, and `apple_common.Objc` @@ -1623,6 +2329,8 @@ def _merge_targets_providers(supports_objc_interop, targets): their data. Args: + implicit_deps_providers: The implicit deps providers `struct` from the + Swift toolchain. supports_objc_interop: `True` if the current toolchain supports Objective-C interop and the `apple_common.Objc` providers should also be used to determine compilation flags and inputs. If `False`, @@ -1640,9 +2348,9 @@ def _merge_targets_providers(supports_objc_interop, targets): * `objc_info`: The merged `apple_common.Objc` provider of the targets. * `swift_info`: The merged `SwiftInfo` provider of the targets. """ - cc_infos = [] - objc_infos = [] - swift_infos = [] + cc_infos = list(implicit_deps_providers.cc_infos) + objc_infos = list(implicit_deps_providers.objc_infos) + swift_infos = list(implicit_deps_providers.swift_infos) # TODO(b/146575101): This is only being used to preserve the current # behavior of strict Objective-C include paths being propagated one more @@ -1756,28 +2464,6 @@ def _register_post_compile_actions( linker_inputs = linker_inputs, ) -def find_swift_version_copt_value(copts): - """Returns the value of the `-swift-version` argument, if found. - - Args: - copts: The list of copts to be scanned. - - Returns: - The value of the `-swift-version` argument, or None if it was not found - in the copt list. - """ - - # Note that the argument can occur multiple times, and the last one wins. - last_swift_version = None - - count = len(copts) - for i in range(count): - copt = copts[i] - if copt == "-swift-version" and i + 1 < count: - last_swift_version = copts[i + 1] - - return last_swift_version - def new_objc_provider( deps, link_inputs, @@ -1785,7 +2471,8 @@ def new_objc_provider( module_map, static_archives, swiftmodules, - objc_header = None): + objc_header = None, + objc_providers = []): """Creates an `apple_common.Objc` provider for a Swift target. Args: @@ -1805,16 +2492,17 @@ def new_objc_provider( `None`, no headers will be propagated. This header is only needed for Swift code that defines classes that should be exposed to Objective-C. + objc_providers: Additional `apple_common.Objc` providers from transitive + dependencies not provided by the `deps` argument. Returns: An `apple_common.Objc` provider that should be returned by the calling rule. """ - objc_providers = get_providers(deps, apple_common.Objc) + all_objc_providers = get_providers(deps, apple_common.Objc) + objc_providers objc_provider_args = { "link_inputs": depset(direct = swiftmodules + link_inputs), - "providers": objc_providers, - "uses_swift": True, + "providers": all_objc_providers, } # The link action registered by `apple_binary` only looks at `Objc` @@ -1853,7 +2541,7 @@ def new_objc_provider( # direct deps' Objective-C module maps to dependents, because those Swift # modules still need to see them. We need to construct a new transitive objc # provider to get the correct strict propagation behavior. - transitive_objc_provider_args = {"providers": objc_providers} + transitive_objc_provider_args = {"providers": all_objc_providers} if module_map: transitive_objc_provider_args["module_map"] = depset( direct = [module_map], @@ -1884,11 +2572,6 @@ def output_groups_from_compilation_outputs(compilation_outputs): compilation_outputs.indexstore, ]) - if compilation_outputs.stats_directory: - output_groups["swift_compile_stats_direct"] = depset([ - compilation_outputs.stats_directory, - ]) - if compilation_outputs.swiftdoc: output_groups["swiftdoc"] = depset([ compilation_outputs.swiftdoc, @@ -1927,29 +2610,6 @@ def swift_library_output_map(name, alwayslink): "archive": "lib{}.{}".format(name, extension), } -def _write_objc_header_module_map( - actions, - module_name, - objc_header, - output): - """Writes a module map for a generated Swift header to a file. - - Args: - actions: The context's actions object. - module_name: The name of the Swift module. - objc_header: The `File` representing the generated header. - output: The `File` to which the module map should be written. - """ - actions.write( - content = ('module "{module_name}" {{\n' + - ' header "../{header_name}"\n' + - "}}\n").format( - header_name = objc_header.basename, - module_name = module_name, - ), - output = output, - ) - def _index_store_path_overridden(copts): """Checks if index_while_building must be disabled. @@ -2008,7 +2668,30 @@ def _disable_autolink_framework_copts(framework_name): ], ) -def _emitted_output_nature(is_wmo_implied_by_features, user_compile_flags): +def _find_num_threads_flag_value(user_compile_flags): + """Finds the value of the `-num-threads` flag. + + This function looks for the `-num-threads` flag and returns the + corresponding value if found. If the flag is present multiple times, the + last value is the one returned. + + Args: + user_compile_flags: The options passed into the compile action. + + Returns: + The numeric value of the `-num-threads` flag if found, otherwise `None`. + """ + num_threads = None + saw_num_threads = False + for copt in user_compile_flags: + if saw_num_threads: + saw_num_threads = False + num_threads = _safe_int(copt) + elif copt == "-num-threads": + saw_num_threads = True + return num_threads + +def _emitted_output_nature(feature_configuration, user_compile_flags): """Returns information about the nature of emitted compilation outputs. The compiler emits a single object if it is invoked with whole-module @@ -2018,8 +2701,8 @@ def _emitted_output_nature(is_wmo_implied_by_features, user_compile_flags): thread count,_ so we have to treat that case separately. Args: - is_wmo_implied_by_features: Whether WMO is implied by features set in - the feature configuration. + feature_configuration: The feature configuration for the current + compilation. user_compile_flags: The options passed into the compile action. Returns: @@ -2033,35 +2716,28 @@ def _emitted_output_nature(is_wmo_implied_by_features, user_compile_flags): compilation action with the given flags. """ is_wmo = ( - is_wmo_implied_by_features or + is_feature_enabled( + feature_configuration = feature_configuration, + feature_name = SWIFT_FEATURE__WMO_IN_SWIFTCOPTS, + ) or + are_all_features_enabled( + feature_configuration = feature_configuration, + feature_names = [SWIFT_FEATURE_OPT, SWIFT_FEATURE_OPT_USES_WMO], + ) or _is_wmo_manually_requested(user_compile_flags) ) - saw_space_separated_num_threads = False - - # If WMO is enabled, the action config will automatically add - # `-num-threads 12` to the command line. We need to stage that as our - # initial default here to ensure that we return the right value if the user - # compile flags don't otherwise override it. - # - # 0 is the only option that makes swift emit a single object file, anything - # greater and it uses that number of threads to emit multiple objects - num_threads = _DEFAULT_WMO_THREAD_COUNT if is_wmo else 0 - - for copt in user_compile_flags: - if saw_space_separated_num_threads: - saw_space_separated_num_threads = False - num_threads = _safe_int(copt) - elif copt == "-num-threads": - saw_space_separated_num_threads = True - elif copt.startswith("-num-threads="): - num_threads = _safe_int(copt.split("=")[1]) - - if num_threads == None or num_threads < 0: - fail("The value of '-num-threads' must be a positive integer.") + # We check the feature first because that implies that `-num-threads 0` was + # present in `--swiftcopt`, which overrides all other flags (like the user + # compile flags, which come from the target's `copts`). Only fallback to + # checking the flags if the feature is disabled. + is_single_threaded = is_feature_enabled( + feature_configuration = feature_configuration, + feature_name = SWIFT_FEATURE__NUM_THREADS_0_IN_SWIFTCOPTS, + ) or _find_num_threads_flag_value(user_compile_flags) == 0 return struct( - emits_multiple_objects = not (is_wmo and num_threads == 0), + emits_multiple_objects = not (is_wmo and is_single_threaded), emits_partial_modules = not is_wmo, ) diff --git a/swift/internal/derived_files.bzl b/swift/internal/derived_files.bzl index ee57fce17..fa9ab4f88 100644 --- a/swift/internal/derived_files.bzl +++ b/swift/internal/derived_files.bzl @@ -29,18 +29,6 @@ def _autolink_flags(actions, target_name): """ return actions.declare_file("{}.autolink".format(target_name)) -def _default_generated_header(actions, target_name): - """Declares the automatically-named generated header for a Swift target. - - Args: - actions: The context's actions object. - target_name: The name of the target being built. - - Returns: - The declared `File`. - """ - return actions.declare_file("{}-Swift.h".format(target_name)) - def _executable(actions, target_name): """Declares a file for the executable created by a binary or test rule. @@ -80,11 +68,10 @@ def _intermediate_frontend_file_path(target_name, src): target and source file should be stored. """ objs_dir = "{}_objs".format(target_name) - owner_rel_path = owner_relative_path(src) - # TODO(b/131185317): Remove this once ar_wrapper handles filenames with - # spaces correctly. - safe_name = src.basename.replace(" ", "__SPACE__") + owner_rel_path = owner_relative_path(src).replace(" ", "__SPACE__") + safe_name = paths.basename(owner_rel_path) + return paths.join(objs_dir, paths.dirname(owner_rel_path)), safe_name def _intermediate_object_file(actions, target_name, src): @@ -109,7 +96,11 @@ def _intermediate_object_file(actions, target_name, src): ) def _module_map(actions, target_name): - """Declares the module map for the generated header of a target. + """Declares the module map for a target. + + These module maps are used when generating a Swift-compatible module map for + a C/Objective-C target, and also when generating the module map for the + generated header of a Swift target. Args: actions: The context's actions object. @@ -118,9 +109,7 @@ def _module_map(actions, target_name): Returns: The declared `File`. """ - return actions.declare_file( - "{}.modulemaps/module.modulemap".format(target_name), - ) + return actions.declare_file("{}.swift.modulemap".format(target_name)) def _modulewrap_object(actions, target_name): """Declares the object file used to wrap Swift modules for ELF binaries. @@ -197,18 +186,6 @@ def _static_archive(actions, alwayslink, link_name): extension = "lo" if alwayslink else "a" return actions.declare_file("lib{}.{}".format(link_name, extension)) -def _stats_directory(actions, target_name): - """Declares a directory that will contain timing statistics for a compile. - - Args: - actions: The context's actions object. - target_name: The name of the target being built. - - Returns: - The declared `File`. - """ - return actions.declare_directory("{}.compile-stats".format(target_name)) - def _swiftc_output_file_map(actions, target_name): """Declares a file for the output file map for a compilation action. @@ -319,7 +296,6 @@ def _xctest_runner_script(actions, target_name): derived_files = struct( autolink_flags = _autolink_flags, - default_generated_header = _default_generated_header, executable = _executable, indexstore_directory = _indexstore_directory, intermediate_object_file = _intermediate_object_file, @@ -329,7 +305,6 @@ derived_files = struct( precompiled_module = _precompiled_module, reexport_modules_src = _reexport_modules_src, static_archive = _static_archive, - stats_directory = _stats_directory, swiftc_output_file_map = _swiftc_output_file_map, swiftdoc = _swiftdoc, swiftinterface = _swiftinterface, diff --git a/swift/internal/feature_names.bzl b/swift/internal/feature_names.bzl index ee01a3e77..318235d0c 100644 --- a/swift/internal/feature_names.bzl +++ b/swift/internal/feature_names.bzl @@ -47,26 +47,38 @@ SWIFT_FEATURE_COVERAGE = "swift.coverage" # `swift_test` output just a binary. SWIFT_FEATURE_BUNDLED_XCTESTS = "swift.bundled_xctests" -# If enabled, the Swift compiler will emit a directory containing JSON files -# with timing information about the driver/frontend's actions. This directory -# will be emitted as a default output when a Swift target is built directly, and -# it will also be propagated in an output group named -# `swift_compile_stats_direct`. Typically this output group will not be accessed -# directly, but used by the `collect_swift_compile_stats` aspect to gather the -# transitive stats for the entire build. -SWIFT_FEATURE_COMPILE_STATS = "swift.compile_stats" - # If enabled, debug builds will use the `-debug-prefix-map` feature to remap the # current working directory to `.`, which permits debugging remote or sandboxed # builds. SWIFT_FEATURE_DEBUG_PREFIX_MAP = "swift.debug_prefix_map" +# If enabled, coverage builds will use the `-coverage-prefix-map` feature to +# remap the current working directory to `.`, which increases reproducibility +# of remote builds. +SWIFT_FEATURE_COVERAGE_PREFIX_MAP = "swift.coverage_prefix_map" + # If enabled, C and Objective-C libraries that are direct or transitive # dependencies of a Swift library will emit explicit precompiled modules that # are compatible with Swift's ClangImporter and propagate them up the build # graph. SWIFT_FEATURE_EMIT_C_MODULE = "swift.emit_c_module" +# If enabled, when compiling an explicit C or Objectve-C module, every header +# included by the module being compiled must belong to one of the modules listed +# in its dependencies. This is ignored for system modules. +SWIFT_FEATURE_LAYERING_CHECK = "swift.layering_check" + +# If enabled, the C or Objective-C target should be compiled as a system module. +SWIFT_FEATURE_SYSTEM_MODULE = "swift.system_module" + +# If enabled, the `-Xcc -fsystem-module` flag will be passed when compiling a +# system C/Objective-C module (with feature `swift.system_module`) because the +# compiler is new enough to honor it correctly. If disabled, we attempt to mimic +# this by disabling certain warnings; however, this unfortunately causes `UInt` +# APIs to be imported by ClangImporter as `UInt` instead of `Int` because +# ClangImporter doesn't recognize them as true system modules. +SWIFT_FEATURE_SUPPORTS_SYSTEM_MODULE_FLAG = "swift.supports_system_module_flag" + # If enabled, Swift compilation actions will use batch mode by passing # `-enable-batch-mode` to `swiftc`. This is a new compilation mode as of # Swift 4.2 that is intended to speed up non-incremental non-WMO builds by @@ -86,21 +98,15 @@ SWIFT_FEATURE_ENABLE_TESTING = "swift.enable_testing" # warnings otherwise. SWIFT_FEATURE_FULL_DEBUG_INFO = "swift.full_debug_info" -# If enabled, ClangImporter will perform implicit search for module maps and -# compile modules in the implicit module cache for any that were not provided -# explicitly on the command line. Otherwise, all modules must be provided -# explicitly. -SWIFT_FEATURE_IMPLICIT_MODULES = "swift.implicit_modules" - # If enabled, the compilation action for a target will produce an index store. +# https://docs.google.com/document/d/1cH2sTpgSnJZCkZtJl1aY-rzy4uGPcrI-6RrUpdATO2Q/ SWIFT_FEATURE_INDEX_WHILE_BUILDING = "swift.index_while_building" -# If enabled, Swift libraries, binaries, and tests will only have automatic -# dependencies on the targets provided by the toolchain's -# `required_implicit_deps` field but not those in the `optional_implicit_deps` -# field. Users may still explicitly list the latter in the `deps` of their -# targets if they are needed. -SWIFT_FEATURE_MINIMAL_DEPS = "swift.minimal_deps" +# If enabled the compilation action will not produce indexes for system modules. +SWIFT_FEATURE_DISABLE_SYSTEM_INDEX = "swift.disable_system_index" + +# Index while building - using a global index store cache +SWIFT_FEATURE_USE_GLOBAL_INDEX_STORE = "swift.use_global_index_store" # If enabled, compilation actions and module map generation will assume that the # header paths in module maps are relative to the current working directory @@ -116,14 +122,9 @@ SWIFT_FEATURE_MODULE_MAP_NO_PRIVATE_HEADERS = ( "swift.module_map_no_private_headers" ) -# If enabled, the compilation action for a library target will not generate an -# Objective-C header for the module. This feature also implies -# `swift.no_generated_module_map`. -SWIFT_FEATURE_NO_GENERATED_HEADER = "swift.no_generated_header" - # If enabled, the compilation action for a library target will not generate a -# module map for the Objective-C generated header. Note that this feature -# is ignored if `swift.no_generated_header` is present. +# module map for the Objective-C generated header. This feature is ignored if +# the target is not generating a header. SWIFT_FEATURE_NO_GENERATED_MODULE_MAP = "swift.no_generated_module_map" # If enabled, builds using the "opt" compilation mode will invoke `swiftc` with @@ -134,6 +135,11 @@ SWIFT_FEATURE_OPT_USES_WMO = "swift.opt_uses_wmo" # the `-Osize` flag instead of `-O`. SWIFT_FEATURE_OPT_USES_OSIZE = "swift.opt_uses_osize" +# If enabled, and if the toolchain specifies a generated header rewriting tool, +# that tool will be invoked after compilation to rewrite the generated header in +# place. +SWIFT_FEATURE_REWRITE_GENERATED_HEADER = "swift.rewrite_generated_header" + # If enabled, Swift compiler invocations will use precompiled modules from # dependencies instead of module maps and headers, if those dependencies provide # them. @@ -148,6 +154,29 @@ SWIFT_FEATURE_USE_C_MODULES = "swift.use_c_modules" # crashes. SWIFT_FEATURE_USE_GLOBAL_MODULE_CACHE = "swift.use_global_module_cache" +# If enabled, and Swift compilation actions will use the shared Clang module +# cache path written to +# `/private/tmp/__build_bazel_rules_swift/swift_module_cache/REPOSITORY_NAME`. +# This makes the embedded Clang module breadcrumbs deterministic between Bazel +# instances, because they are always embedded as absolute paths. Note that the +# use of this cache is non-hermetic--the cached modules are not wiped between +# builds, and won't be cleaned when invoking `bazel clean`; the user is +# responsible for manually cleaning them. +# +# Additionally, this can be used as a workaround for a bug in the Swift +# compiler that causes the module breadcrumbs to be embedded even though the +# `-no-clang-module-breadcrumbs` flag is passed +# (https://bugs.swift.org/browse/SR-13275). +# +# Since the source path of modulemaps might be different for the same module, +# (i.e. multiple checkouts of the same repository, or remote execution), +# multiple modules with different hashes can end up in the cache. This can +# result in build failures. Don't use this feature with sandboxing (or +# probably remote execution as well). +# +# This feature requires `swift.use_global_module_cache` to be enabled. +SWIFT_FEATURE_GLOBAL_MODULE_CACHE_USES_TMPDIR = "swift.global_module_cache_uses_tmpdir" + # If enabled, actions invoking the Swift driver or frontend may write argument # lists into response files (i.e., "@args.txt") to avoid passing command lines # that exceed the system limit. Toolchains typically set this automatically if @@ -200,3 +229,40 @@ SWIFT_FEATURE_SUPPORTS_PRIVATE_DEPS = "swift.supports_private_deps" # numbers of `-Wl,-add_ast_path,` flags to the linker do not overrun the # system command line limit. SWIFT_FEATURE_NO_EMBED_DEBUG_MODULE = "swift.no_embed_debug_module" + +# If enabled, the toolchain will directly generate from the raw proto files +# and not from the DescriptorSets. +# +# The DescriptorSets ProtoInfo exposes don't have source info, so comments in +# the .proto files don't get carried over to the generated Swift sources as +# documentation comments. https://github.com/bazelbuild/bazel/issues/9337 +# is open to attempt to get that, but this provides a way to opt into forcing +# it. +# +# This does come with a minor risk for cross repository and/or generated proto +# files where the protoc command line might not be crafted correctly, so it +# remains opt in. +SWIFT_FEATURE_GENERATE_FROM_RAW_PROTO_FILES = "swift.generate_from_raw_proto_files" + +# If enabled and whole module optimisation is being used, the `*.swiftdoc`, +# `*.swiftmodule` and `*-Swift.h` are generated with a separate action +# rather than as part of the compilation. +SWIFT_FEATURE_SPLIT_DERIVED_FILES_GENERATION = "swift.split_derived_files_generation" + +# If enabled the skip function bodies frontend flag is passed when using derived +# files generation. This requires Swift 5.2 +SWIFT_FEATURE_ENABLE_SKIP_FUNCTION_BODIES = "swift.skip_function_bodies_for_derived_files" + +# If enabled remap the absolute path to Xcode in debug info. When used with +# swift.coverage_prefix_map also remap the path in coverage data. +SWIFT_FEATURE_REMAP_XCODE_PATH = "swift.remap_xcode_path" + +# A private feature that is set by the toolchain if a flag enabling WMO was +# passed on the command line using `--swiftcopt`. Users should never manually +# enable, disable, or query this feature. +SWIFT_FEATURE__WMO_IN_SWIFTCOPTS = "swift._wmo_in_swiftcopts" + +# A private feature that is set by the toolchain if the flags `-num-threads 0` +# were passed on the command line using `--swiftcopt`. Users should never +# manually enable, disable, or query this feature. +SWIFT_FEATURE__NUM_THREADS_0_IN_SWIFTCOPTS = "swift._num_threads_0_in_swiftcopts" diff --git a/swift/internal/features.bzl b/swift/internal/features.bzl index cf6b264b3..2cd6cfc4b 100644 --- a/swift/internal/features.bzl +++ b/swift/internal/features.bzl @@ -14,7 +14,6 @@ """Helper functions for working with Bazel features.""" -load("@bazel_skylib//lib:collections.bzl", "collections") load("@bazel_skylib//lib:new_sets.bzl", "sets") load( ":feature_names.bzl", @@ -60,9 +59,10 @@ def configure_features( Args: ctx: The rule context. swift_toolchain: The `SwiftToolchainInfo` provider of the toolchain - being used to build. The C++ toolchain associated with the Swift - toolchain is used to create the underlying C++ feature - configuration. + being used to build. This is used to determine features that are + enabled by default or unsupported by the toolchain, and the C++ + toolchain associated with the Swift toolchain is used to create the + underlying C++ feature configuration. requested_features: The list of features to be enabled. This is typically obtained using the `ctx.features` field in a rule implementation function. @@ -72,48 +72,47 @@ def configure_features( Returns: An opaque value representing the feature configuration that can be - passed to other `swift_common` functions. + passed to other `swift_common` functions. Note that the structure of + this value should otherwise not be relied on or inspected directly. """ + if swift_toolchain.feature_allowlists: + _check_allowlists( + allowlists = swift_toolchain.feature_allowlists, + label = ctx.label, + requested_features = requested_features, + unsupported_features = unsupported_features, + ) # The features to enable for a particular rule/target are the ones requested # by the toolchain, plus the ones requested by the target itself, *minus* - # any that are explicitly disabled on the target itself. - requested_features_set = sets.make(swift_toolchain.requested_features) - requested_features_set = sets.union( - requested_features_set, + # any that are explicitly disabled on the target or the toolchain. + requestable_features_set = sets.make(swift_toolchain.requested_features) + requestable_features_set = sets.union( + requestable_features_set, sets.make(requested_features), ) - requested_features_set = sets.difference( - requested_features_set, + requestable_features_set = sets.difference( + requestable_features_set, sets.make(unsupported_features), ) - all_requested_features = sets.to_list(requested_features_set) - - all_unsupported_features = collections.uniq( - swift_toolchain.unsupported_features + unsupported_features, + requestable_features_set = sets.difference( + requestable_features_set, + sets.make(swift_toolchain.unsupported_features), ) - - # Verify the consistency of Swift features requested vs. those that are not - # supported by the toolchain. We don't need to do this for C++ features - # because `cc_common.configure_features` handles verifying those. - for feature in requested_features: - if feature.startswith("swift.") and feature in all_unsupported_features: - fail("Feature '{}' was requested, ".format(feature) + - "but it is not supported by the current toolchain or rule.") + requestable_features = sets.to_list(requestable_features_set) cc_feature_configuration = cc_common.configure_features( ctx = ctx, cc_toolchain = swift_toolchain.cc_toolchain_info, - requested_features = all_requested_features, - unsupported_features = all_unsupported_features, + requested_features = requestable_features, + unsupported_features = unsupported_features, ) return struct( - cc_feature_configuration = cc_feature_configuration, - requested_features = all_requested_features, - unsupported_features = all_unsupported_features, + _cc_feature_configuration = cc_feature_configuration, + _enabled_features = requestable_features, ) -def features_for_build_modes(ctx, objc_fragment = None): +def features_for_build_modes(ctx, objc_fragment = None, cpp_fragment = None): """Returns a list of Swift toolchain features for current build modes. This function explicitly breaks the "don't pass `ctx` as an argument" @@ -123,6 +122,7 @@ def features_for_build_modes(ctx, objc_fragment = None): Args: ctx: The current rule context. objc_fragment: The Objective-C configuration fragment, if available. + cpp_fragment: The Cpp configuration fragment, if available. Returns: A list of Swift toolchain features to enable. @@ -134,7 +134,13 @@ def features_for_build_modes(ctx, objc_fragment = None): features.append(SWIFT_FEATURE_COVERAGE) if compilation_mode in ("dbg", "fastbuild"): features.append(SWIFT_FEATURE_ENABLE_TESTING) - if objc_fragment and objc_fragment.generate_dsym: + + # TODO: Remove getattr once bazel is released with this change + if cpp_fragment and getattr(cpp_fragment, "apple_generate_dsym", False): + features.append(SWIFT_FEATURE_FULL_DEBUG_INFO) + + # TODO: Remove the objc_fragment usage once bazel is released with the C++ change + if objc_fragment and getattr(objc_fragment, "generate_dsym", False): features.append(SWIFT_FEATURE_FULL_DEBUG_INFO) return features @@ -150,7 +156,7 @@ def get_cc_feature_configuration(feature_configuration): [`cc_common.configure_features`](https://docs.bazel.build/versions/master/skylark/lib/cc_common.html#configure_features) for more information). """ - return feature_configuration.cc_feature_configuration + return feature_configuration._cc_feature_configuration def is_feature_enabled(feature_configuration, feature_name): """Returns `True` if the feature is enabled in the feature configuration. @@ -168,7 +174,7 @@ def is_feature_enabled(feature_configuration, feature_name): `True` if the given feature is enabled in the feature configuration. """ if feature_name.startswith("swift."): - return feature_name in feature_configuration.requested_features + return feature_name in feature_configuration._enabled_features else: return cc_common.is_enabled( feature_configuration = get_cc_feature_configuration( @@ -176,3 +182,98 @@ def is_feature_enabled(feature_configuration, feature_name): ), feature_name = feature_name, ) + +def _check_allowlists( + *, + allowlists, + label, + requested_features, + unsupported_features): + """Checks the toolchain's allowlists to verify the requested features. + + If any of the features requested to be enabled or disabled is not allowed in + the target's package by one of the allowlists, the build will fail with an + error message indicating the feature and the allowlist that denied it. + + Args: + allowlists: A list of `SwiftFeatureAllowlistInfo` providers that will be + checked. + label: The label of the target being checked against the allowlist. + requested_features: The list of features to be enabled. This is + typically obtained using the `ctx.features` field in a rule + implementation function. + unsupported_features: The list of features that are unsupported by the + current rule. This is typically obtained using the + `ctx.disabled_features` field in a rule implementation function. + """ + features_to_check = list(requested_features) + features_to_check.extend( + ["-{}".format(feature) for feature in unsupported_features], + ) + + for allowlist in allowlists: + for feature_string in features_to_check: + if not _is_feature_allowed_in_package( + allowlist = allowlist, + feature = feature_string, + package = label.package, + workspace_name = label.workspace_name, + ): + fail(( + "Feature '{feature}' is not allowed to be set by the " + + "target '{target}'; see the allowlist at '{allowlist}' " + + "for more information." + ).format( + allowlist = allowlist.allowlist_label, + feature = feature_string, + target = str(label), + )) + +def _is_feature_allowed_in_package( + allowlist, + feature, + package, + workspace_name = None): + """Returns a value indicating whether a feature is allowed in a package. + + Args: + allowlist: The `SwiftFeatureAllowlistInfo` provider that contains the + allowlist. + feature: The name of the feature (or its negation) being checked. + package: The package part of the label being checked for access (e.g., + the value of `ctx.label.package`). + workspace_name: The workspace name part of the label being checked for + access (e.g., the value of `ctx.label.workspace_name`). + + Returns: + True if the feature is allowed to be used in the package, or False if it + is not. + """ + + # Any feature not managed by the allowlist is allowed by default. + if feature not in allowlist.managed_features: + return True + + if workspace_name: + package_spec = "@{}//{}".format(workspace_name, package) + else: + package_spec = "//{}".format(package) + + is_allowed = False + for package_info in allowlist.packages: + if package_info.match_subpackages: + is_match = ( + package_spec == package_info.package or + package_spec.startswith(package_info.package + "/") + ) + else: + is_match = package_spec == package_info.package + + # Package exclusions always take precedence over package inclusions, so + # if we have an exclusion match, return false immediately. + if package_info.excluded and is_match: + return False + else: + is_allowed = True + + return is_allowed diff --git a/swift/internal/linking.bzl b/swift/internal/linking.bzl index 6d4336478..08699878a 100644 --- a/swift/internal/linking.bzl +++ b/swift/internal/linking.bzl @@ -15,7 +15,6 @@ """Implementation of linking logic for Swift.""" load("@bazel_skylib//lib:collections.bzl", "collections") -load("@bazel_skylib//lib:partial.bzl", "partial") load( "@bazel_tools//tools/build_defs/cc:action_names.bzl", "CPP_LINK_STATIC_LIBRARY_ACTION_NAME", @@ -58,7 +57,15 @@ def _register_static_library_link_action( ) args = actions.args() args.add_all(command_line) - args.add_all(objects) + + filelist_args = actions.args() + if swift_toolchain.linker_supports_filelist: + args.add("-filelist") + filelist_args.set_param_file_format("multiline") + filelist_args.use_param_file("%s", use_always = True) + filelist_args.add_all(objects) + else: + args.add_all(objects) env = cc_common.get_environment_variables( action_name = CPP_LINK_STATIC_LIBRARY_ACTION_NAME, @@ -73,7 +80,7 @@ def _register_static_library_link_action( execution_requirements = {req: "1" for req in execution_requirements_list} actions.run( - arguments = [args], + arguments = [args, filelist_args], env = env, executable = archiver_path, execution_requirements = execution_requirements, @@ -86,16 +93,21 @@ def _register_static_library_link_action( progress_message = "Linking {}".format(output.short_path), ) -def register_libraries_to_link( +def create_linker_input( + *, actions, alwayslink, cc_feature_configuration, + compilation_outputs, is_dynamic, is_static, library_name, objects, - swift_toolchain): - """Declares the requested libraries and registers actions to link them. + owner, + swift_toolchain, + additional_inputs = [], + user_link_flags = []): + """Creates a linker input for a library to link and additional inputs/flags. Args: actions: The object used to register actions. @@ -104,17 +116,26 @@ def register_libraries_to_link( argument is ignored if `is_static` is False. cc_feature_configuration: The C++ feature configuration to use when constructing the action. + compilation_outputs: The compilation outputs from a Swift compile + action, as returned by `swift_common.compile`, or None. is_dynamic: If True, declare and link a dynamic library. is_static: If True, declare and link a static library. library_name: The basename (without extension) of the libraries to declare. objects: A list of `File`s denoting object (`.o`) files that will be linked. + owner: The `Label` of the target that owns this linker input. swift_toolchain: The Swift toolchain provider to use when constructing the action. + additional_inputs: A list of extra `File` inputs passed to the linking + action. + user_link_flags: A list of extra flags to pass to the linking command. Returns: - A `LibraryToLink` object containing the libraries that were created. + A tuple containing two elements: + + 1. A `LinkerInput` object containing the library that was created. + 2. The single `LibraryToLink` object that is inside the linker input. """ dynamic_library = None if is_dynamic: @@ -137,7 +158,7 @@ def register_libraries_to_link( else: static_library = None - return cc_common.create_library_to_link( + library_to_link = cc_common.create_library_to_link( actions = actions, alwayslink = alwayslink, cc_toolchain = swift_toolchain.cc_toolchain_info, @@ -145,6 +166,18 @@ def register_libraries_to_link( pic_static_library = static_library, dynamic_library = dynamic_library, ) + linker_input = cc_common.create_linker_input( + owner = owner, + libraries = depset([library_to_link]), + additional_inputs = depset( + compilation_outputs.linker_inputs + additional_inputs, + ), + user_link_flags = depset( + compilation_outputs.linker_flags + user_link_flags, + ), + ) + + return linker_input, library_to_link def register_link_binary_action( actions, @@ -156,6 +189,7 @@ def register_link_binary_action( name, objects, output_type, + owner, stamp, swift_toolchain, user_link_flags): @@ -178,6 +212,7 @@ def register_link_binary_action( objects: A list of object (.o) files that will be passed to the linker. output_type: A string indicating the output type; "executable" or "dynamic_library". + owner: The `Label` of the target that owns this linker input. stamp: A tri-state value (-1, 0, or 1) that specifies whether link stamping is enabled. See `cc_common.link` for details about the behavior of this argument. @@ -223,7 +258,12 @@ def register_link_binary_action( linking_contexts.append( cc_common.create_linking_context( - user_link_flags = dep_link_flags, + linker_inputs = depset([ + cc_common.create_linker_input( + owner = owner, + user_link_flags = depset(dep_link_flags), + ), + ]), ), ) @@ -246,29 +286,3 @@ def register_link_binary_action( output_type = output_type, stamp = stamp, ) - -def swift_runtime_linkopts(is_static, toolchain, is_test = False): - """Returns the flags that should be passed when linking a Swift binary. - - This function provides the appropriate linker arguments to callers who need - to link a binary using something other than `swift_binary` (for example, an - application bundle containing a universal `apple_binary`). - - Args: - is_static: A `Boolean` value indicating whether the binary should be - linked against the static (rather than the dynamic) Swift runtime - libraries. - toolchain: The `SwiftToolchainInfo` provider of the toolchain whose - linker options are desired. - is_test: A `Boolean` value indicating whether the target being linked is - a test target. - - Returns: - A `list` of command line flags that should be passed when linking a - binary against the Swift runtime libraries. - """ - return partial.call( - toolchain.linker_opts_producer, - is_static = is_static, - is_test = is_test, - ) diff --git a/swift/internal/module_maps.bzl b/swift/internal/module_maps.bzl index 1fb4f6eee..67b77b1c5 100644 --- a/swift/internal/module_maps.bzl +++ b/swift/internal/module_maps.bzl @@ -14,13 +14,25 @@ """Logic for generating Clang module map files.""" -load("@bazel_skylib//lib:paths.bzl", "paths") +# TODO: Once bazel supports nested functions unify it with upstream +def _add_headers(*, headers, kind, content, relative_to_dir, back_to_root_path): + # Each header is added to the `Args` object as a tuple along with + # `relative_to_dir` and `back_to_root_path`. This gives the mapping + # function the information it needs to relativize the header paths even + # when they're expanded from a tree artifact (and thus not known at + # analysis time). + content.add_all( + [(file, relative_to_dir, back_to_root_path) for file in headers], + format_each = ' {} "%s"'.format(kind), + map_each = _header_info_mapper, + ) def write_module_map( actions, module_map_file, module_name, dependent_module_names = [], + exported_module_ids = [], public_headers = [], public_textual_headers = [], private_headers = [], @@ -34,6 +46,14 @@ def write_module_map( module_name: The name of the module being generated. dependent_module_names: A `list` of names of Clang modules that are direct dependencies of the target whose module map is being written. + exported_module_ids: A `list` of Clang wildcard module identifiers that + will be re-exported as part of the API of the module being written. + The values in this list should match `wildcard-module-id` as + described by + https://clang.llvm.org/docs/Modules.html#export-declaration. Common + values include the empty list to re-export nothing (except the + module's own API), or `["*"]` to re-export all modules that were + imported by the header files in the module. public_headers: The `list` of `File`s representing the public modular headers of the target whose module map is being written. public_textual_headers: The `list` of `File`s representing the public @@ -46,64 +66,84 @@ def write_module_map( written in the module map file should be relative to the workspace or relative to the module map file. """ - content = 'module "{}" {{\n'.format(module_name) - content += " export *\n\n" - - content += "".join([ - ' header "{}"\n'.format(_header_path( - header_file = header_file, - module_map_file = module_map_file, - workspace_relative = workspace_relative, - )) - for header_file in public_headers - ]) - content += "".join([ - ' private header "{}"\n'.format(_header_path( - header_file = header_file, - module_map_file = module_map_file, - workspace_relative = workspace_relative, - )) - for header_file in private_headers - ]) - content += "".join([ - ' textual header "{}"\n'.format(_header_path( - header_file = header_file, - module_map_file = module_map_file, - workspace_relative = workspace_relative, - )) - for header_file in public_textual_headers - ]) - content += "".join([ - ' private textual header "{}"\n'.format(_header_path( - header_file = header_file, - module_map_file = module_map_file, - workspace_relative = workspace_relative, - )) - for header_file in private_textual_headers - ]) - - content += "".join([ - ' use "{}"\n'.format(name) - for name in dependent_module_names - ]) - - content += "}\n" + + # In the non-workspace-relative case, precompute the relative-to-dir and the + # repeated `../` string used to go back up to the workspace root instead of + # recomputing it every time a header path is written. + if workspace_relative: + relative_to_dir = None + back_to_root_path = None + else: + relative_to_dir = module_map_file.dirname + back_to_root_path = "../" * len(relative_to_dir.split("/")) + + content = actions.args() + content.set_param_file_format("multiline") + + content.add(module_name, format = 'module "%s" {') + + # Write an `export` declaration for each of the module identifiers that + # should be re-exported by this module. + content.add_all(exported_module_ids, format_each = " export %s") + content.add("") + + _add_headers(headers = public_headers, kind = "header", content = content, relative_to_dir = relative_to_dir, back_to_root_path = back_to_root_path) + _add_headers(headers = private_headers, kind = "private header", content = content, relative_to_dir = relative_to_dir, back_to_root_path = back_to_root_path) + _add_headers(headers = public_textual_headers, kind = "textual header", content = content, relative_to_dir = relative_to_dir, back_to_root_path = back_to_root_path) + _add_headers( + headers = private_textual_headers, + kind = "private textual header", + content = content, + relative_to_dir = relative_to_dir, + back_to_root_path = back_to_root_path, + ) + content.add("") + + # Write a `use` declaration for each of the module's dependencies. + content.add_all(dependent_module_names, format_each = ' use "%s"') + content.add("}") actions.write( content = content, output = module_map_file, ) -def _header_path(header_file, module_map_file, workspace_relative): +def _header_info_mapper(header_info, directory_expander): + """Maps header info passed to the `Args` object to a list of header paths. + + Args: + header_info: A tuple containing three elements: a `File` representing a + header (or a directory containing headers), the path to the + directory that should be used to relativize the header paths, and + the path string consisting of repeated `../` segments that should be + used to return from the module map's directory to the workspace + root. The latter two elements will be `None` if the headers should + be written workspace-relative). + directory_expander: The object used to expand tree artifacts into the + list of files in that directory. + + Returns: + A list of file paths as they should be written into the module map file. + """ + return [ + _header_path(file, header_info[1], header_info[2]) + for file in directory_expander.expand(header_info[0]) + ] + +def _header_path(header_file, relative_to_dir, back_to_root_path): """Returns the path to a header file to be written in the module map. Args: header_file: A `File` representing the header whose path should be returned. - module_map_file: A `File` representing the module map being written, - which is used during path relativization if necessary. - workspace_relative: A Boolean value indicating whether the path should - be workspace-relative or module-map-relative. + relative_to_dir: A `File` representing the module map being + written, which is used during path relativization if necessary. If + this is `None`, then no relativization is performed of the header + path and the workspace-relative path is used instead. + back_to_root_path: A path string consisting of repeated `../` segments + that should be used to return from the module map's directory to the + workspace root. This should be `None` if `relative_to_dir` is + `None`. Returns: The path to the header file, relative to either the workspace or the @@ -112,13 +152,21 @@ def _header_path(header_file, module_map_file, workspace_relative): # If the module map is workspace-relative, then the file's path is what we # want. - if workspace_relative: + if not relative_to_dir: return header_file.path + # Minor optimization for the generated Objective-C header of a Swift module, + # which will be in the same directory as the module map file -- we can just + # use the header's basename instead of the elaborate relative path string + # below. + if header_file.dirname == relative_to_dir: + return header_file.basename + # Otherwise, since the module map is generated, we need to get the full path # to it rather than just its short path (that is, the path starting with # bazel-out/). Then, we can simply walk up the same number of parent # directories as there are path segments, and append the header file's path - # to that. - num_segments = len(paths.dirname(module_map_file.path).split("/")) - return ("../" * num_segments) + header_file.path + # to that. The `back_to_root_path` string is guaranteed to end in a slash, + # so we use simple concatenation instead of Skylib's `paths.join` to avoid + # extra work. + return back_to_root_path + header_file.path diff --git a/swift/internal/proto_gen_utils.bzl b/swift/internal/proto_gen_utils.bzl index 6f51560e1..017397b33 100644 --- a/swift/internal/proto_gen_utils.bzl +++ b/swift/internal/proto_gen_utils.bzl @@ -15,7 +15,6 @@ """Utilities for rules/aspects that generate sources from .proto files.""" load("@bazel_skylib//lib:paths.bzl", "paths") -load(":utils.bzl", "proto_import_path") def declare_generated_files( name, @@ -113,6 +112,33 @@ def register_module_mapping_write_action(name, actions, module_mappings): return mapping_file +def proto_import_path(f, proto_source_root): + """ Returns the import path of a `.proto` file given its path. + + Args: + f: The `File` object representing the `.proto` file. + proto_source_root: The source root for the `.proto` file. + + Returns: + The path the `.proto` file should be imported at. + """ + + if proto_source_root: + # Don't want to accidentally match "foo" to "foobar", so add the slash. + if not proto_source_root.endswith("/"): + proto_source_root += "/" + if f.path.startswith(proto_source_root): + return f.path[len(proto_source_root):] + + # Cross-repository proto file references is sorta a grey area. If that is + # needed, please see the comments in ProtoCompileActionBuilder.java's + # guessProtoPathUnderRoot() for some guidance of what would be needed, but + # the current (Q3/2020) reading says that seems to not maintain the + # references, so the proto file namespace is likely flat across + # repositories. + workspace_path = paths.join(f.root.path, f.owner.workspace_root) + return paths.relativize(f.path, workspace_path) + def _generated_file_path( name, extension_fragment, diff --git a/swift/internal/providers.bzl b/swift/internal/providers.bzl index fcfcaac7a..c2a576e8c 100644 --- a/swift/internal/providers.bzl +++ b/swift/internal/providers.bzl @@ -14,7 +14,46 @@ """Defines Starlark providers that propagated by the Swift BUILD rules.""" -load("@bazel_skylib//lib:sets.bzl", "sets") +SwiftAllowlistPackageInfo = provider( + doc = "Describes a package match in an allowlist.", + fields = { + "excluded": """\ +A Boolean value indicating whether the packages described by this value are +exclusions rather than inclusions. +""", + "match_subpackages": """\ +A Boolean value indicating whether subpackages of `package` should also be +matched. +""", + "package": """\ +A string indicating the name of the package to match, in the form +`//path/to/package`, or `@repository//path/to/package` if an explicit repository +name was given. +""", + }, +) + +SwiftFeatureAllowlistInfo = provider( + doc = """\ +Describes a set of features and the packages that are allowed to request or +disable them. +""", + fields = { + "allowlist_label": """\ +A string containing the label of the `swift_feature_allowlist` target that +created this provider. +""", + "managed_features": """\ +A list of strings representing feature names or their negations that packages in +the `packages` list are allowed to explicitly request or disable. +""", + "packages": """\ +A list of `SwiftAllowlistPackageInfo` values describing packages (possibly +recursive) whose targets are allowed to request or disable a feature managed by +this allowlist. +""", + }, +) SwiftInfo = provider( doc = """\ @@ -26,66 +65,14 @@ directly, consider using the `swift_common.create_swift_info` function, which has reasonable defaults for any fields not explicitly set. """, fields = { - "direct_defines": """\ -`List` of `string`s. The values specified by the `defines` attribute of the -library that directly propagated this provider. -""", "direct_modules": """\ `List` of values returned from `swift_common.create_module`. The modules (both Swift and C/Objective-C) emitted by the library that propagated this provider. -""", - "direct_swiftdocs": """\ -`List` of `File`s. The Swift documentation (`.swiftdoc`) files for the library -that directly propagated this provider. -""", - "direct_swiftmodules": """\ -`List` of `File`s. The Swift modules (`.swiftmodule`) for the library that -directly propagated this provider. -""", - "module_name": """\ -`String`. The name of the Swift module represented by the target that directly -propagated this provider. - -This field will be equal to the explicitly assigned module name (if present); -otherwise, it will be equal to the autogenerated module name. -""", - "swift_version": """\ -`String`. The version of the Swift language that was used when compiling the -propagating target; that is, the value passed via the `-swift-version` compiler -flag. This will be `None` if the flag was not set. -""", - "transitive_defines": """\ -`Depset` of `string`s. The transitive `defines` specified for the library that -propagated this provider and all of its dependencies. -""", - "transitive_generated_headers": """\ -`Depset` of `File`s. The transitive generated header files that can be used by -Objective-C sources to interop with the transitive Swift libraries. -""", - "transitive_modulemaps": """\ -`Depset` of `File`s. The transitive module map files that will be passed to -Clang using the `-fmodule-map-file` option. - -This field is deprecated; use `transitive_modules` instead. """, "transitive_modules": """\ `Depset` of values returned from `swift_common.create_module`. The transitive modules (both Swift and C/Objective-C) emitted by the library that propagated this provider and all of its dependencies. -""", - "transitive_swiftdocs": """\ -`Depset` of `File`s. The transitive Swift documentation (`.swiftdoc`) files -emitted by the library that propagated this provider and all of its -dependencies. -""", - "transitive_swiftinterfaces": """\ -`Depset` of `File`s. The transitive Swift interface (`.swiftinterface`) files -emitted by the library that propagated this provider and all of its -dependencies. -""", - "transitive_swiftmodules": """\ -`Depset` of `File`s. The transitive Swift modules (`.swiftmodule`) emitted by -the library that propagated this provider and all of its dependencies. """, }, ) @@ -124,36 +111,68 @@ using this toolchain. The `cc_common.CcToolchainInfo` provider from the Bazel C++ toolchain that this Swift toolchain depends on. """, - "command_line_copts": """\ -`List` of `strings`. Flags that were passed to Bazel using the `--swiftcopt` -command line flag. These flags have the highest precedence; they are added to -compilation command lines after the toolchain default flags -(`SwiftToolchainInfo.swiftc_copts`) and after flags specified in the `copts` -attributes of Swift targets. + "clang_implicit_deps_providers": """\ +A `struct` with the following fields, which represent providers from targets +that should be added as implicit dependencies of any precompiled explicit +C/Objective-C modules: + +* `cc_infos`: A list of `CcInfo` providers from targets specified as the + toolchain's implicit dependencies. +* `objc_infos`: A list of `apple_common.Objc` providers from targets specified + as the toolchain's implicit dependencies. +* `swift_infos`: A list of `SwiftInfo` providers from targets specified as the + toolchain's implicit dependencies. + +For ease of use, this field is never `None`; it will always be a valid `struct` +containing the fields described above, even if those lists are empty. """, "cpu": """\ `String`. The CPU architecture that the toolchain is targeting. """, - "linker_opts_producer": """\ -Skylib `partial`. A partial function that returns the flags that should be -passed to Clang to link a binary or test target with the Swift runtime -libraries. + "feature_allowlists": """\ +A list of `SwiftFeatureAllowlistInfo` providers that allow or prohibit packages +from requesting or disabling features. +""", + "generated_header_module_implicit_deps_providers": """\ +A `struct` with the following fields, which are providers from targets that +should be treated as compile-time inputs to actions that precompile the explicit +module for the generated Objective-C header of a Swift module: + +* `cc_infos`: A list of `CcInfo` providers from targets specified as the + toolchain's implicit dependencies. +* `objc_infos`: A list of `apple_common.Objc` providers from targets specified + as the toolchain's implicit dependencies. +* `swift_infos`: A list of `SwiftInfo` providers from targets specified as the + toolchain's implicit dependencies. + +This is used to provide modular dependencies for the fixed inclusions (Darwin, +Foundation) that are unconditionally emitted in those files. + +For ease of use, this field is never `None`; it will always be a valid `struct` +containing the fields described above, even if those lists are empty. +""", + "implicit_deps_providers": """\ +A `struct` with the following fields, which represent providers from targets +that should be added as implicit dependencies of any Swift compilation or +linking target (but not to precompiled explicit C/Objective-C modules): -The partial should be called with two arguments: +* `cc_infos`: A list of `CcInfo` providers from targets specified as the + toolchain's implicit dependencies. +* `objc_infos`: A list of `apple_common.Objc` providers from targets specified + as the toolchain's implicit dependencies. +* `swift_infos`: A list of `SwiftInfo` providers from targets specified as the + toolchain's implicit dependencies. -* `is_static`: A `Boolean` value indicating whether to link against the static - or dynamic runtime libraries. -* `is_test`: A `Boolean` value indicating whether the target being linked is a - test target. +For ease of use, this field is never `None`; it will always be a valid `struct` +containing the fields described above, even if those lists are empty. +""", + "linker_supports_filelist": """\ +`Boolean`. Indicates whether or not the toolchain's linker supports the input +files passed to it via a file list. """, "object_format": """\ `String`. The object file format of the platform that the toolchain is targeting. The currently supported values are `"elf"` and `"macho"`. -""", - "optional_implicit_deps": """\ -`List` of `Target`s. Library targets that should be added as implicit -dependencies of any `swift_library`, `swift_binary`, or `swift_test` target that -does not have the feature `swift.minimal_deps` applied. """, "requested_features": """\ `List` of `string`s. Features that should be implicitly enabled by default for @@ -164,11 +183,6 @@ their negation in the `features` attribute of a target/package or in the These features determine various compilation and debugging behaviors of the Swift build rules, and they are also passed to the C++ APIs used when linking (so features defined in CROSSTOOL may be used here). -""", - "required_implicit_deps": """\ -`List` of `Target`s. Library targets that should be unconditionally added as -implicit dependencies of any `swift_library`, `swift_binary`, or `swift_test` -target. """, "root_dir": """\ `String`. The workspace-relative root directory of the toolchain. @@ -189,6 +203,7 @@ compiles). * `env`: A `dict` of environment variables to be set when running tests that were built with this toolchain. + * `execution_requirements`: A `dict` of execution requirements for tests that were built with this toolchain. @@ -225,7 +240,7 @@ provider. }, ) -def create_module(name, *, clang = None, swift = None): +def create_module(*, name, clang = None, is_system = False, swift = None): """Creates a value containing Clang/Swift module artifacts of a dependency. At least one of the `clang` and `swift` arguments must not be `None`. It is @@ -239,6 +254,20 @@ def create_module(name, *, clang = None, swift = None): contains artifacts related to Clang modules, such as a module map or precompiled module. This may be `None` if the module is a pure Swift module with no generated Objective-C interface. + is_system: Indicates whether the module is a system module. The default + value is `False`. System modules differ slightly from non-system + modules in the way that they are passed to the compiler. For + example, non-system modules have their Clang module maps passed to + the compiler in both implicit and explicit module builds. System + modules, on the other hand, do not have their module maps passed to + the compiler in implicit module builds because there is currently no + way to indicate that modules declared in a file passed via + `-fmodule-map-file` should be treated as system modules even if they + aren't declared with the `[system]` attribute, and some system + modules may not build cleanly with respect to warnings otherwise. + Therefore, it is assumed that any module with `is_system == True` + must be able to be found using import search paths in order for + implicit module builds to succeed. swift: A value returned by `swift_common.create_swift_module` that contains artifacts related to Swift modules, such as the `.swiftmodule`, `.swiftdoc`, and/or `.swiftinterface` files emitted @@ -246,14 +275,14 @@ def create_module(name, *, clang = None, swift = None): C/Objective-C module. Returns: - A `struct` containing the `name`, `clang`, and `swift` fields provided - as arguments. + A `struct` containing the `name`, `clang`, `is_system`, and `swift` + fields provided as arguments. """ - - # TODO(b/149999519): Once Swift module artifacts have migrated to this API, - # fail if both `clang` and `swift` are `None`. + if clang == None and swift == None: + fail("Must provide at least a clang or swift module.") return struct( clang = clang, + is_system = is_system, name = name, swift = swift, ) @@ -270,8 +299,11 @@ def create_clang_module( files, include paths, and other context necessary to compile targets that depend on this module (if using the text module map instead of the precompiled module). - module_map: A `File` representing the text module map file that defines - this module. + module_map: The text module map file that defines this module. This + argument may be specified as a `File` or as a `string`; in the + latter case, it is assumed to be the path to a file that cannot + be provided as an action input because it is outside the workspace + (for example, the module map for a module from an Xcode SDK). precompiled_module: A `File` representing the precompiled module (`.pcm` file) if one was emitted for the module. This may be `None` if no explicit module was built for the module; in that case, targets that @@ -318,10 +350,9 @@ def create_swift_module( def create_swift_info( *, - module_name = None, + direct_swift_infos = [], modules = [], - swift_infos = [], - swift_version = None): + swift_infos = []): """Creates a new `SwiftInfo` provider with the given values. This function is recommended instead of directly creating a `SwiftInfo` @@ -330,117 +361,38 @@ def create_swift_info( are set consistently. This function can also be used to do a simple merge of `SwiftInfo` - providers, by leaving all of the arguments except for `swift_infos` as their - empty defaults. In that case, the returned provider will not represent a - true Swift module; it is merely a "collector" for other dependencies. + providers, by leaving the `modules` argument unspecified. In that case, the + returned provider will not represent a true Swift module; it is merely a + "collector" for other dependencies. Args: - module_name: This argument is deprecated. The module name(s) should be - specified in the values passed to the `modules` argument. + direct_swift_infos: A list of `SwiftInfo` providers from dependencies + whose direct modules should be treated as direct modules in the + resulting provider, in addition to their transitive modules being + merged. modules: A list of values (as returned by `swift_common.create_module`) that represent Clang and/or Swift module artifacts that are direct outputs of the target being built. - swift_infos: A list of `SwiftInfo` providers from dependencies, whose - transitive fields should be merged into the new one. If omitted, no - transitive data is collected. - swift_version: A string containing the value of the `-swift-version` - flag used when compiling this target, or `None` (the default) if it - was not set or is not relevant. + swift_infos: A list of `SwiftInfo` providers from dependencies whose + transitive modules should be merged into the resulting provider. Returns: A new `SwiftInfo` provider with the given values. """ - # TODO(b/149999519): Remove the legacy direct/transitive fields and this - # transitional code. - defines_set = sets.make() - generated_headers = [] - swiftdocs = [] - swiftinterfaces = [] - swiftmodules = [] - for module in modules: - swift_module = module.swift - if not swift_module: - continue - - if swift_module.defines: - defines_set = sets.union( - defines_set, - sets.make(swift_module.defines), - ) - if swift_module.swiftdoc: - swiftdocs.append(swift_module.swiftdoc) - if swift_module.swiftinterface: - swiftinterfaces.append(swift_module.swiftinterface) - if swift_module.swiftmodule: - swiftmodules.append(swift_module.swiftmodule) - - # If this is both a Swift and a Clang module, then the header in its - # compilation context is its Swift generated header. - clang_module = module.clang - if clang_module: - generated_headers.extend( - clang_module.compilation_context.headers.to_list(), - ) - - defines = sets.to_list(defines_set) - - # TODO(b/149999519): Remove the legacy `module_name` field and this - # transitional code. - if not module_name and len(modules) == 1: - # Populate the module name based on the single module provided, if there - # was one, and if the legacy `module_name` parameter wasn't already - # given. - module_name = modules[0].name - - transitive_defines = [] - transitive_generated_headers = [] - transitive_modulemaps = [] - transitive_modules = [] - transitive_swiftdocs = [] - transitive_swiftinterfaces = [] - transitive_swiftmodules = [] - for swift_info in swift_infos: - transitive_defines.append(swift_info.transitive_defines) - transitive_generated_headers.append( - swift_info.transitive_generated_headers, - ) - transitive_modulemaps.append(swift_info.transitive_modulemaps) - transitive_modules.append(swift_info.transitive_modules) - transitive_swiftdocs.append(swift_info.transitive_swiftdocs) - transitive_swiftinterfaces.append(swift_info.transitive_swiftinterfaces) - transitive_swiftmodules.append(swift_info.transitive_swiftmodules) - - # TODO(b/149999519): Remove `transitive_modulemaps`, - # `(direct|transitive)_swift*` fields, `module_name`, and `swift_version` - # after Tulsi is migrated. + direct_modules = modules + [ + provider.modules + for provider in direct_swift_infos + ] + transitive_modules = [ + provider.transitive_modules + for provider in direct_swift_infos + swift_infos + ] + return SwiftInfo( - direct_defines = defines, - direct_modules = modules, - direct_swiftdocs = swiftdocs, - direct_swiftmodules = swiftmodules, - module_name = module_name, - swift_version = swift_version, - transitive_defines = depset(defines, transitive = transitive_defines), - transitive_generated_headers = depset( - generated_headers, - transitive = transitive_generated_headers, - ), - transitive_modulemaps = depset( - [m.clang.module_map for m in modules if m.clang], - transitive = transitive_modulemaps, - ), - transitive_modules = depset(modules, transitive = transitive_modules), - transitive_swiftdocs = depset( - swiftdocs, - transitive = transitive_swiftdocs, - ), - transitive_swiftinterfaces = depset( - swiftinterfaces, - transitive = transitive_swiftinterfaces, - ), - transitive_swiftmodules = depset( - swiftmodules, - transitive = transitive_swiftmodules, + direct_modules = direct_modules, + transitive_modules = depset( + direct_modules, + transitive = transitive_modules, ), ) diff --git a/swift/internal/swift_autoconfiguration.bzl b/swift/internal/swift_autoconfiguration.bzl index 7ba611926..d386773a4 100644 --- a/swift/internal/swift_autoconfiguration.bzl +++ b/swift/internal/swift_autoconfiguration.bzl @@ -28,7 +28,7 @@ load( "@build_bazel_rules_swift//swift/internal:feature_names.bzl", "SWIFT_FEATURE_DEBUG_PREFIX_MAP", "SWIFT_FEATURE_ENABLE_BATCH_MODE", - "SWIFT_FEATURE_IMPLICIT_MODULES", + "SWIFT_FEATURE_ENABLE_SKIP_FUNCTION_BODIES", "SWIFT_FEATURE_MODULE_MAP_NO_PRIVATE_HEADERS", "SWIFT_FEATURE_SUPPORTS_PRIVATE_DEPS", "SWIFT_FEATURE_USE_RESPONSE_FILES", @@ -75,6 +75,15 @@ def _check_enable_batch_mode(repository_ctx, swiftc_path, temp_dir): "-enable-batch-mode", ) +def _check_skip_function_bodies(repository_ctx, swiftc_path, temp_dir): + """Returns True if `swiftc` supports skip function bodies.""" + return _swift_succeeds( + repository_ctx, + swiftc_path, + "-version", + "-experimental-skip-non-inlinable-function-bodies", + ) + def _check_debug_prefix_map(repository_ctx, swiftc_path, temp_dir): """Returns True if `swiftc` supports debug prefix mapping.""" return _swift_succeeds( @@ -187,6 +196,7 @@ def _compute_feature_values(repository_ctx, swiftc_path): _FEATURE_CHECKS = { SWIFT_FEATURE_DEBUG_PREFIX_MAP: _check_debug_prefix_map, SWIFT_FEATURE_ENABLE_BATCH_MODE: _check_enable_batch_mode, + SWIFT_FEATURE_ENABLE_SKIP_FUNCTION_BODIES: _check_skip_function_bodies, SWIFT_FEATURE_SUPPORTS_PRIVATE_DEPS: _check_supports_private_deps, SWIFT_FEATURE_USE_RESPONSE_FILES: _check_use_response_files, } @@ -203,16 +213,13 @@ def _create_linux_toolchain(repository_ctx): "in your environment before invoking Bazel.") path_to_swiftc = repository_ctx.which("swiftc") + if not path_to_swiftc: + fail("No 'swiftc' executable found in $PATH") + root = path_to_swiftc.dirname.dirname feature_values = _compute_feature_values(repository_ctx, path_to_swiftc) version_file = _write_swift_version(repository_ctx, path_to_swiftc) - # TODO: This is being enabled here, rather than in the toolchain rule - # implementations, so that we can provide a way to optionally turn it off - # later when we have a way to model modules from outside Bazel workspaces - # (i.e., from the Swift toolchain) as explicit modules. - feature_values.append(SWIFT_FEATURE_IMPLICIT_MODULES) - # 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. @@ -252,15 +259,7 @@ def _create_xcode_toolchain(repository_ctx): Args: repository_ctx: The repository rule context. """ - path_to_swiftc = repository_ctx.which("swiftc") - feature_values = [ - # TODO: This is being enabled here, rather than in the toolchain rule - # implementations, so that we can provide a way to optionally turn it - # off later when we have a way to model modules from outside Bazel - # workspaces (i.e., from the Swift toolchain and Xcode SDKs) as explicit - # modules. - SWIFT_FEATURE_IMPLICIT_MODULES, # 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. diff --git a/swift/internal/swift_binary_test.bzl b/swift/internal/swift_binary_test.bzl index fa0a56b4e..868f9ccad 100644 --- a/swift/internal/swift_binary_test.bzl +++ b/swift/internal/swift_binary_test.bzl @@ -15,12 +15,12 @@ """Implementation of the `swift_binary` and `swift_test` rules.""" load("@bazel_skylib//lib:dicts.bzl", "dicts") -load("@bazel_skylib//lib:partial.bzl", "partial") load(":compiling.bzl", "output_groups_from_compilation_outputs") load(":derived_files.bzl", "derived_files") load(":feature_names.bzl", "SWIFT_FEATURE_BUNDLED_XCTESTS") load(":linking.bzl", "register_link_binary_action") load(":providers.bzl", "SwiftToolchainInfo") +load(":swift_clang_module_aspect.bzl", "swift_clang_module_aspect") load(":swift_common.bzl", "swift_common") load(":utils.bzl", "expand_locations") @@ -35,7 +35,8 @@ def _binary_rule_attrs(stamp_default): """ return dicts.add( swift_common.compilation_attrs( - additional_deps_aspects = [swift_common.swift_clang_module_aspect], + additional_deps_aspects = [swift_clang_module_aspect], + requires_srcs = False, ), { "linkopts": attr.string_list( @@ -66,8 +67,10 @@ into the binary. Possible values are: * `stamp = 1`: Stamp the build information into the binary. Stamped binaries are only rebuilt when their dependencies change. Use this if there are tests that depend on the build information. + * `stamp = 0`: Always replace build information by constant values. This gives good build result caching. + * `stamp = -1`: Embedding of build information is controlled by the `--[no]stamp` flag. """, @@ -78,6 +81,15 @@ into the binary. Possible values are: "_cc_toolchain": attr.label( default = Label("@bazel_tools//tools/cpp:current_cc_toolchain"), ), + # A late-bound attribute denoting the value of the `--custom_malloc` + # command line flag (or None if the flag is not provided). + "_custom_malloc": attr.label( + default = configuration_field( + fragment = "cpp", + name = "custom_malloc", + ), + providers = [[CcInfo]], + ), # TODO(b/119082664): Used internally only. "_grep_includes": attr.label( allow_single_file = True, @@ -186,6 +198,7 @@ def _swift_linking_rule_impl( srcs = srcs, swift_toolchain = swift_toolchain, target_name = ctx.label.name, + workspace_name = ctx.workspace_name, ) user_link_flags.extend(compilation_outputs.linker_flags) objects_to_link.extend(compilation_outputs.object_files) @@ -195,30 +208,15 @@ def _swift_linking_rule_impl( compilation_outputs = compilation_outputs, ) - # Retrieve any additional linker flags required by the Swift toolchain. - # TODO(b/70228246): Also support mostly-static and fully-dynamic modes, - # here and for the C++ toolchain args below. - toolchain_linker_flags = partial.call( - swift_toolchain.linker_opts_producer, - is_static = True, - is_test = is_test, - ) - user_link_flags.extend(toolchain_linker_flags) - - # Collect the dependencies of the target being linked as well as the - # toolchain's implicit dependencies (depending on the current feature - # configuration). - deps_to_link = ctx.attr.deps + swift_common.get_implicit_deps( - feature_configuration = feature_configuration, - swift_toolchain = swift_toolchain, - ) + # Collect linking contexts from any of the toolchain's implicit + # dependencies. + for cc_info in swift_toolchain.implicit_deps_providers.cc_infos: + additional_linking_contexts.append(cc_info.linking_context) # If a custom malloc implementation has been provided, pass that to the # linker as well. - if ctx.attr.malloc: - additional_linking_contexts.append( - ctx.attr.malloc[CcInfo].linking_context, - ) + malloc = ctx.attr._custom_malloc or ctx.attr.malloc + additional_linking_contexts.append(malloc[CcInfo].linking_context) # Finally, consider linker flags in the `linkopts` attribute and the # `--linkopt` command line flag last, so they get highest priority. @@ -234,11 +232,12 @@ def _swift_linking_rule_impl( additional_inputs = additional_inputs_to_linker, additional_linking_contexts = additional_linking_contexts, cc_feature_configuration = cc_feature_configuration, - deps = deps_to_link, + deps = ctx.attr.deps, grep_includes = ctx.file._grep_includes, name = ctx.label.name, objects = objects_to_link, output_type = "executable", + owner = ctx.label, stamp = ctx.attr.stamp, swift_toolchain = swift_toolchain, user_link_flags = user_link_flags, diff --git a/swift/internal/swift_clang_module_aspect.bzl b/swift/internal/swift_clang_module_aspect.bzl index 8ae9db53c..e2e0efa06 100644 --- a/swift/internal/swift_clang_module_aspect.bzl +++ b/swift/internal/swift_clang_module_aspect.bzl @@ -19,6 +19,7 @@ load(":compiling.bzl", "derive_module_name", "precompile_clang_module") load(":derived_files.bzl", "derived_files") load( ":feature_names.bzl", + "SWIFT_FEATURE_EMIT_C_MODULE", "SWIFT_FEATURE_MODULE_MAP_HOME_IS_CWD", "SWIFT_FEATURE_MODULE_MAP_NO_PRIVATE_HEADERS", ) @@ -32,7 +33,11 @@ load( "create_module", "create_swift_info", ) -load(":utils.bzl", "get_providers") +load( + ":utils.bzl", + "compilation_context_for_explicit_module_compilation", + "get_providers", +) _MULTIPLE_TARGET_ASPECT_ATTRS = [ "deps", @@ -46,6 +51,126 @@ _SINGLE_TARGET_ASPECT_ATTRS = [ "_jre_lib", ] +_SwiftInteropInfo = provider( + doc = """\ +Contains minimal information required to allow `swift_clang_module_aspect` to +manage the creation of a `SwiftInfo` provider for a C/Objective-C target. +""", + fields = { + "module_map": """\ +A `File` representing an existing module map that should be used to represent +the module, or `None` if the module map should be generated based on the headers +in the target's compilation context. +""", + "module_name": """\ +A string denoting the name of the module, or `None` if the name should be +derived automatically from the target label. +""", + "requested_features": """\ +A list of features that should be enabled for the target, in addition to those +supplied in the `features` attribute, unless the feature is otherwise marked as +unsupported (either on the target or by the toolchain). This allows the rule +implementation to supply an additional set of fixed features that should always +be enabled when the aspect processes that target; for example, a rule can +request that `swift.emit_c_module` always be enabled for its targets even if it +is not explicitly enabled in the toolchain or on the target directly. +""", + "swift_infos": """\ +A list of `SwiftInfo` providers from dependencies of the target, which will be +merged with the new `SwiftInfo` created by the aspect. +""", + "unsupported_features": """\ +A list of features that should be disabled for the target, in addition to those +supplied as negations in the `features` attribute. This allows the rule +implementation to supply an additional set of fixed features that should always +be disabled when the aspect processes that target; for example, a rule that +processes frameworks with headers that do not follow strict layering can request +that `swift.strict_module` always be disabled for its targets even if it is +enabled by default in the toolchain. +""", + }, +) + +def create_swift_interop_info( + *, + module_map = None, + module_name = None, + requested_features = [], + swift_infos = [], + unsupported_features = []): + """Returns a provider that lets a target expose C/Objective-C APIs to Swift. + + The provider returned by this function allows custom build rules written in + Starlark to be uninvolved with much of the low-level machinery involved in + making a Swift-compatible module. Such a target should propagate a `CcInfo` + provider whose compilation context contains the headers that it wants to + make into a module, and then also propagate the provider returned from this + function. + + The simplest usage is for a custom rule to call + `swift_common.create_swift_interop_info` passing it only the list of + `SwiftInfo` providers from its dependencies; this tells + `swift_clang_module_aspect` to derive the module name from the target label + and create a module map using the headers from the compilation context. + + If the custom rule has reason to provide its own module name or module map, + then it can do so using the `module_name` and `module_map` arguments. + + When a rule returns this provider, it must provide the full set of + `SwiftInfo` providers from dependencies that will be merged with the one + that `swift_clang_module_aspect` creates for the target itself; the aspect + will not do so automatically. This allows the rule to not only add extra + dependencies (such as support libraries from implicit attributes) but also + exclude dependencies if necessary. + + Args: + module_map: A `File` representing an existing module map that should be + used to represent the module, or `None` (the default) if the module + map should be generated based on the headers in the target's + compilation context. If this argument is provided, then + `module_name` must also be provided. + module_name: A string denoting the name of the module, or `None` (the + default) if the name should be derived automatically from the target + label. + requested_features: A list of features (empty by default) that should be + requested for the target, which are added to those supplied in the + `features` attribute of the target. These features will be enabled + unless they are otherwise marked as unsupported (either on the + target or by the toolchain). This allows the rule implementation to + have additional control over features that should be supported by + default for all instances of that rule as if it were creating the + feature configuration itself; for example, a rule can request that + `swift.emit_c_module` always be enabled for its targets even if it + is not explicitly enabled in the toolchain or on the target + directly. + swift_infos: A list of `SwiftInfo` providers from dependencies, which + will be merged with the new `SwiftInfo` created by the aspect. + unsupported_features: A list of features (empty by default) that should + be considered unsupported for the target, which are added to those + supplied as negations in the `features` attribute. This allows the + rule implementation to have additional control over features that + should be disabled by default for all instances of that rule as if + it were creating the feature configuration itself; for example, a + rule that processes frameworks with headers that do not follow + strict layering can request that `swift.strict_module` always be + disabled for its targets even if it is enabled by default in the + toolchain. + + Returns: + A provider whose type/layout is an implementation detail and should not + be relied upon. + """ + if module_map and not module_name: + fail("'module_name' must be specified when 'module_map' is specified.") + + return _SwiftInteropInfo( + module_map = module_map, + module_name = module_name, + requested_features = requested_features, + swift_infos = swift_infos, + unsupported_features = unsupported_features, + ) + def _tagged_target_module_name(label, tags): """Returns the module name of a `swift_module`-tagged target. @@ -84,6 +209,13 @@ def _tagged_target_module_name(label, tags): _, _, module_name = tag.partition("=") return module_name +# TODO: Once bazel supports nested functions unify this with upstream +# Sort dependent module names and the headers to ensure a deterministic +# order in the output file, in the event the compilation context would ever +# change this on us. For files, use the execution path as the sorting key. +def _path_sorting_key(file): + return file.path + def _generate_module_map( actions, compilation_context, @@ -127,32 +259,163 @@ def _generate_module_map( actions = actions, target_name = target.label.name, ) + write_module_map( actions = actions, - dependent_module_names = dependent_module_names, + dependent_module_names = sorted(dependent_module_names), + exported_module_ids = ["*"], module_map_file = module_map_file, module_name = module_name, - private_headers = private_headers, - public_headers = compilation_context.direct_public_headers, - public_textual_headers = compilation_context.direct_textual_headers, + private_headers = sorted(private_headers, key = _path_sorting_key), + public_headers = sorted( + compilation_context.direct_public_headers, + key = _path_sorting_key, + ), + public_textual_headers = sorted( + compilation_context.direct_textual_headers, + key = _path_sorting_key, + ), workspace_relative = workspace_relative, ) return module_map_file -def _handle_cc_target( +def _module_info_for_target( + target, aspect_ctx, + compilation_context, + dependent_module_names, feature_configuration, + module_name): + """Returns the module name and module map for the target. + + Args: + target: The target for which the module map is being generated. + aspect_ctx: The aspect context. + compilation_context: The C++ compilation context that provides the + headers for the module. + dependent_module_names: A `list` of names of Clang modules that are + direct dependencies of the target whose module map is being written. + feature_configuration: A Swift feature configuration. + module_name: The module name to prefer (if we're generating a module map + from `_SwiftInteropInfo`), or None to derive it from other + properties of the target. + + Returns: + A tuple containing the module name (a string) and module map file (a + `File`) for the target. One or both of these values may be `None`. + """ + attr = aspect_ctx.rule.attr + + if not module_name and apple_common.Objc in target: + # TODO(b/142867898): For `objc_library`, stop using the module map from + # the Objc provider and generate our own. (For imported frameworks, + # continue using the module map included with it.) + objc = target[apple_common.Objc] + module_maps = objc.direct_module_maps + if not module_maps: + return None, None + + # If the target isn't an `objc_library`, return its module map (so we + # can propagate it) but don't return a module name; this will be used + # later as an indicator that we shouldn't try to compile an explicit + # module. + if not aspect_ctx.rule.kind == "objc_library": + return None, module_maps[0] + + # If an `objc_library` doesn't have any headers (and doesn't specify an + # explicit module map), then don't generate or propagate a module map + # for it. Such modules define nothing and only waste space on the + # compilation command line and add more work for the compiler. + if not getattr(attr, "module_map", None) and not ( + compilation_context.direct_headers or + compilation_context.direct_textual_headers + ): + return None, None + + module_name = getattr(attr, "module_name", None) + if not module_name: + module_name = derive_module_name(target.label) + + # For an `objc_library`, if we're emitting an explicit module, generate + # our own module map instead of using the one generated by the native + # Bazel `objc_library` implementation. This is necessary to get the + # correct `use` decls for dependencies to include everything in + # `SwiftInfo.direct_modules[].clang`, which will reference targets that + # `ObjcProvider` doesn't know about, like SDK modules. + # TODO(b/142867905): Once explicit modules are enabled by default, make + # this the only code path (i.e., this aspect should generate all module + # maps for `objc_library` targets). + emit_c_module = is_feature_enabled( + feature_configuration = feature_configuration, + feature_name = SWIFT_FEATURE_EMIT_C_MODULE, + ) + if not emit_c_module: + return module_name, module_maps[0] + + module_map_file = _generate_module_map( + actions = aspect_ctx.actions, + compilation_context = compilation_context, + dependent_module_names = dependent_module_names, + feature_configuration = feature_configuration, + module_name = module_name, + target = target, + ) + return module_name, module_map_file + + # If a target doesn't have any headers (and if we're on this code path, it + # didn't provide an explicit module map), then don't generate a module map + # for it. Such modules define nothing and only waste space on the + # compilation command line and add more work for the compiler. + if not ( + compilation_context.direct_headers or + compilation_context.direct_textual_headers + ): + return None, None + + if not module_name: + # For all other targets, there is no mechanism to provide a custom + # module map, and we only generate one if the target is tagged. + module_name = _tagged_target_module_name( + label = target.label, + tags = attr.tags, + ) + if not module_name: + return None, None + + module_map_file = _generate_module_map( + actions = aspect_ctx.actions, + compilation_context = compilation_context, + dependent_module_names = dependent_module_names, + feature_configuration = feature_configuration, + module_name = module_name, + target = target, + ) + return module_name, module_map_file + +def _handle_module( + aspect_ctx, + compilation_context, + feature_configuration, + module_map_file, + module_name, swift_infos, swift_toolchain, target): - """Processes a C++ target that is a dependency of a Swift target. + """Processes a C/Objective-C target that is a dependency of a Swift target. Args: aspect_ctx: The aspect's context. + compilation_context: The `CcCompilationContext` containing the target's + headers. feature_configuration: The current feature configuration. + module_map_file: The `.modulemap` file that defines the module, or None + if it should be inferred from other properties of the target (for + legacy support). + module_name: The name of the module, or None if it should be inferred + from other properties of the target (for legacy support). swift_infos: The `SwiftInfo` providers of the current target's dependencies, which should be merged into the `SwiftInfo` provider - created and returned for this C++ target. + created and returned for this target. swift_toolchain: The Swift toolchain being used to build this target. target: The C++ target to which the aspect is currently being applied. @@ -161,61 +424,34 @@ def _handle_cc_target( """ attr = aspect_ctx.rule.attr - # TODO(b/142867898): Only check `CcInfo` once all rules correctly propagate - # it. - if CcInfo in target: - compilation_context = target[CcInfo].compilation_context - else: - compilation_context = None - - if swift_infos: - merged_swift_info = create_swift_info(swift_infos = swift_infos) - else: - merged_swift_info = None - - module_map_file = None - module_name = None + all_swift_infos = ( + swift_infos + swift_toolchain.clang_implicit_deps_providers.swift_infos + ) # Collect the names of Clang modules that the module being built directly # depends on. dependent_module_names = [] - for swift_info in swift_infos: + for swift_info in all_swift_infos: for module in swift_info.direct_modules: if module.clang: dependent_module_names.append(module.name) - if apple_common.Objc in target: - # TODO(b/142867898): For `objc_library`, stop using the module map from - # the Objc provider and generate our own. (For imported frameworks, - # continue using the module map included with it.) - objc = target[apple_common.Objc] - module_maps = objc.direct_module_maps - if module_maps: - if aspect_ctx.rule.kind == "objc_library": - module_name = getattr(attr, "module_name", None) - if not module_name: - module_name = derive_module_name(target.label) - module_map_file = module_maps[0] - else: - # For all other targets, there is no mechanism to provide a custom - # module map, and we only generate one if the target is tagged. - module_name = _tagged_target_module_name( - label = target.label, - tags = attr.tags, + # If we weren't passed a module map (i.e., from a `_SwiftInteropInfo` + # provider), infer it and the module name based on properties of the rule to + # support legacy rules. + if not module_map_file: + module_name, module_map_file = _module_info_for_target( + target = target, + aspect_ctx = aspect_ctx, + compilation_context = compilation_context, + dependent_module_names = dependent_module_names, + feature_configuration = feature_configuration, + module_name = module_name, ) - if module_name: - module_map_file = _generate_module_map( - actions = aspect_ctx.actions, - compilation_context = compilation_context, - dependent_module_names = dependent_module_names, - feature_configuration = feature_configuration, - module_name = module_name, - target = target, - ) if not module_map_file: - if merged_swift_info: - return [merged_swift_info] + if all_swift_infos: + return [create_swift_info(swift_infos = swift_infos)] else: return [] @@ -224,24 +460,12 @@ def _handle_cc_target( # a framework import rule. For now, we won't support compiling those as # explicit modules; fix this. if module_name: - # We only need to propagate the information from the compilation - # contexts, but we can't merge those directly; we can only merge - # `CcInfo` objects. So we "unwrap" the compilation context from each - # provider and then "rewrap" it in a new provider that lacks the linking - # context so that our merge operation does less work. - target_and_deps_cc_infos = [ - CcInfo(compilation_context = compilation_context), - ] - for dep in getattr(attr, "deps", []): - if CcInfo in dep: - target_and_deps_cc_infos.append( - CcInfo(compilation_context = dep[CcInfo].compilation_context), - ) - - compilation_context_to_compile = cc_common.merge_cc_infos( - direct_cc_infos = target_and_deps_cc_infos, - ).compilation_context - + compilation_context_to_compile = ( + compilation_context_for_explicit_module_compilation( + compilation_contexts = [compilation_context], + deps = getattr(attr, "deps", []), + ) + ) precompiled_module = precompile_clang_module( actions = aspect_ctx.actions, bin_dir = aspect_ctx.bin_dir, @@ -250,7 +474,7 @@ def _handle_cc_target( genfiles_dir = aspect_ctx.genfiles_dir, module_map_file = module_map_file, module_name = module_name, - swift_info = merged_swift_info, + swift_infos = swift_infos, swift_toolchain = swift_toolchain, target_name = target.label.name, ) @@ -261,51 +485,88 @@ def _handle_cc_target( module_name = derive_module_name(target.label) precompiled_module = None - return [create_swift_info( - modules = [ - create_module( - name = module_name, - clang = create_clang_module( - compilation_context = compilation_context, - module_map = module_map_file, - precompiled_module = precompiled_module, + providers = [ + create_swift_info( + modules = [ + create_module( + name = module_name, + clang = create_clang_module( + compilation_context = compilation_context, + module_map = module_map_file, + precompiled_module = precompiled_module, + ), ), + ], + swift_infos = swift_infos, + ), + ] + + if precompiled_module: + providers.append( + OutputGroupInfo( + swift_explicit_module = depset([precompiled_module]), ), - ], - swift_infos = swift_infos, - )] + ) + + return providers def _swift_clang_module_aspect_impl(target, aspect_ctx): # Do nothing if the target already propagates `SwiftInfo`. if SwiftInfo in target: return [] - # Collect `SwiftInfo` providers from dependencies, based on the attributes - # that this aspect traverses. - attr = aspect_ctx.rule.attr - deps = [] - for attr_name in _MULTIPLE_TARGET_ASPECT_ATTRS: - deps.extend(getattr(attr, attr_name, [])) - for attr_name in _SINGLE_TARGET_ASPECT_ATTRS: - dep = getattr(attr, attr_name, None) - if dep: - deps.append(dep) - swift_infos = get_providers(deps, SwiftInfo) + if CcInfo in target: + compilation_context = target[CcInfo].compilation_context + else: + compilation_context = None + + requested_features = aspect_ctx.features + unsupported_features = aspect_ctx.disabled_features + + if _SwiftInteropInfo in target: + interop_info = target[_SwiftInteropInfo] + module_map_file = interop_info.module_map + module_name = ( + interop_info.module_name or derive_module_name(target.label) + ) + swift_infos = interop_info.swift_infos + requested_features.extend(interop_info.requested_features) + unsupported_features.extend(interop_info.unsupported_features) + else: + module_map_file = None + module_name = None + + # Collect `SwiftInfo` providers from dependencies, based on the + # attributes that this aspect traverses. + deps = [] + attr = aspect_ctx.rule.attr + for attr_name in _MULTIPLE_TARGET_ASPECT_ATTRS: + deps.extend(getattr(attr, attr_name, [])) + for attr_name in _SINGLE_TARGET_ASPECT_ATTRS: + dep = getattr(attr, attr_name, None) + if dep: + deps.append(dep) + swift_infos = get_providers(deps, SwiftInfo) swift_toolchain = aspect_ctx.attr._toolchain_for_aspect[SwiftToolchainInfo] feature_configuration = configure_features( ctx = aspect_ctx, - requested_features = aspect_ctx.features, + requested_features = requested_features, swift_toolchain = swift_toolchain, - unsupported_features = aspect_ctx.disabled_features, + unsupported_features = unsupported_features, ) - # TODO(b/142867898): Only check `CcInfo` once all native rules correctly - # propagate both. - if apple_common.Objc in target or CcInfo in target: - return _handle_cc_target( + if ( + _SwiftInteropInfo in target or + apple_common.Objc in target or + CcInfo in target + ): + return _handle_module( aspect_ctx = aspect_ctx, + compilation_context = compilation_context, feature_configuration = feature_configuration, + module_map_file = module_map_file, + module_name = module_name, swift_infos = swift_infos, swift_toolchain = swift_toolchain, target = target, @@ -336,10 +597,10 @@ depends on an `objc_library` that depends on a `swift_library`). It also manages module map generation for `cc_library` targets that have the `swift_module` tag. This tag may take one of two forms: - * `swift_module`: By itself, this indicates that the target is compatible - with Swift and should be given a module name that is derived from its - target label. - * `swift_module=name`: The module should be given the name `name`. +* `swift_module`: By itself, this indicates that the target is compatible + with Swift and should be given a module name that is derived from its + target label. +* `swift_module=name`: The module should be given the name `name`. Note that the public headers of such `cc_library` targets must be parsable as C, since Swift does not support C++ interop at this time. diff --git a/swift/internal/swift_common.bzl b/swift/internal/swift_common.bzl index 09dffc4d5..985103abc 100644 --- a/swift/internal/swift_common.bzl +++ b/swift/internal/swift_common.bzl @@ -33,7 +33,6 @@ load( ":compiling.bzl", "compile", "derive_module_name", - "get_implicit_deps", "precompile_clang_module", ) load( @@ -42,7 +41,6 @@ load( "get_cc_feature_configuration", "is_feature_enabled", ) -load(":linking.bzl", "swift_runtime_linkopts") load( ":providers.bzl", "create_clang_module", @@ -50,7 +48,7 @@ load( "create_swift_info", "create_swift_module", ) -load(":swift_clang_module_aspect.bzl", "swift_clang_module_aspect") +load(":swift_clang_module_aspect.bzl", "create_swift_interop_info") # The exported `swift_common` module, which defines the public API for directly # invoking actions that compile Swift code from other rules. @@ -62,13 +60,11 @@ swift_common = struct( create_clang_module = create_clang_module, create_module = create_module, create_swift_info = create_swift_info, + create_swift_interop_info = create_swift_interop_info, create_swift_module = create_swift_module, derive_module_name = derive_module_name, - get_implicit_deps = get_implicit_deps, is_enabled = is_feature_enabled, library_rule_attrs = swift_library_rule_attrs, precompile_clang_module = precompile_clang_module, - swift_clang_module_aspect = swift_clang_module_aspect, - swift_runtime_linkopts = swift_runtime_linkopts, toolchain_attrs = swift_toolchain_attrs, ) diff --git a/swift/internal/swift_feature_allowlist.bzl b/swift/internal/swift_feature_allowlist.bzl new file mode 100644 index 000000000..fc31a778f --- /dev/null +++ b/swift/internal/swift_feature_allowlist.bzl @@ -0,0 +1,109 @@ +# Copyright 2021 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. + +"""Support for restricting access to features based on an allowlist.""" + +load(":providers.bzl", "SwiftAllowlistPackageInfo", "SwiftFeatureAllowlistInfo") + +def _parse_allowlist_package(package_spec): + """Parses an allowlist package specification from a string. + + Args: + package_spec: A string that represents a possibly recursive package + specification, with an optional exclusion marker in front. + + Returns: + An instance of `SwiftAllowlistPackageInfo` containing the parsed + information from the package specification. + """ + if package_spec.startswith("-"): + excluded = True + package_spec = package_spec[1:] + else: + excluded = False + + if package_spec.endswith("/..."): + match_subpackages = True + package_spec = package_spec[:-4] + else: + match_subpackages = False + + return SwiftAllowlistPackageInfo( + excluded = excluded, + match_subpackages = match_subpackages, + package = package_spec, + ) + +def _swift_feature_allowlist_impl(ctx): + return [SwiftFeatureAllowlistInfo( + allowlist_label = str(ctx.label), + managed_features = ctx.attr.managed_features, + packages = [ + _parse_allowlist_package(package_spec) + for package_spec in ctx.attr.packages + ], + )] + +swift_feature_allowlist = rule( + attrs = { + "managed_features": attr.string_list( + allow_empty = True, + doc = """\ +A list of feature strings that are permitted to be specified by the targets in +the packages matched by the `packages` attribute. This list may include both +feature names and/or negations (a name with a leading `-`); a regular feature +name means that the targets in the matching packages may explicitly request that +the feature be enabled, and a negated feature means that the target may +explicitly request that the feature be disabled. + +For example, `managed_features = ["foo", "-bar"]` means that targets in the +allowlist's packages may request that feature `"foo"` be enabled and that +feature `"bar"` be disabled. +""", + mandatory = False, + ), + "packages": attr.string_list( + allow_empty = True, + doc = """\ +A list of strings representing packages (possibly recursive) whose targets are +allowed to enable/disable the features in `managed_features`. Each package +pattern is written in the syntax used by the `package_group` function: + +* `//foo/bar`: Targets in the package `//foo/bar` but not in subpackages. +* `//foo/bar/...`: Targets in the package `//foo/bar` and any of its + subpackages. +* A leading `-` excludes packages that would otherwise have been included by + the patterns in the list. + +Exclusions always take priority over inclusions; order in the list is +irrelevant. +""", + mandatory = True, + ), + }, + doc = """\ +Limits the ability to request or disable certain features to a set of packages +(and possibly subpackages) in the workspace. + +A Swift toolchain target can reference any number (zero or more) of +`swift_feature_allowlist` targets. The features managed by these allowlists may +overlap. For some package _P_, a feature is allowed to be used by targets in +that package if _P_ matches the `packages` patterns in *all* of the allowlists +that manage that feature. + +A feature that is not managed by any allowlist is allowed to be used by any +package. +""", + implementation = _swift_feature_allowlist_impl, +) diff --git a/swift/internal/swift_grpc_library.bzl b/swift/internal/swift_grpc_library.bzl index d8960fc19..3298920ba 100644 --- a/swift/internal/swift_grpc_library.bzl +++ b/swift/internal/swift_grpc_library.bzl @@ -24,13 +24,14 @@ load( load( ":feature_names.bzl", "SWIFT_FEATURE_ENABLE_TESTING", - "SWIFT_FEATURE_NO_GENERATED_HEADER", + "SWIFT_FEATURE_GENERATE_FROM_RAW_PROTO_FILES", ) -load(":linking.bzl", "register_libraries_to_link") +load(":linking.bzl", "create_linker_input") load( ":proto_gen_utils.bzl", "declare_generated_files", "extract_generated_dir_path", + "proto_import_path", "register_module_mapping_write_action", ) load(":providers.bzl", "SwiftInfo", "SwiftProtoInfo", "SwiftToolchainInfo") @@ -40,7 +41,6 @@ load( "compact", "create_cc_info", "get_providers", - "proto_import_path", ) def _register_grpcswift_generate_action( @@ -50,6 +50,7 @@ def _register_grpcswift_generate_action( proto_source_root, transitive_descriptor_sets, module_mapping_file, + generate_from_proto_sources, mkdir_and_run, protoc_executable, protoc_plugin_executable, @@ -71,6 +72,10 @@ def _register_grpcswift_generate_action( target being analyzed. May be `None`, in which case no module mapping will be passed (the case for leaf nodes in the dependency graph). + generate_from_proto_sources: True/False for is generation should happen + from proto source file vs just via the DescriptorSets. The Sets + don't have source info, so the generated sources won't have + comments (https://github.com/bazelbuild/bazel/issues/9337). mkdir_and_run: The `File` representing the `mkdir_and_run` executable. protoc_executable: The `File` representing the `protoc` executable. protoc_plugin_executable: The `File` representing the `protoc` plugin @@ -140,19 +145,39 @@ def _register_grpcswift_generate_action( extra_module_imports, format_each = "--swiftgrpc_opt=ExtraModuleImports=%s", ) - #if module_mapping_file and proto_visibility != "Internal": - # protoc_args.add( - # module_mapping_file, - # format = "--swiftgrpc_opt=ProtoPathModuleMappings=%s", - # ) - protoc_args.add("--descriptor_set_in") - protoc_args.add_joined(transitive_descriptor_sets, join_with = ":") + if module_mapping_file: + protoc_args.add( + module_mapping_file, + format = "--swiftgrpc_opt=ProtoPathModuleMappings=%s", + ) + + protoc_args.add_joined( + transitive_descriptor_sets, + join_with = ":", + format_joined = "--descriptor_set_in=%s", + omit_if_empty = True, + ) + + if generate_from_proto_sources: + # ProtoCompileActionBuilder.java's XPAND_TRANSITIVE_PROTO_PATH_FLAGS + # leaves this off also. + if proto_source_root != ".": + protoc_args.add(proto_source_root, format = "--proto_path=%s") + + # Follow ProtoCompileActionBuilder.java's + # ExpandImportArgsFn::expandToCommandLine() logic and provide a mapping + # for each file to the proto path. + for f in direct_srcs: + protoc_args.add("-I%s=%s" % (proto_import_path(f, proto_source_root), f.path)) + protoc_args.add_all([ proto_import_path(f, proto_source_root) for f in direct_srcs ]) additional_command_inputs = [] + if generate_from_proto_sources: + additional_command_inputs.extend(direct_srcs) if module_mapping_file: additional_command_inputs.append(module_mapping_file) @@ -191,16 +216,41 @@ def _swift_grpc_library_impl(ctx): swift_toolchain = ctx.attr._toolchain[SwiftToolchainInfo] - # Direct sources are passed as arguments to protoc to generate *only* the - # files in this target, but we need to pass the transitive sources as inputs - # to the generating action so that all the dependent files are available for - # protoc to parse. - # Instead of providing all those files and opening/reading them, we use - # protoc's support for reading descriptor sets to resolve things. - direct_srcs = ctx.attr.srcs[0][ProtoInfo].direct_sources - transitive_descriptor_sets = ( - ctx.attr.srcs[0][ProtoInfo].transitive_descriptor_sets + unsupported_features = ctx.disabled_features + if ctx.attr.flavor != "client_stubs": + unsupported_features.append(SWIFT_FEATURE_ENABLE_TESTING) + + feature_configuration = swift_common.configure_features( + ctx = ctx, + requested_features = ctx.features, + swift_toolchain = swift_toolchain, + unsupported_features = unsupported_features, ) + + generate_from_proto_sources = swift_common.is_enabled( + feature_configuration = feature_configuration, + feature_name = SWIFT_FEATURE_GENERATE_FROM_RAW_PROTO_FILES, + ) + + srcs_proto_info = ctx.attr.srcs[0][ProtoInfo] + + # Only the files for direct sources should be generated, but the + # transitive descriptor sets are still need to be able to parse/load + # those descriptors. + direct_srcs = srcs_proto_info.direct_sources + if generate_from_proto_sources: + # Take the transitive descriptor sets from the proto_library deps, + # so the direct sources won't be in any descriptor sets to reduce + # the input to the action (and what protoc has to parse). + direct_descriptor_set = srcs_proto_info.direct_descriptor_set + transitive_descriptor_sets = depset(direct = [ + x + for x in srcs_proto_info.transitive_descriptor_sets.to_list() + if x != direct_descriptor_set + ]) + else: + transitive_descriptor_sets = srcs_proto_info.transitive_descriptor_sets + deps = ctx.attr.deps minimal_module_mappings = deps[0][SwiftProtoInfo].module_mappings @@ -219,9 +269,10 @@ def _swift_grpc_library_impl(ctx): ctx.label, ctx.actions, direct_srcs, - ctx.attr.srcs[0][ProtoInfo].proto_source_root, + srcs_proto_info.proto_source_root, transitive_descriptor_sets, transitive_module_mapping_file, + generate_from_proto_sources, ctx.executable._mkdir_and_run, ctx.executable._protoc, ctx.executable._protoc_gen_swiftgrpc, @@ -236,17 +287,6 @@ def _swift_grpc_library_impl(ctx): # action. compile_deps = deps + ctx.attr._proto_support - unsupported_features = ctx.disabled_features - if ctx.attr.flavor != "client_stubs": - unsupported_features.append(SWIFT_FEATURE_ENABLE_TESTING) - - feature_configuration = swift_common.configure_features( - ctx = ctx, - requested_features = ctx.features + [SWIFT_FEATURE_NO_GENERATED_HEADER], - swift_toolchain = swift_toolchain, - unsupported_features = unsupported_features, - ) - module_name = swift_common.derive_module_name(ctx.label) compilation_outputs = swift_common.compile( @@ -260,18 +300,21 @@ def _swift_grpc_library_impl(ctx): srcs = generated_files, swift_toolchain = swift_toolchain, target_name = ctx.label.name, + workspace_name = ctx.workspace_name, ) - library_to_link = register_libraries_to_link( + linker_input, library_to_link = create_linker_input( actions = ctx.actions, alwayslink = False, cc_feature_configuration = swift_common.cc_feature_configuration( feature_configuration = feature_configuration, ), + compilation_outputs = compilation_outputs, is_dynamic = False, is_static = True, library_name = ctx.label.name, objects = compilation_outputs.object_files, + owner = ctx.label, swift_toolchain = swift_toolchain, ) @@ -289,7 +332,7 @@ def _swift_grpc_library_impl(ctx): create_cc_info( cc_infos = get_providers(compile_deps, CcInfo), compilation_outputs = compilation_outputs, - libraries_to_link = [library_to_link], + linker_inputs = [linker_input], ), deps[0][SwiftProtoInfo], swift_common.create_swift_info( @@ -351,7 +394,9 @@ on the `swift_grpc_library` implementing the service. The kind of definitions that should be generated: * `"client"` to generate client definitions. + * `"client_stubs"` to generate client test stubs. + * `"server"` to generate server definitions. """, ), @@ -476,13 +521,6 @@ def _swift_grpc_code_impl(ctx): ) deps = ctx.attr.deps - minimal_module_mappings = deps[0][SwiftProtoInfo].module_mappings - transitive_module_mapping_file = register_module_mapping_write_action( - ctx.label.name, - ctx.actions, - minimal_module_mappings, - ) - extra_module_imports = [] if ctx.attr.flavor == "client_stubs": extra_module_imports.append(swift_common.derive_module_name(deps[0].label)) @@ -494,7 +532,8 @@ def _swift_grpc_code_impl(ctx): direct_srcs, ctx.attr.srcs[0][ProtoInfo].proto_source_root, transitive_descriptor_sets, - transitive_module_mapping_file, + None, + True, ctx.executable._mkdir_and_run, ctx.executable._protoc, ctx.executable._protoc_gen_swiftgrpc, diff --git a/swift/internal/swift_import.bzl b/swift/internal/swift_import.bzl index 7fdfd3f31..079feeb3a 100644 --- a/swift/internal/swift_import.bzl +++ b/swift/internal/swift_import.bzl @@ -38,15 +38,18 @@ def _swift_import_impl(ctx): unsupported_features = ctx.disabled_features, ) - libraries_to_link = [ - cc_common.create_library_to_link( - actions = ctx.actions, - cc_toolchain = cc_toolchain, - feature_configuration = cc_feature_configuration, - static_library = archive, - ) - for archive in archives - ] + linker_input = cc_common.create_linker_input( + owner = ctx.label, + libraries = depset([ + cc_common.create_library_to_link( + actions = ctx.actions, + cc_toolchain = cc_toolchain, + feature_configuration = cc_feature_configuration, + static_library = archive, + ) + for archive in archives + ]), + ) providers = [ DefaultInfo( @@ -59,7 +62,7 @@ def _swift_import_impl(ctx): ), create_cc_info( cc_infos = get_providers(deps, CcInfo), - libraries_to_link = libraries_to_link, + linker_inputs = [linker_input], ), # Propagate an `Objc` provider so that Apple-specific rules like # apple_binary` will link the imported library properly. Typically we'd diff --git a/swift/internal/swift_library.bzl b/swift/internal/swift_library.bzl index 374b5184e..de5bc9d09 100644 --- a/swift/internal/swift_library.bzl +++ b/swift/internal/swift_library.bzl @@ -15,9 +15,13 @@ """Implementation of the `swift_library` rule.""" load(":attrs.bzl", "swift_deps_attr") +load( + ":build_settings.bzl", + "PerModuleSwiftCoptSettingInfo", + "additional_per_module_swiftcopts", +) load( ":compiling.bzl", - "find_swift_version_copt_value", "new_objc_provider", "output_groups_from_compilation_outputs", "swift_library_output_map", @@ -28,14 +32,16 @@ load( "SWIFT_FEATURE_ENABLE_LIBRARY_EVOLUTION", "SWIFT_FEATURE_SUPPORTS_PRIVATE_DEPS", ) -load(":linking.bzl", "register_libraries_to_link") +load(":linking.bzl", "create_linker_input") load(":providers.bzl", "SwiftInfo", "SwiftToolchainInfo") +load(":swift_clang_module_aspect.bzl", "swift_clang_module_aspect") load(":swift_common.bzl", "swift_common") load( ":utils.bzl", "compact", "create_cc_info", "expand_locations", + "expand_make_variables", "get_providers", ) load("@bazel_skylib//lib:dicts.bzl", "dicts") @@ -101,9 +107,17 @@ def _swift_library_impl(ctx): # These can't use additional_inputs since expand_locations needs targets, # not files. copts = expand_locations(ctx, ctx.attr.copts, ctx.attr.swiftc_inputs) + copts = expand_make_variables(ctx, copts, "copts") linkopts = expand_locations(ctx, ctx.attr.linkopts, ctx.attr.swiftc_inputs) + linkopts = expand_make_variables(ctx, linkopts, "linkopts") srcs = ctx.files.srcs + module_copts = additional_per_module_swiftcopts( + ctx.label, + ctx.attr._per_module_swiftcopt[PerModuleSwiftCoptSettingInfo], + ) + copts.extend(module_copts) + extra_features = [] if ctx.attr._config_emit_swiftinterface[BuildSettingInfo].value: extra_features.append(SWIFT_FEATURE_ENABLE_LIBRARY_EVOLUTION) @@ -121,10 +135,6 @@ def _swift_library_impl(ctx): unsupported_features = ctx.disabled_features, ) - implicit_deps = swift_common.get_implicit_deps( - feature_configuration = feature_configuration, - swift_toolchain = swift_toolchain, - ) if swift_common.is_enabled( feature_configuration = feature_configuration, feature_name = SWIFT_FEATURE_SUPPORTS_PRIVATE_DEPS, @@ -135,8 +145,8 @@ def _swift_library_impl(ctx): # deps in "deps", either, so we need to make sure not to pass them in to # `_check_deps_are_disjoint`. deps = ctx.attr.deps - private_deps = ctx.attr.private_deps + implicit_deps - _check_deps_are_disjoint(ctx.label, deps, ctx.attr.private_deps) + private_deps = ctx.attr.private_deps + _check_deps_are_disjoint(ctx.label, deps, private_deps) elif ctx.attr.private_deps: fail( ("In target '{}', 'private_deps' cannot be used because this " + @@ -148,58 +158,78 @@ def _swift_library_impl(ctx): deps = ctx.attr.deps private_deps = [] + if ctx.attr.generates_header: + generated_header_name = ( + ctx.attr.generated_header_name or + "{}-Swift.h".format(ctx.label.name) + ) + elif not ctx.attr.generated_header_name: + generated_header_name = None + else: + fail( + "'generated_header_name' may only be provided when " + + "'generates_header' is True.", + attr = "generated_header_name", + ) + compilation_outputs = swift_common.compile( actions = ctx.actions, additional_inputs = additional_inputs, bin_dir = ctx.bin_dir, copts = _maybe_parse_as_library_copts(srcs) + copts, defines = ctx.attr.defines, - deps = deps + private_deps, + deps = deps, feature_configuration = feature_configuration, - generated_header_name = ctx.attr.generated_header_name, + generated_header_name = generated_header_name, genfiles_dir = ctx.genfiles_dir, module_name = module_name, + private_deps = private_deps, srcs = srcs, swift_toolchain = swift_toolchain, target_name = ctx.label.name, + workspace_name = ctx.workspace_name, ) - # If a module map was created for the generated header, propagate it as a - # Clang module so that it is passed as a module input to upstream - # compilation actions. + # If a module was created for the generated header, propagate it as well so + # that it is passed as a module input to upstream compilation actions. if compilation_outputs.generated_module_map: clang_module = swift_common.create_clang_module( compilation_context = cc_common.create_compilation_context( headers = depset([compilation_outputs.generated_header]), ), module_map = compilation_outputs.generated_module_map, - # TODO(b/142867898): Precompile the module and place it here. - precompiled_module = None, + precompiled_module = compilation_outputs.precompiled_module, ) else: clang_module = None - library_to_link = register_libraries_to_link( + linker_input, library_to_link = create_linker_input( actions = ctx.actions, + additional_inputs = additional_inputs, alwayslink = ctx.attr.alwayslink, cc_feature_configuration = swift_common.cc_feature_configuration( feature_configuration = feature_configuration, ), + compilation_outputs = compilation_outputs, is_dynamic = False, is_static = True, library_name = ctx.label.name, objects = compilation_outputs.object_files, + owner = ctx.label, swift_toolchain = swift_toolchain, + user_link_flags = linkopts, ) direct_output_files = compact([ compilation_outputs.generated_header, + compilation_outputs.precompiled_module, compilation_outputs.swiftdoc, compilation_outputs.swiftinterface, compilation_outputs.swiftmodule, library_to_link.pic_static_library, ]) + implicit_deps_providers = swift_toolchain.implicit_deps_providers providers = [ DefaultInfo( files = depset(direct_output_files), @@ -213,14 +243,15 @@ def _swift_library_impl(ctx): compilation_outputs = compilation_outputs, )), create_cc_info( - additional_inputs = additional_inputs, cc_infos = get_providers(deps, CcInfo), compilation_outputs = compilation_outputs, defines = ctx.attr.defines, includes = [ctx.bin_dir.path], - libraries_to_link = [library_to_link], - private_cc_infos = get_providers(private_deps, CcInfo), - user_link_flags = linkopts, + linker_inputs = [linker_input], + private_cc_infos = ( + get_providers(private_deps, CcInfo) + + implicit_deps_providers.cc_infos + ), ), coverage_common.instrumented_files_info( ctx, @@ -244,7 +275,6 @@ def _swift_library_impl(ctx): # Note that private_deps are explicitly omitted here; they should # not propagate. swift_infos = get_providers(deps, SwiftInfo), - swift_version = find_swift_version_copt_value(copts), ), ] @@ -264,6 +294,7 @@ def _swift_library_impl(ctx): static_archives = compact([library_to_link.pic_static_library]), swiftmodules = [compilation_outputs.swiftmodule], objc_header = compilation_outputs.generated_header, + objc_providers = implicit_deps_providers.objc_infos, )) return providers @@ -271,11 +302,11 @@ def _swift_library_impl(ctx): swift_library = rule( attrs = dicts.add( swift_common.library_rule_attrs(additional_deps_aspects = [ - swift_common.swift_clang_module_aspect, + swift_clang_module_aspect, ]), { "private_deps": swift_deps_attr( - aspects = [swift_common.swift_clang_module_aspect], + aspects = [swift_clang_module_aspect], doc = """\ A list of targets that are implementation-only dependencies of the target being built. Libraries/linker flags from these dependencies will be propagated to diff --git a/swift/internal/swift_module_alias.bzl b/swift/internal/swift_module_alias.bzl index 28eea793c..66942b40c 100644 --- a/swift/internal/swift_module_alias.bzl +++ b/swift/internal/swift_module_alias.bzl @@ -21,7 +21,7 @@ load( "output_groups_from_compilation_outputs", ) load(":derived_files.bzl", "derived_files") -load(":linking.bzl", "register_libraries_to_link") +load(":linking.bzl", "create_linker_input") load(":providers.bzl", "SwiftInfo", "SwiftToolchainInfo") load(":swift_common.bzl", "swift_common") load(":utils.bzl", "compact", "create_cc_info", "get_providers") @@ -29,9 +29,9 @@ load(":utils.bzl", "compact", "create_cc_info", "get_providers") def _swift_module_alias_impl(ctx): deps = ctx.attr.deps module_mapping = { - dep[SwiftInfo].module_name: dep.label + module.name: dep.label for dep in deps - if dep[SwiftInfo].module_name + for module in dep[SwiftInfo].direct_modules } module_name = ctx.attr.module_name @@ -70,18 +70,21 @@ def _swift_module_alias_impl(ctx): srcs = [reexport_src], swift_toolchain = swift_toolchain, target_name = ctx.label.name, + workspace_name = ctx.workspace_name, ) - library_to_link = register_libraries_to_link( + linker_input, library_to_link = create_linker_input( actions = ctx.actions, alwayslink = False, cc_feature_configuration = swift_common.cc_feature_configuration( feature_configuration = feature_configuration, ), + compilation_outputs = compilation_outputs, is_dynamic = False, is_static = True, library_name = ctx.label.name, objects = compilation_outputs.object_files, + owner = ctx.label, swift_toolchain = swift_toolchain, ) @@ -97,11 +100,15 @@ def _swift_module_alias_impl(ctx): OutputGroupInfo(**output_groups_from_compilation_outputs( compilation_outputs = compilation_outputs, )), + coverage_common.instrumented_files_info( + ctx, + dependency_attributes = ["deps"], + ), create_cc_info( cc_infos = get_providers(deps, CcInfo), compilation_outputs = compilation_outputs, includes = [ctx.bin_dir.path], - libraries_to_link = [library_to_link], + linker_inputs = [linker_input], ), swift_common.create_swift_info( modules = [ diff --git a/swift/internal/swift_protoc_gen_aspect.bzl b/swift/internal/swift_protoc_gen_aspect.bzl index 605169bf6..1ecf453ee 100644 --- a/swift/internal/swift_protoc_gen_aspect.bzl +++ b/swift/internal/swift_protoc_gen_aspect.bzl @@ -24,13 +24,14 @@ load( "SWIFT_FEATURE_EMIT_SWIFTINTERFACE", "SWIFT_FEATURE_ENABLE_LIBRARY_EVOLUTION", "SWIFT_FEATURE_ENABLE_TESTING", - "SWIFT_FEATURE_NO_GENERATED_HEADER", + "SWIFT_FEATURE_GENERATE_FROM_RAW_PROTO_FILES", ) -load(":linking.bzl", "register_libraries_to_link") +load(":linking.bzl", "create_linker_input") load( ":proto_gen_utils.bzl", "declare_generated_files", "extract_generated_dir_path", + "proto_import_path", "register_module_mapping_write_action", ) load(":providers.bzl", "SwiftInfo", "SwiftProtoInfo", "SwiftToolchainInfo") @@ -40,7 +41,6 @@ load( "compact", "create_cc_info", "get_providers", - "proto_import_path", ) # The paths of proto files bundled with the runtime. This is mainly the well @@ -109,6 +109,7 @@ def _register_pbswift_generate_action( proto_source_root, transitive_descriptor_sets, module_mapping_file, + generate_from_proto_sources, mkdir_and_run, proto_visibility, protoc_executable, @@ -128,6 +129,10 @@ def _register_pbswift_generate_action( target being analyzed. May be `None`, in which case no module mapping will be passed (the case for leaf nodes in the dependency graph). + generate_from_proto_sources: True/False for is generation should happen + from proto source file vs just via the DescriptorSets. The Sets + don't have source info, so the generated sources won't have + comments (https://github.com/bazelbuild/bazel/issues/9337). mkdir_and_run: The `File` representing the `mkdir_and_run` executable. proto_visibility: Whether the generated code should have access control set to Public or Internal. protoc_executable: The `File` representing the `protoc` executable. @@ -175,19 +180,37 @@ def _register_pbswift_generate_action( protoc_args.add("--swift_opt=Visibility=Internal") else: protoc_args.add("--swift_opt=Visibility=Public") + if module_mapping_file: + protoc_args.add( + module_mapping_file, + format = "--swift_opt=ProtoPathModuleMappings=%s", + ) + protoc_args.add_joined( + transitive_descriptor_sets, + join_with = ":", + format_joined = "--descriptor_set_in=%s", + omit_if_empty = True, + ) + + if generate_from_proto_sources: + # ProtoCompileActionBuilder.java's XPAND_TRANSITIVE_PROTO_PATH_FLAGS + # leaves this off also. + if proto_source_root != ".": + protoc_args.add(proto_source_root, format = "--proto_path=%s") + + # Follow ProtoCompileActionBuilder.java's + # ExpandImportArgsFn::expandToCommandLine() logic and provide a mapping + # for each file to the proto path. + for f in direct_srcs: + protoc_args.add("-I%s=%s" % (proto_import_path(f, proto_source_root), f.path)) - #if module_mapping_file and proto_visibility != "Internal": - # protoc_args.add( - # module_mapping_file, - # format = "--swift_opt=ProtoPathModuleMappings=%s", - # ) - protoc_args.add("--descriptor_set_in") - protoc_args.add_joined(transitive_descriptor_sets, join_with = ":") protoc_args.add_all( [proto_import_path(f, proto_source_root) for f in direct_srcs], ) additional_command_inputs = [] + if generate_from_proto_sources: + additional_command_inputs.extend(direct_srcs) if module_mapping_file: additional_command_inputs.append(module_mapping_file) @@ -304,13 +327,6 @@ def _swift_protoc_gen_aspect_impl(target, aspect_ctx): target[ProtoInfo].proto_source_root, ) - # Direct sources are passed as arguments to protoc to generate *only* the - # files in this target, but we need to pass the transitive sources as inputs - # to the generating action so that all the dependent files are available for - # protoc to parse. - # Instead of providing all those files and opening/reading them, we use - # protoc's support for reading descriptor sets to resolve things. - transitive_descriptor_sets = target[ProtoInfo].transitive_descriptor_sets proto_deps = [ dep for dep in aspect_ctx.rule.attr.deps @@ -340,20 +356,6 @@ def _swift_protoc_gen_aspect_impl(target, aspect_ctx): support_deps = aspect_ctx.attr._proto_support if direct_srcs: - # Generate the Swift sources from the .proto files. - pbswift_files = _register_pbswift_generate_action( - target.label, - aspect_ctx.actions, - direct_srcs, - target[ProtoInfo].proto_source_root, - transitive_descriptor_sets, - transitive_module_mapping_file, - aspect_ctx.executable._mkdir_and_run, - "Public", # stay compatible with upstream - aspect_ctx.executable._protoc, - aspect_ctx.executable._protoc_gen_swift, - ) - extra_features = [] # This feature is not fully supported because the SwiftProtobuf library @@ -372,15 +374,48 @@ def _swift_protoc_gen_aspect_impl(target, aspect_ctx): # compile action. feature_configuration = swift_common.configure_features( ctx = aspect_ctx, - requested_features = aspect_ctx.features + extra_features + [ - SWIFT_FEATURE_NO_GENERATED_HEADER, - ], + requested_features = aspect_ctx.features + extra_features, swift_toolchain = swift_toolchain, unsupported_features = aspect_ctx.disabled_features + [ SWIFT_FEATURE_ENABLE_TESTING, ], ) + generate_from_proto_sources = swift_common.is_enabled( + feature_configuration = feature_configuration, + feature_name = SWIFT_FEATURE_GENERATE_FROM_RAW_PROTO_FILES, + ) + + # Only the files for direct sources should be generated, but the + # transitive descriptor sets are still need to be able to parse/load + # those descriptors. + if generate_from_proto_sources: + # Take the transitive descriptor sets from the proto_library deps, + # so the direct sources won't be in any descriptor sets to reduce + # the input to the action (and what protoc has to parse). + transitive_descriptor_sets = depset(transitive = [ + dep[ProtoInfo].transitive_descriptor_sets + for dep in aspect_ctx.rule.attr.deps + if ProtoInfo in dep + ]) + else: + transitive_descriptor_sets = target[ProtoInfo].transitive_descriptor_sets + + # Generate the Swift sources from the .proto files. + pbswift_files = _register_pbswift_generate_action( + target.label, + aspect_ctx.actions, + direct_srcs, + target[ProtoInfo].proto_source_root, + transitive_descriptor_sets, + transitive_module_mapping_file, + generate_from_proto_sources, + aspect_ctx.executable._mkdir_and_run, + "Public", # stay compatible with upstream + aspect_ctx.executable._protoc, + aspect_ctx.executable._protoc_gen_swift, + ) + module_name = swift_common.derive_module_name(target.label) compilation_outputs = swift_common.compile( @@ -394,14 +429,16 @@ def _swift_protoc_gen_aspect_impl(target, aspect_ctx): srcs = pbswift_files, swift_toolchain = swift_toolchain, target_name = target.label.name, + workspace_name = aspect_ctx.workspace_name, ) - library_to_link = register_libraries_to_link( + linker_input, library_to_link = create_linker_input( actions = aspect_ctx.actions, alwayslink = False, cc_feature_configuration = swift_common.cc_feature_configuration( feature_configuration = feature_configuration, ), + compilation_outputs = compilation_outputs, is_dynamic = False, is_static = True, # Prevent conflicts with C++ protos in the same output directory, @@ -409,6 +446,7 @@ def _swift_protoc_gen_aspect_impl(target, aspect_ctx): # `lib{name}.swift.a` instead. library_name = "{}.swift".format(target.label.name), objects = compilation_outputs.object_files, + owner = target.label, swift_toolchain = swift_toolchain, ) @@ -468,7 +506,6 @@ def _swift_protoc_gen_aspect_impl(target, aspect_ctx): includes = [aspect_ctx.bin_dir.path] objc_info = apple_common.new_objc_provider( providers = objc_infos, - uses_swift = True, **objc_info_args ) else: @@ -484,7 +521,7 @@ def _swift_protoc_gen_aspect_impl(target, aspect_ctx): cc_infos = cc_infos, compilation_outputs = compilation_outputs, includes = includes, - libraries_to_link = [library_to_link], + linker_inputs = [linker_input], ), objc_info = objc_info, ), @@ -667,7 +704,8 @@ def _swift_protoc_gen_code_aspect_impl(target, aspect_ctx): direct_srcs, target[ProtoInfo].proto_source_root, transitive_descriptor_sets, - transitive_module_mapping_file, + None, + True, aspect_ctx.executable._mkdir_and_run, aspect_ctx.attr.proto_visibility, aspect_ctx.executable._protoc, diff --git a/swift/internal/swift_toolchain.bzl b/swift/internal/swift_toolchain.bzl index d52efab7b..67672ca31 100644 --- a/swift/internal/swift_toolchain.bzl +++ b/swift/internal/swift_toolchain.bzl @@ -20,24 +20,26 @@ toolchain, see `swift.bzl`. """ load("@bazel_skylib//lib:dicts.bzl", "dicts") -load("@bazel_skylib//lib:partial.bzl", "partial") load("@bazel_tools//tools/cpp:toolchain_utils.bzl", "find_cpp_toolchain") load(":actions.bzl", "swift_action_names") load(":attrs.bzl", "swift_toolchain_driver_attrs") load(":autolinking.bzl", "autolink_extract_action_configs") -load(":compiling.bzl", "compile_action_configs") +load(":compiling.bzl", "compile_action_configs", "features_from_swiftcopts") load(":debugging.bzl", "modulewrap_action_configs") load( ":feature_names.bzl", "SWIFT_FEATURE_MODULE_MAP_HOME_IS_CWD", - "SWIFT_FEATURE_NO_GENERATED_HEADER", "SWIFT_FEATURE_NO_GENERATED_MODULE_MAP", "SWIFT_FEATURE_USE_RESPONSE_FILES", ) load(":features.bzl", "features_for_build_modes") -load(":providers.bzl", "SwiftToolchainInfo") +load(":providers.bzl", "SwiftFeatureAllowlistInfo", "SwiftToolchainInfo") load(":toolchain_config.bzl", "swift_toolchain_config") -load(":utils.bzl", "get_swift_executable_for_toolchain") +load( + ":utils.bzl", + "collect_implicit_deps_providers", + "get_swift_executable_for_toolchain", +) def _all_tool_configs( swift_executable, @@ -59,6 +61,15 @@ def _all_tool_configs( """ _swift_driver_tool_config = swift_toolchain_config.driver_tool_config + compile_tool_config = _swift_driver_tool_config( + driver_mode = "swiftc", + swift_executable = swift_executable, + toolchain_root = toolchain_root, + use_param_file = use_param_file, + worker_mode = "persistent", + additional_tools = additional_tools, + ) + return { swift_action_names.AUTOLINK_EXTRACT: _swift_driver_tool_config( driver_mode = "swift-autolink-extract", @@ -67,14 +78,8 @@ def _all_tool_configs( worker_mode = "wrap", additional_tools = additional_tools, ), - swift_action_names.COMPILE: _swift_driver_tool_config( - driver_mode = "swiftc", - swift_executable = swift_executable, - toolchain_root = toolchain_root, - use_param_file = use_param_file, - worker_mode = "persistent", - additional_tools = additional_tools, - ), + swift_action_names.COMPILE: compile_tool_config, + swift_action_names.DERIVE_FILES: compile_tool_config, swift_action_names.MODULEWRAP: _swift_driver_tool_config( # This must come first after the driver name. args = ["-modulewrap"], @@ -86,30 +91,35 @@ def _all_tool_configs( ), } -def _all_action_configs(): +def _all_action_configs(additional_swiftc_copts): """Returns the action configurations for the Swift toolchain. + Args: + additional_swiftc_copts: Additional Swift compiler flags obtained from + the `swift` configuration fragment. + Returns: A list of action configurations for the toolchain. """ return ( - compile_action_configs() + + compile_action_configs( + additional_swiftc_copts = additional_swiftc_copts, + ) + modulewrap_action_configs() + autolink_extract_action_configs() ) -def _default_linker_opts( +def _swift_linkopts_cc_info( cc_toolchain, cpu, os, - toolchain_root, - is_static, - is_test): - """Returns options that should be passed by default to `clang` when linking. + toolchain_label, + toolchain_root): + """Returns a `CcInfo` containing flags that should be passed to the linker. - This function is wrapped in a `partial` that will be propagated as part of - the toolchain provider. The first three arguments are pre-bound; the - `is_static` and `is_test` arguments are expected to be passed by the caller. + The provider returned by this function will be used as an implicit + dependency of the toolchain to ensure that any binary containing Swift code + will link to the standard libraries correctly. Args: cc_toolchain: The cpp toolchain from which the `ld` executable is @@ -117,18 +127,15 @@ def _default_linker_opts( cpu: The CPU architecture, which is used as part of the library path. os: The operating system name, which is used as part of the library path. + toolchain_label: The label of the Swift toolchain that will act as the + owner of the linker input propagating the flags. toolchain_root: The toolchain's root directory. - is_static: `True` to link against the static version of the Swift - runtime, or `False` to link against dynamic/shared libraries. - is_test: `True` if the target being linked is a test target. Returns: - The command line options to pass to `clang` to link against the desired - variant of the Swift runtime libraries. + A `CcInfo` provider that will provide linker flags to binaries that + depend on Swift targets. """ - _ignore = is_test - # TODO(#8): Support statically linking the Swift runtime. platform_lib_dir = "{toolchain_root}/lib/swift/{os}".format( os = os, @@ -149,32 +156,39 @@ def _default_linker_opts( "-lrt", "-ldl", runtime_object_path, + "-static-libgcc", ] - if is_static: - linkopts.append("-static-libgcc") - - return linkopts + return CcInfo( + linking_context = cc_common.create_linking_context( + linker_inputs = depset([ + cc_common.create_linker_input( + owner = toolchain_label, + user_link_flags = depset(linkopts), + ), + ]), + ), + ) def _swift_toolchain_impl(ctx): toolchain_root = ctx.attr.root cc_toolchain = find_cpp_toolchain(ctx) - linker_opts_producer = partial.make( - _default_linker_opts, + swift_linkopts_cc_info = _swift_linkopts_cc_info( cc_toolchain, ctx.attr.arch, ctx.attr.os, + ctx.label, toolchain_root, ) # Combine build mode features, autoconfigured features, and required # features. - requested_features = features_for_build_modes(ctx) - requested_features.extend([ - SWIFT_FEATURE_NO_GENERATED_HEADER, - SWIFT_FEATURE_NO_GENERATED_MODULE_MAP, - ]) + requested_features = ( + features_for_build_modes(ctx) + + features_from_swiftcopts(swiftcopts = ctx.fragments.swift.copts()) + ) + requested_features.append(SWIFT_FEATURE_NO_GENERATED_MODULE_MAP) requested_features.extend(ctx.features) # Swift.org toolchains assume everything is just available on the PATH so we @@ -191,7 +205,9 @@ def _swift_toolchain_impl(ctx): use_param_file = SWIFT_FEATURE_USE_RESPONSE_FILES in ctx.features, additional_tools = [ctx.file.version_file], ) - all_action_configs = _all_action_configs() + all_action_configs = _all_action_configs( + additional_swiftc_copts = ctx.fragments.swift.copts(), + ) # TODO(allevato): Move some of the remaining hardcoded values, like object # format and Obj-C interop support, to attributes so that we can remove the @@ -201,13 +217,24 @@ def _swift_toolchain_impl(ctx): action_configs = all_action_configs, all_files = depset(all_files), cc_toolchain_info = cc_toolchain, - command_line_copts = ctx.fragments.swift.copts(), + clang_implicit_deps_providers = ( + collect_implicit_deps_providers([]) + ), cpu = ctx.attr.arch, - linker_opts_producer = linker_opts_producer, + feature_allowlists = [ + target[SwiftFeatureAllowlistInfo] + for target in ctx.attr.feature_allowlists + ], + generated_header_module_implicit_deps_providers = ( + collect_implicit_deps_providers([]) + ), + implicit_deps_providers = collect_implicit_deps_providers( + [], + additional_cc_infos = [swift_linkopts_cc_info], + ), + linker_supports_filelist = False, object_format = "elf", - optional_implicit_deps = [], requested_features = requested_features, - required_implicit_deps = [], root_dir = toolchain_root, supports_objc_interop = False, swift_worker = ctx.executable._worker, @@ -236,6 +263,13 @@ architecture-specific content, such as "x86_64" in "lib/swift/linux/x86_64". """, mandatory = True, ), + "feature_allowlists": attr.label_list( + doc = """\ +A list of `swift_feature_allowlist` targets that allow or prohibit packages from +requesting or disabling features. +""", + providers = [[SwiftFeatureAllowlistInfo]], + ), "os": attr.string( doc = """\ The name of the operating system that this toolchain targets. @@ -274,5 +308,6 @@ for incremental compilation using a persistent mode. doc = "Represents a Swift compiler toolchain.", fragments = ["swift"], toolchains = ["@bazel_tools//tools/cpp:toolchain_type"], + incompatible_use_toolchain_transition = True, implementation = _swift_toolchain_impl, ) diff --git a/swift/internal/utils.bzl b/swift/internal/utils.bzl index 13b4caa89..da5c0c758 100644 --- a/swift/internal/utils.bzl +++ b/swift/internal/utils.bzl @@ -14,6 +14,7 @@ """Common utility definitions used by various BUILD rules.""" +load(":providers.bzl", "SwiftInfo") load("@bazel_skylib//lib:paths.bzl", "paths") def collect_cc_libraries( @@ -41,28 +42,66 @@ def collect_cc_libraries( """ libraries = [] - # TODO(https://github.com/bazelbuild/bazel/issues/8118): Remove once flag is - # flipped. - libraries_to_link = cc_info.linking_context.libraries_to_link - if hasattr(libraries_to_link, "to_list"): - libraries_to_link = libraries_to_link.to_list() - - for library in libraries_to_link: - if include_pic_static: - if library.pic_static_library: - libraries.append(library.pic_static_library) - elif library.static_library: + for linker_input in cc_info.linking_context.linker_inputs.to_list(): + for library in linker_input.libraries: + if include_pic_static: + if library.pic_static_library: + libraries.append(library.pic_static_library) + elif library.static_library: + libraries.append(library.static_library) + elif include_static and library.static_library: libraries.append(library.static_library) - elif include_static and library.static_library: - libraries.append(library.static_library) - if include_dynamic and library.dynamic_library: - libraries.append(library.dynamic_library) - if include_interface and library.interface_library: - libraries.append(library.interface_library) + if include_dynamic and library.dynamic_library: + libraries.append(library.dynamic_library) + if include_interface and library.interface_library: + libraries.append(library.interface_library) return libraries +def collect_implicit_deps_providers( + targets, + additional_cc_infos = [], + additional_objc_infos = []): + """Returns a struct with important providers from a list of implicit deps. + + Note that the relationship between each provider in the list and the target + it originated from is no longer retained. + + Args: + targets: A list (possibly empty) of `Target`s. + additional_cc_infos: A `list` of additional `CcInfo` providers that + should be included in the returned value. + additional_objc_infos: A `list` of additional `apple_common.Objc` + providers that should be included in the returned value. + + Returns: + A `struct` containing three fields: + + * `cc_infos`: The merged `CcInfo` provider from the given targets. + * `objc_infos`: The merged `apple_common.Objc` provider from the given + targets. + * `swift_infos`: The merged `SwiftInfo` provider from the given + targets. + """ + cc_infos = [] + objc_infos = [] + swift_infos = [] + + for target in targets: + if CcInfo in target: + cc_infos.append(target[CcInfo]) + if apple_common.Objc in target: + objc_infos.append(target[apple_common.Objc]) + if SwiftInfo in target: + swift_infos.append(target[SwiftInfo]) + + return struct( + cc_infos = cc_infos + additional_cc_infos, + objc_infos = objc_infos + additional_objc_infos, + swift_infos = swift_infos, + ) + def compact(sequence): """Returns a copy of the sequence with any `None` items removed. @@ -74,19 +113,16 @@ def compact(sequence): return [item for item in sequence if item != None] def create_cc_info( - additional_inputs = [], + *, cc_infos = [], compilation_outputs = None, defines = [], includes = [], - libraries_to_link = [], - private_cc_infos = [], - user_link_flags = []): + linker_inputs = [], + private_cc_infos = []): """Creates a `CcInfo` provider from Swift compilation info and deps. Args: - additional_inputs: A list of additional files that should be passed as - inputs to the final link action. cc_infos: A list of `CcInfo` providers from public dependencies, whose compilation and linking contexts should both be merged into the new provider. @@ -96,32 +132,25 @@ def create_cc_info( context. includes: The list of include paths to insert into the compilation context. - libraries_to_link: A list of `LibraryToLink` objects that represent the - libraries that should be linked into the final binary. + linker_inputs: A list of `LinkerInput` objects that represent the + libraries that should be linked into the final binary as well as any + additional inputs and flags that should be passed to the linker. private_cc_infos: A list of `CcInfo` providers from private (implementation-only) dependencies, whose linking contexts should be merged into the new provider but whose compilation contexts should be excluded. - user_link_flags: A list of flags that should be passed to the final link - action. Returns: A new `CcInfo`. """ - all_additional_inputs = list(additional_inputs) - all_user_link_flags = list(user_link_flags) all_headers = [] if compilation_outputs: - all_additional_inputs.extend(compilation_outputs.linker_inputs) - all_user_link_flags.extend(compilation_outputs.linker_flags) all_headers = compact([compilation_outputs.generated_header]) local_cc_infos = [ CcInfo( linking_context = cc_common.create_linking_context( - additional_inputs = all_additional_inputs, - libraries_to_link = libraries_to_link, - user_link_flags = all_user_link_flags, + linker_inputs = depset(linker_inputs), ), compilation_context = cc_common.create_compilation_context( defines = depset(defines), @@ -141,7 +170,55 @@ def create_cc_info( linking_context = full_private_cc_info.linking_context, )) - return cc_common.merge_cc_infos(cc_infos = local_cc_infos + cc_infos) + return cc_common.merge_cc_infos( + cc_infos = cc_infos, + direct_cc_infos = local_cc_infos, + ) + +def compilation_context_for_explicit_module_compilation( + compilation_contexts, + deps): + """Returns a compilation context suitable for compiling an explicit module. + + Args: + compilation_contexts: `CcCompilationContext`s that provide information + about headers and include paths for the target being compiled. + deps: Direct dependencies of the target being compiled. + + Returns: + A `CcCompilationContext` containing information needed when compiling an + explicit module, such as the headers and search paths of direct + dependencies (since Clang needs to find those on the file system in + order to map them to a module). + """ + cc_infos = [ + CcInfo(compilation_context = compilation_context) + for compilation_context in compilation_contexts + ] + + for dep in deps: + if CcInfo in dep: + # TODO(b/179692096): We only need to propagate the information from + # the compilation contexts, but we can't merge those directly; we + # can only merge `CcInfo` objects. So we "unwrap" the compilation + # context from each provider and then "rewrap" it in a new provider + # that lacks the linking context so that our merge operation does + # less work. + cc_infos.append( + CcInfo(compilation_context = dep[CcInfo].compilation_context), + ) + if apple_common.Objc in dep: + cc_infos.append( + CcInfo( + compilation_context = cc_common.create_compilation_context( + includes = dep[apple_common.Objc].strict_include, + ), + ), + ) + + return cc_common.merge_cc_infos( + direct_cc_infos = cc_infos, + ).compilation_context def expand_locations(ctx, values, targets = []): """Expands the `$(location)` placeholders in each of the given values. @@ -157,6 +234,23 @@ def expand_locations(ctx, values, targets = []): """ return [ctx.expand_location(value, targets) for value in values] +def expand_make_variables(ctx, values, attribute_name): + """Expands all references to Make variables in each of the given values. + + Args: + ctx: The rule context. + values: A list of strings, which may contain Make variable placeholders. + attribute_name: The attribute name string that will be presented in + console when an error occurs. + + Returns: + A list of strings with Make variables placeholders filled in. + """ + return [ + ctx.expand_make_variables(attribute_name, value, {}) + for value in values + ] + def get_swift_executable_for_toolchain(ctx): """Returns the Swift driver executable that the toolchain should use. @@ -330,36 +424,3 @@ def struct_fields(s): # TODO(b/36412967): Remove the `to_json` and `to_proto` checks. if field not in ("to_json", "to_proto") } - -def _workspace_relative_path(file): - """Returns the path of a file relative to its workspace. - - Args: - file: The `File` object. - - Returns: - The path of the file relative to its workspace. - """ - workspace_path = paths.join(file.root.path, file.owner.workspace_root) - return paths.relativize(file.path, workspace_path) - -def proto_import_path(f, proto_source_root): - """ Returns the import path of a `.proto` file given its path. - - Args: - f: The `File` object representing the `.proto` file. - proto_source_root: The source root for the `.proto` file. - - Returns: - The path the `.proto` file should be imported at. - """ - - # The simple repo case seems to say this branch is never happening as the - # proto_source_root seems to always be ".". So the general logic here likely - # needs a revisit. - if f.path.startswith(proto_source_root): - return f.path[len(proto_source_root) + 1:] - else: - # Happens before Bazel 1.0, where proto_source_root was not - # guaranteed to be a parent of the .proto file - return _workspace_relative_path(f) diff --git a/swift/internal/xcode_swift_toolchain.bzl b/swift/internal/xcode_swift_toolchain.bzl index 81c8f24a6..7573e0334 100644 --- a/swift/internal/xcode_swift_toolchain.bzl +++ b/swift/internal/xcode_swift_toolchain.bzl @@ -19,31 +19,45 @@ toolchain package. If you are looking for rules to build Swift code using this toolchain, see `swift.bzl`. """ -load("@bazel_skylib//lib:collections.bzl", "collections") load("@bazel_skylib//lib:dicts.bzl", "dicts") load("@bazel_skylib//lib:partial.bzl", "partial") load("@bazel_skylib//lib:paths.bzl", "paths") load("@bazel_tools//tools/cpp:toolchain_utils.bzl", "find_cpp_toolchain") load(":actions.bzl", "swift_action_names") load(":attrs.bzl", "swift_toolchain_driver_attrs") -load(":compiling.bzl", "compile_action_configs") +load(":compiling.bzl", "compile_action_configs", "features_from_swiftcopts") load( ":feature_names.bzl", "SWIFT_FEATURE_BITCODE_EMBEDDED", "SWIFT_FEATURE_BITCODE_EMBEDDED_MARKERS", "SWIFT_FEATURE_BUNDLED_XCTESTS", + "SWIFT_FEATURE_COVERAGE", + "SWIFT_FEATURE_COVERAGE_PREFIX_MAP", "SWIFT_FEATURE_DEBUG_PREFIX_MAP", "SWIFT_FEATURE_ENABLE_BATCH_MODE", + "SWIFT_FEATURE_ENABLE_SKIP_FUNCTION_BODIES", "SWIFT_FEATURE_MODULE_MAP_HOME_IS_CWD", "SWIFT_FEATURE_MODULE_MAP_NO_PRIVATE_HEADERS", + "SWIFT_FEATURE_REMAP_XCODE_PATH", "SWIFT_FEATURE_SUPPORTS_LIBRARY_EVOLUTION", "SWIFT_FEATURE_SUPPORTS_PRIVATE_DEPS", + "SWIFT_FEATURE_SUPPORTS_SYSTEM_MODULE_FLAG", "SWIFT_FEATURE_USE_RESPONSE_FILES", ) load(":features.bzl", "features_for_build_modes") load(":toolchain_config.bzl", "swift_toolchain_config") -load(":providers.bzl", "SwiftToolchainInfo") -load(":utils.bzl", "get_swift_executable_for_toolchain") +load( + ":providers.bzl", + "SwiftFeatureAllowlistInfo", + "SwiftInfo", + "SwiftToolchainInfo", +) +load( + ":utils.bzl", + "collect_implicit_deps_providers", + "compact", + "get_swift_executable_for_toolchain", +) def _swift_developer_lib_dir(platform_framework_dir): """Returns the directory containing extra Swift developer libraries. @@ -105,111 +119,150 @@ def _command_line_objc_copts(compilation_mode, objc_fragment): ] clang_copts = objc_fragment.copts + legacy_copts + return [copt for copt in clang_copts if copt != "-g"] - return collections.before_each( - "-Xcc", - [copt for copt in clang_copts if copt != "-g"], - ) +def _platform_developer_framework_dir( + apple_toolchain, + apple_fragment, + xcode_config): + """Returns the Developer framework directory for the platform. + + Args: + apple_fragment: The `apple` configuration fragment. + apple_toolchain: The `apple_common.apple_toolchain()` object. + xcode_config: The Xcode configuration. + + Returns: + The path to the Developer framework directory for the platform if one + exists, otherwise `None`. + """ + + # All platforms have a `Developer/Library/Frameworks` directory in their + # platform root, except for watchOS prior to Xcode 12.5. + platform_type = apple_fragment.single_arch_platform.platform_type + if ( + platform_type == apple_common.platform_type.watchos and + not _is_xcode_at_least_version(xcode_config, "12.5") + ): + return None + + return apple_toolchain.platform_developer_framework_dir(apple_fragment) + +def _sdk_developer_framework_dir(apple_toolchain, apple_fragment, xcode_config): + """Returns the Developer framework directory for the SDK. + + Args: + apple_fragment: The `apple` configuration fragment. + apple_toolchain: The `apple_common.apple_toolchain()` object. + xcode_config: The Xcode configuration. + + Returns: + The path to the Developer framework directory for the SDK if one + exists, otherwise `None`. + """ + + # All platforms have a `Developer/Library/Frameworks` directory in their SDK + # root except for macOS (all versions of Xcode so far), and watchOS (prior + # to Xcode 12.5). + platform_type = apple_fragment.single_arch_platform.platform_type + if ( + platform_type == apple_common.platform_type.macos or + ( + platform_type == apple_common.platform_type.watchos and + not _is_xcode_at_least_version(xcode_config, "12.5") + ) + ): + return None + + return paths.join(apple_toolchain.sdk_dir(), "Developer/Library/Frameworks") -def _default_linker_opts( +def _swift_linkopts_providers( apple_fragment, apple_toolchain, platform, target, - xcode_config, - is_static, - is_test): - """Returns options that should be passed by default to `clang` when linking. + toolchain_label, + xcode_config): + """Returns providers containing flags that should be passed to the linker. - This function is wrapped in a `partial` that will be propagated as part of - the toolchain provider. The first five arguments are pre-bound; the - `is_static` and `is_test` arguments are expected to be passed by the caller. + The providers returned by this function will be used as implicit + dependencies of the toolchain to ensure that any binary containing Swift code + will link to the standard libraries correctly. Args: apple_fragment: The `apple` configuration fragment. apple_toolchain: The `apple_common.apple_toolchain()` object. platform: The `apple_platform` value describing the target platform. target: The target triple. + toolchain_label: The label of the Swift toolchain that will act as the + owner of the linker input propagating the flags. xcode_config: The Xcode configuration. - is_static: `True` to link against the static version of the Swift - runtime, or `False` to link against dynamic/shared libraries. - is_test: `True` if the target being linked is a test target. Returns: - The command line options to pass to `clang` to link against the desired - variant of the Swift runtime libraries. + A `struct` containing the following fields: + + * `cc_info`: A `CcInfo` provider that will provide linker flags to + binaries that depend on Swift targets. + * `objc_info`: An `apple_common.Objc` provider that will provide + linker flags to binaries that depend on Swift targets. """ - platform_framework_dir = apple_toolchain.platform_developer_framework_dir( + platform_developer_framework_dir = _platform_developer_framework_dir( + apple_toolchain, apple_fragment, + xcode_config, ) - linkopts = [] - - uses_runtime_in_os = _is_xcode_at_least_version(xcode_config, "10.2") - if uses_runtime_in_os: - # Starting with Xcode 10.2, Apple forbids statically linking to the - # Swift runtime. The libraries are distributed with the OS and located - # in /usr/lib/swift. - swift_subdir = "swift" - linkopts.append("-Wl,-rpath,/usr/lib/swift") - elif is_static: - # This branch and the branch below now only support Xcode 10.1 and - # below. Eventually, once we drop support for those versions, they can - # be deleted. - swift_subdir = "swift_static" - linkopts.extend([ - "-Wl,-force_load_swift_libs", - "-framework", - "Foundation", - "-lstdc++", - ]) - else: - swift_subdir = "swift" - - swift_lib_dir = ( - "{developer_dir}/Toolchains/{toolchain}.xctoolchain/" + - "usr/lib/{swift_subdir}/{platform}" - ).format( - developer_dir = apple_toolchain.developer_dir(), - platform = platform.name_in_plist.lower(), - swift_subdir = swift_subdir, - toolchain = "XcodeDefault", + sdk_developer_framework_dir = _sdk_developer_framework_dir( + apple_toolchain, + apple_fragment, + xcode_config, + ) + swift_lib_dir = paths.join( + apple_toolchain.developer_dir(), + "Toolchains/XcodeDefault.xctoolchain/usr/lib/swift", + platform.name_in_plist.lower(), ) - # TODO(b/128303533): It's possible to run Xcode 10.2 on a version of macOS - # 10.14.x that does not yet include `/usr/lib/swift`. Later Xcode 10.2 betas - # have deleted the `swift_static` directory, so we must manually add the - # dylibs to the binary's rpath or those binaries won't be able to run at - # all. This is added after `/usr/lib/swift` above so the system versions - # will always be preferred if they are present. This workaround can be - # removed once Xcode 10.2 and macOS 10.14.4 are out of beta. - if uses_runtime_in_os and platform == apple_common.platform.macos: - linkopts.append("-Wl,-rpath,{}".format(swift_lib_dir)) - - linkopts.extend([ - "-F{}".format(platform_framework_dir), + linkopts = [ + "-F{}".format(path) + for path in compact([ + platform_developer_framework_dir, + sdk_developer_framework_dir, + ]) + ] + [ + "-Wl,-rpath,/usr/lib/swift", "-L{}".format(swift_lib_dir), - # TODO(b/112000244): These should get added by the C++ Starlark API, but - # we're using the "c++-link-executable" action right now instead of - # "objc-executable" because the latter requires additional variables not - # provided by cc_common. Figure out how to handle this correctly. + "-L/usr/lib/swift", + # TODO(b/112000244): These should get added by the C++ Starlark API, + # but we're using the "c++-link-executable" action right now instead + # of "objc-executable" because the latter requires additional + # variables not provided by cc_common. Figure out how to handle this + # correctly. "-ObjC", "-Wl,-objc_abi_version,2", - ]) - - use_system_swift_libs = _is_xcode_at_least_version(xcode_config, "11.0") - if use_system_swift_libs: - linkopts.append("-L/usr/lib/swift") + ] - # XCTest.framework only lives in the Xcode bundle (its platform framework - # directory), so test binaries need to have that directory explicitly added - # to their rpaths. - if is_test: + # Add the linker path to the directory containing the dylib with Swift + # extensions for the XCTest module. + if platform_developer_framework_dir: linkopts.extend([ - "-Wl,-rpath,{}".format(platform_framework_dir), - "-L{}".format(_swift_developer_lib_dir(platform_framework_dir)), + "-L{}".format( + _swift_developer_lib_dir(platform_developer_framework_dir), + ), ]) - return linkopts + return struct( + cc_info = CcInfo( + linking_context = cc_common.create_linking_context( + linker_inputs = depset([ + cc_common.create_linker_input( + owner = toolchain_label, + user_link_flags = depset(linkopts), + ), + ]), + ), + ), + objc_info = apple_common.new_objc_provider(linkopt = depset(linkopts)), + ) def _features_for_bitcode_mode(bitcode_mode): """Gets the list of features to enable for the selected Bitcode mode. @@ -260,60 +313,161 @@ def _resource_directory_configurator(developer_dir, prerequisites, args): ) def _all_action_configs( + additional_objc_copts, + additional_swiftc_copts, apple_fragment, apple_toolchain, + generated_header_rewriter, needs_resource_directory, - target_triple): + target_triple, + xcode_config): """Returns the action configurations for the Swift toolchain. Args: + additional_objc_copts: Additional Objective-C compiler flags obtained + from the `objc` configuration fragment (and legacy flags that were + previously passed directly by Bazel). + additional_swiftc_copts: Additional Swift compiler flags obtained from + the `swift` configuration fragment. apple_fragment: The `apple` configuration fragment. apple_toolchain: The `apple_common.apple_toolchain()` object. + generated_header_rewriter: An executable that will be invoked after + compilation to rewrite the generated header, or None if this is not + desired. needs_resource_directory: If True, the toolchain needs the resource directory passed explicitly to the compiler. target_triple: The target triple. + xcode_config: The Xcode configuration. Returns: The action configurations for the Swift toolchain. """ - developer_dir = apple_toolchain.developer_dir() - platform_framework_dir = ( - apple_toolchain.platform_developer_framework_dir(apple_fragment) + platform_developer_framework_dir = _platform_developer_framework_dir( + apple_toolchain, + apple_fragment, + xcode_config, + ) + sdk_developer_framework_dir = _sdk_developer_framework_dir( + apple_toolchain, + apple_fragment, + xcode_config, ) - sdk_dir = apple_toolchain.sdk_dir() + developer_framework_dirs = compact([ + platform_developer_framework_dir, + sdk_developer_framework_dir, + ]) + # Basic compilation flags (target triple and toolchain search paths). action_configs = [ - # Basic compilation flags (target triple and toolchain search paths). swift_toolchain_config.action_config( - actions = [swift_action_names.COMPILE], + actions = [ + swift_action_names.COMPILE, + swift_action_names.DERIVE_FILES, + swift_action_names.PRECOMPILE_C_MODULE, + ], configurators = [ swift_toolchain_config.add_arg("-target", target_triple), - swift_toolchain_config.add_arg("-sdk", sdk_dir), swift_toolchain_config.add_arg( - platform_framework_dir, - format = "-F%s", + "-sdk", + apple_toolchain.sdk_dir(), ), + ] + [ + swift_toolchain_config.add_arg(framework_dir, format = "-F%s") + for framework_dir in developer_framework_dirs + ], + ), + swift_toolchain_config.action_config( + actions = [swift_action_names.PRECOMPILE_C_MODULE], + configurators = [ swift_toolchain_config.add_arg( - _swift_developer_lib_dir(platform_framework_dir), - format = "-I%s", - ), + "-Xcc", + framework_dir, + format = "-F%s", + ) + for framework_dir in developer_framework_dirs ], ), + ] + # The platform developer framework directory contains XCTest.swiftmodule + # with Swift extensions to XCTest, so it needs to be added to the search + # path on platforms where it exists. + if platform_developer_framework_dir: + action_configs.append( + swift_toolchain_config.action_config( + actions = [ + swift_action_names.COMPILE, + swift_action_names.DERIVE_FILES, + swift_action_names.PRECOMPILE_C_MODULE, + ], + configurators = [ + swift_toolchain_config.add_arg( + _swift_developer_lib_dir( + platform_developer_framework_dir, + ), + format = "-I%s", + ), + ], + ), + ) + + action_configs.extend([ # Bitcode-related flags. swift_toolchain_config.action_config( - actions = [swift_action_names.COMPILE], + actions = [ + swift_action_names.COMPILE, + swift_action_names.PRECOMPILE_C_MODULE, + ], configurators = [swift_toolchain_config.add_arg("-embed-bitcode")], features = [SWIFT_FEATURE_BITCODE_EMBEDDED], ), swift_toolchain_config.action_config( - actions = [swift_action_names.COMPILE], + actions = [ + swift_action_names.COMPILE, + swift_action_names.PRECOMPILE_C_MODULE, + ], configurators = [ swift_toolchain_config.add_arg("-embed-bitcode-marker"), ], features = [SWIFT_FEATURE_BITCODE_EMBEDDED_MARKERS], ), - ] + + # Xcode path remapping + swift_toolchain_config.action_config( + actions = [ + swift_action_names.COMPILE, + swift_action_names.DERIVE_FILES, + ], + configurators = [ + swift_toolchain_config.add_arg( + "-debug-prefix-map", + "__BAZEL_XCODE_DEVELOPER_DIR__=DEVELOPER_DIR", + ), + ], + features = [ + [SWIFT_FEATURE_REMAP_XCODE_PATH, SWIFT_FEATURE_DEBUG_PREFIX_MAP], + ], + ), + swift_toolchain_config.action_config( + actions = [ + swift_action_names.COMPILE, + swift_action_names.DERIVE_FILES, + ], + configurators = [ + swift_toolchain_config.add_arg( + "-coverage-prefix-map", + "__BAZEL_XCODE_DEVELOPER_DIR__=DEVELOPER_DIR", + ), + ], + features = [ + [ + SWIFT_FEATURE_REMAP_XCODE_PATH, + SWIFT_FEATURE_COVERAGE_PREFIX_MAP, + SWIFT_FEATURE_COVERAGE, + ], + ], + ), + ]) if needs_resource_directory: # If the user is using a custom driver but not a complete custom @@ -321,26 +475,35 @@ def _all_action_configs( # directory so that modules are found correctly. action_configs.append( swift_toolchain_config.action_config( - actions = [swift_action_names.COMPILE], + actions = [ + swift_action_names.COMPILE, + swift_action_names.DERIVE_FILES, + swift_action_names.PRECOMPILE_C_MODULE, + ], configurators = [ partial.make( _resource_directory_configurator, - developer_dir, + apple_toolchain.developer_dir(), ), ], ), ) - action_configs.extend(compile_action_configs()) + action_configs.extend(compile_action_configs( + additional_objc_copts = additional_objc_copts, + additional_swiftc_copts = additional_swiftc_copts, + generated_header_rewriter = generated_header_rewriter, + )) return action_configs def _all_tool_configs( custom_toolchain, env, execution_requirements, + generated_header_rewriter, swift_executable, toolchain_root, - use_param_file): + xcode_config): """Returns the tool configurations for the Swift toolchain. Args: @@ -348,11 +511,13 @@ def _all_tool_configs( one was requested. env: The environment variables to set when launching tools. execution_requirements: The execution requirements for tools. + generated_header_rewriter: An executable that will be invoked after + compilation to rewrite the generated header, or None if this is not + desired. swift_executable: A custom Swift driver executable to be used during the build, if provided. toolchain_root: The root directory of the toolchain, if provided. - use_param_file: If True, actions should have their arguments written to - param files. + xcode_config: The `apple_common.XcodeVersionConfig` provider. Returns: A dictionary mapping action name to tool configuration. @@ -365,35 +530,47 @@ def _all_tool_configs( env = dict(env) env["TOOLCHAINS"] = custom_toolchain - return { - swift_action_names.COMPILE: swift_toolchain_config.driver_tool_config( - driver_mode = "swiftc", - env = env, - execution_requirements = execution_requirements, - swift_executable = swift_executable, - toolchain_root = toolchain_root, - use_param_file = use_param_file, - worker_mode = "persistent", - ), - } + additional_compile_tools = [] + if generated_header_rewriter: + additional_compile_tools.append(generated_header_rewriter) -def _is_macos(platform): - """Returns `True` if the given platform is macOS. + tool_config = swift_toolchain_config.driver_tool_config( + additional_tools = additional_compile_tools, + driver_mode = "swiftc", + env = env, + execution_requirements = execution_requirements, + swift_executable = swift_executable, + toolchain_root = toolchain_root, + use_param_file = True, + worker_mode = "persistent", + ) - Args: - platform: An `apple_platform` value describing the platform for which a - target is being built. + tool_configs = { + swift_action_names.COMPILE: tool_config, + swift_action_names.DERIVE_FILES: tool_config, + } - Returns: - `True` if the given platform is macOS. - """ - return platform.platform_type == apple_common.platform_type.macos + # Xcode 12.0 implies Swift 5.3. + if _is_xcode_at_least_version(xcode_config, "12.0"): + tool_configs[swift_action_names.PRECOMPILE_C_MODULE] = ( + swift_toolchain_config.driver_tool_config( + driver_mode = "swiftc", + env = env, + execution_requirements = execution_requirements, + swift_executable = swift_executable, + toolchain_root = toolchain_root, + use_param_file = True, + worker_mode = "wrap", + ) + ) + + return tool_configs def _is_xcode_at_least_version(xcode_config, desired_version): """Returns True if we are building with at least the given Xcode version. Args: - xcode_config: the `apple_common.XcodeVersionConfig` provider. + xcode_config: The `apple_common.XcodeVersionConfig` provider. desired_version: The minimum desired Xcode version, as a dotted version string. @@ -454,31 +631,6 @@ def _xcode_env(xcode_config, platform): apple_common.target_apple_env(xcode_config, platform), ) -def _xcode_execution_requirements(xcode_config): - """Returns execution requirements for actions involving Xcode. - - Args: - xcode_config: The Xcode configuration object. - - Returns: - A dictionary of execution requirements to be passed when registering - actions. - """ - - # All Swift actions should be executed on Darwin, even if Bazel is running - # on a non-Darwin host. - # TODO(steinman): Replace this with xcode_config.execution_info once it is - # available. - execution_requirements = {"requires-darwin": ""} - if xcode_config: - if xcode_config.availability() == "remote": - execution_requirements["no-local"] = "1" - elif xcode_config.availability() == "local": - execution_requirements["no-remote"] = "1" - execution_requirements["supports-xcode-requirements-set"] = "1" - - return execution_requirements - def _xcode_swift_toolchain_impl(ctx): apple_fragment = ctx.fragments.apple apple_toolchain = apple_common.apple_toolchain() @@ -493,12 +645,12 @@ def _xcode_swift_toolchain_impl(ctx): ) target = _swift_apple_target_triple(cpu, platform, target_os_version) - linker_opts_producer = partial.make( - _default_linker_opts, + swift_linkopts_providers = _swift_linkopts_providers( apple_fragment, apple_toolchain, platform, target, + ctx.label, xcode_config, ) @@ -526,57 +678,62 @@ def _xcode_swift_toolchain_impl(ctx): requested_features = features_for_build_modes( ctx, objc_fragment = ctx.fragments.objc, - ) + cpp_fragment = ctx.fragments.cpp, + ) + features_from_swiftcopts(swiftcopts = ctx.fragments.swift.copts()) requested_features.extend(ctx.features) - requested_features.append(SWIFT_FEATURE_BUNDLED_XCTESTS) requested_features.extend( _features_for_bitcode_mode(apple_fragment.bitcode_mode), ) - - # TODO(b/142867898): Added to match existing Bazel Objective-C module map - # behavior; remove it when possible. - requested_features.append(SWIFT_FEATURE_MODULE_MAP_NO_PRIVATE_HEADERS) - - # Xcode 10.0 implies Swift 4.2. - if _is_xcode_at_least_version(xcode_config, "10.0"): - use_param_file = True - requested_features.append(SWIFT_FEATURE_ENABLE_BATCH_MODE) - requested_features.append(SWIFT_FEATURE_USE_RESPONSE_FILES) - else: - use_param_file = False - - # Xcode 10.2 implies Swift 5.0. - if _is_xcode_at_least_version(xcode_config, "10.2"): - requested_features.append(SWIFT_FEATURE_DEBUG_PREFIX_MAP) + requested_features.extend([ + SWIFT_FEATURE_BUNDLED_XCTESTS, + SWIFT_FEATURE_ENABLE_BATCH_MODE, + SWIFT_FEATURE_USE_RESPONSE_FILES, + SWIFT_FEATURE_DEBUG_PREFIX_MAP, + SWIFT_FEATURE_SUPPORTS_LIBRARY_EVOLUTION, + SWIFT_FEATURE_SUPPORTS_PRIVATE_DEPS, + # TODO(b/142867898): Added to match existing Bazel Objective-C module + # map behavior; remove it when possible. + SWIFT_FEATURE_MODULE_MAP_NO_PRIVATE_HEADERS, + ]) # Xcode 11.0 implies Swift 5.1. if _is_xcode_at_least_version(xcode_config, "11.0"): requested_features.append(SWIFT_FEATURE_SUPPORTS_LIBRARY_EVOLUTION) requested_features.append(SWIFT_FEATURE_SUPPORTS_PRIVATE_DEPS) - command_line_copts = _command_line_objc_copts( - ctx.var["COMPILATION_MODE"], - ctx.fragments.objc, - ) + ctx.fragments.swift.copts() + # Xcode 11.4 implies Swift 5.2. + if _is_xcode_at_least_version(xcode_config, "11.4"): + requested_features.append(SWIFT_FEATURE_ENABLE_SKIP_FUNCTION_BODIES) + + # Xcode 12.5 implies Swift 5.4. + if _is_xcode_at_least_version(xcode_config, "12.5"): + requested_features.append(SWIFT_FEATURE_SUPPORTS_SYSTEM_MODULE_FLAG) env = _xcode_env(platform = platform, xcode_config = xcode_config) - execution_requirements = _xcode_execution_requirements( - xcode_config = xcode_config, - ) + execution_requirements = xcode_config.execution_info() + generated_header_rewriter = ctx.executable.generated_header_rewriter all_tool_configs = _all_tool_configs( custom_toolchain = custom_toolchain, env = env, execution_requirements = execution_requirements, + generated_header_rewriter = generated_header_rewriter, swift_executable = swift_executable, toolchain_root = toolchain_root, - use_param_file = use_param_file, + xcode_config = xcode_config, ) all_action_configs = _all_action_configs( + additional_objc_copts = _command_line_objc_copts( + ctx.var["COMPILATION_MODE"], + ctx.fragments.objc, + ), + additional_swiftc_copts = ctx.fragments.swift.copts(), apple_fragment = apple_fragment, apple_toolchain = apple_toolchain, + generated_header_rewriter = generated_header_rewriter, needs_resource_directory = swift_executable or toolchain_root, target_triple = target, + xcode_config = xcode_config, ) # Xcode toolchains don't pass any files explicitly here because they're @@ -591,13 +748,27 @@ def _xcode_swift_toolchain_impl(ctx): action_configs = all_action_configs, all_files = depset(all_files), cc_toolchain_info = cc_toolchain, - command_line_copts = command_line_copts, + clang_implicit_deps_providers = collect_implicit_deps_providers( + ctx.attr.clang_implicit_deps, + ), cpu = cpu, - linker_opts_producer = linker_opts_producer, + feature_allowlists = [ + target[SwiftFeatureAllowlistInfo] + for target in ctx.attr.feature_allowlists + ], + generated_header_module_implicit_deps_providers = ( + collect_implicit_deps_providers( + ctx.attr.generated_header_module_implicit_deps, + ) + ), + implicit_deps_providers = collect_implicit_deps_providers( + ctx.attr.implicit_deps, + additional_cc_infos = [swift_linkopts_providers.cc_info], + additional_objc_infos = [swift_linkopts_providers.objc_info], + ), + linker_supports_filelist = True, object_format = "macho", - optional_implicit_deps = [], requested_features = requested_features, - required_implicit_deps = [], supports_objc_interop = True, swift_worker = ctx.executable._worker, system_name = "darwin", @@ -616,6 +787,62 @@ xcode_swift_toolchain = rule( attrs = dicts.add( swift_toolchain_driver_attrs(), { + "clang_implicit_deps": attr.label_list( + doc = """\ +A list of labels to library targets that should be unconditionally added as +implicit dependencies of any explicit C/Objective-C module compiled by the Swift +toolchain. + +Despite being C/Objective-C modules, the targets specified by this attribute +must propagate the `SwiftInfo` provider because the Swift build rules use that +provider to look up Clang module requirements. In particular, the targets must +propagate the provider in their rule implementation themselves and not rely on +the implicit traversal performed by `swift_clang_module_aspect`; the latter is +not possible as it would create a dependency cycle between the toolchain and the +implicit dependencies. +""", + providers = [[SwiftInfo]], + ), + "feature_allowlists": attr.label_list( + doc = """\ +A list of `swift_feature_allowlist` targets that allow or prohibit packages from +requesting or disabling features. +""", + providers = [[SwiftFeatureAllowlistInfo]], + ), + "generated_header_module_implicit_deps": attr.label_list( + doc = """\ +Targets whose `SwiftInfo` providers should be treated as compile-time inputs to +actions that precompile the explicit module for the generated Objective-C header +of a Swift module. +""", + providers = [[SwiftInfo]], + ), + "generated_header_rewriter": attr.label( + allow_files = True, + cfg = "host", + doc = """\ +If present, an executable that will be invoked after compilation to rewrite the +generated header. + +This tool is expected to have a command line interface such that the Swift +compiler invocation is passed to it following a `"--"` argument, and any +arguments preceding the `"--"` can be defined by the tool itself (however, at +this time the worker does not support passing additional flags to the tool). +""", + executable = True, + ), + "implicit_deps": attr.label_list( + allow_files = True, + doc = """\ +A list of labels to library targets that should be unconditionally added as +implicit dependencies of any Swift compilation or linking target. +""", + providers = [ + [CcInfo], + [SwiftInfo], + ], + ), "_cc_toolchain": attr.label( default = Label("@bazel_tools//tools/cpp:current_cc_toolchain"), doc = """\ @@ -646,9 +873,11 @@ for incremental compilation using a persistent mode. doc = "Represents a Swift compiler toolchain provided by Xcode.", fragments = [ "apple", + "cpp", "objc", "swift", ], toolchains = ["@bazel_tools//tools/cpp:toolchain_type"], + incompatible_use_toolchain_transition = True, implementation = _xcode_swift_toolchain_impl, ) diff --git a/swift/repositories.bzl b/swift/repositories.bzl index b7e1ae40b..bbc01d868 100644 --- a/swift/repositories.bzl +++ b/swift/repositories.bzl @@ -42,18 +42,18 @@ def swift_rules_dependencies(): http_archive, name = "bazel_skylib", urls = [ - "https://github.com/bazelbuild/bazel-skylib/releases/download/1.0.2/bazel-skylib-1.0.2.tar.gz", + "https://github.com/bazelbuild/bazel-skylib/releases/download/1.0.3/bazel-skylib-1.0.3.tar.gz", ], - sha256 = "97e70364e9249702246c0e9444bccdc4b847bed1eb03c5a3ece4f83dfe6abc44", + sha256 = "1c531376ac7e5a180e0237938a2536de0c54d93f5c278634818e0efc952dd56c", ) _maybe( http_archive, name = "build_bazel_apple_support", urls = [ - "https://github.com/bazelbuild/apple_support/releases/download/0.7.2/apple_support.0.7.2.tar.gz", + "https://github.com/bazelbuild/apple_support/releases/download/0.11.0/apple_support.0.11.0.tar.gz", ], - sha256 = "9114c452eee622598cf9cdc90ecb12b06af7f914f33440b26deba9a9704d450c", + sha256 = "76df040ade90836ff5543888d64616e7ba6c3a7b33b916aa3a4b68f342d1b447", ) _maybe( @@ -68,9 +68,9 @@ def swift_rules_dependencies(): _maybe( http_archive, name = "com_github_apple_swift_protobuf", - urls = ["https://github.com/apple/swift-protobuf/archive/1.7.0.zip"], - sha256 = "a4546ee8e95e7f7d4cf46b5b667e824b58f3943a71c352bf1e0b91660afdf3c3", - strip_prefix = "swift-protobuf-1.7.0/", + urls = ["https://github.com/apple/swift-protobuf/archive/1.12.0.zip"], + sha256 = "a9c1c14d81df690ed4c15bfb3c0aab0cb7a3f198ee95620561b89b1da7b76a9f", + strip_prefix = "swift-protobuf-1.12.0/", type = "zip", build_file = "@build_bazel_rules_swift//third_party:com_github_apple_swift_protobuf/BUILD.overlay", ) @@ -98,16 +98,28 @@ def swift_rules_dependencies(): _maybe( http_archive, - name = "com_google_protobuf", - # v3.11.2, latest as of 2019-12-19 + name = "rules_proto", + # latest as of 2021-01-27 urls = [ - "https://github.com/protocolbuffers/protobuf/archive/v3.11.2.zip", + "https://github.com/bazelbuild/rules_proto/archive/a0761ed101b939e19d83b2da5f59034bffc19c12.zip", ], - sha256 = "e4f8bedb19a93d0dccc359a126f51158282e0b24d92e0cad9c76a9699698268d", - strip_prefix = "protobuf-3.11.2", + sha256 = "32c9deb114c9e2d6ea3afd48a4d203d775b60a01876186d1ad52d752a8be439f", + strip_prefix = "rules_proto-a0761ed101b939e19d83b2da5f59034bffc19c12", type = "zip", ) + # It relies on `index-import` to import indexes into Bazel's remote + # cache and allow using a global index internally in workers. + # Note: this is only loaded if swift.index_while_building_v2 is enabled + _maybe( + http_archive, + name = "build_bazel_rules_swift_index_import", + build_file = "@build_bazel_rules_swift//third_party:build_bazel_rules_swift_index_import/BUILD.overlay", + canonical_id = "index-import-5.3.2.6", + urls = ["https://github.com/MobileNativeFoundation/index-import/releases/download/5.3.2.6/index-import.zip"], + sha256 = "61a58363f56c5fd84d4ebebe0d9b5dd90c74ae170405a7b9018e8cf698e679de", + ) + _maybe( swift_autoconfiguration, name = "build_bazel_rules_swift_local_config", diff --git a/swift/stats.bzl b/swift/stats.bzl deleted file mode 100644 index 293b4b9d4..000000000 --- a/swift/stats.bzl +++ /dev/null @@ -1,61 +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. - -"""An aspect that collects compilation timing statistics.""" - -load( - "@build_bazel_rules_swift//swift/internal:utils.bzl", - "get_output_groups", -) - -def _collect_swift_compile_stats_impl(target, aspect_ctx): - output_groups = target[OutputGroupInfo] - - deps = getattr(aspect_ctx.rule.attr, "deps", []) - merged_stats = get_output_groups(deps, "swift_compile_stats") - direct_stats = getattr(output_groups, "swift_compile_stats_direct", None) - if direct_stats: - merged_stats.append(direct_stats) - - return [OutputGroupInfo( - swift_compile_stats = depset(transitive = merged_stats), - )] - -collect_swift_compile_stats = aspect( - attr_aspects = ["deps"], - doc = """ -Collects compilation statistics reports from the entire build graph. - -This aspect is intended to be used from the command line to profile the Swift -compiler during a build. It needs to be combined with the `swift.compile_stats` -feature that asks the compiler to write out the statistics and a request for the -`swift_compile_stats` output group so that the files are available at the end of -the build: - -``` -bazel build //your/swift:target \ - --features=swift.compile_stats \ - --output_groups=swift_compile_stats \ - --aspects=@build_bazel_rules_swift//swift:stats.bzl%collect_swift_compile_stats -``` - -Since this command is a bit of a mouthful, we've provided a helper script in the -tools directory that wraps this up: - -``` -.../tools/compile_stats/build.sh -``` -""", - implementation = _collect_swift_compile_stats_impl, -) diff --git a/swift/swift.bzl b/swift/swift.bzl index f965ec940..7b589c759 100644 --- a/swift/swift.bzl +++ b/swift/swift.bzl @@ -16,6 +16,15 @@ This file is the public interface that users should import to use the Swift rules. Do not import definitions from the `internal` subdirectory directly. + +To use the Swift build rules in your BUILD files, load them from +`@build_bazel_rules_swift//swift:swift.bzl`. + +For example: + +```build +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") +``` """ load( @@ -34,10 +43,18 @@ load( "@build_bazel_rules_swift//swift/internal:swift_c_module.bzl", _swift_c_module = "swift_c_module", ) +load( + "@build_bazel_rules_swift//swift/internal:swift_clang_module_aspect.bzl", + _swift_clang_module_aspect = "swift_clang_module_aspect", +) load( "@build_bazel_rules_swift//swift/internal:swift_common.bzl", _swift_common = "swift_common", ) +load( + "@build_bazel_rules_swift//swift/internal:swift_feature_allowlist.bzl", + _swift_feature_allowlist = "swift_feature_allowlist", +) load( "@build_bazel_rules_swift//swift/internal:swift_grpc_library.bzl", _swift_grpc_library = "swift_grpc_library", @@ -77,14 +94,16 @@ swift_common = _swift_common # Re-export rules. swift_binary = _swift_binary swift_c_module = _swift_c_module +swift_feature_allowlist = _swift_feature_allowlist swift_grpc_library = _swift_grpc_library swift_grpc_code = _swift_grpc_code swift_import = _swift_import swift_library = _swift_library -swift_test = _swift_test swift_module_alias = _swift_module_alias swift_proto_library = _swift_proto_library swift_proto_code = _swift_proto_code +swift_test = _swift_test # Re-export public aspects. +swift_clang_module_aspect = _swift_clang_module_aspect swift_usage_aspect = _swift_usage_aspect diff --git a/test/BUILD b/test/BUILD index 38d7e1de4..9d0783f77 100644 --- a/test/BUILD +++ b/test/BUILD @@ -1,19 +1,28 @@ load("@bazel_skylib//:bzl_library.bzl", "bzl_library") load(":debug_settings_tests.bzl", "debug_settings_test_suite") +load(":coverage_settings_tests.bzl", "coverage_settings_test_suite") load(":generated_header_tests.bzl", "generated_header_test_suite") +load(":module_cache_settings_tests.bzl", "module_cache_settings_test_suite") load(":private_deps_tests.bzl", "private_deps_test_suite") load(":swift_through_non_swift_tests.bzl", "swift_through_non_swift_test_suite") +load(":split_derived_files_tests.bzl", "split_derived_files_test_suite") licenses(["notice"]) debug_settings_test_suite() +coverage_settings_test_suite() + generated_header_test_suite() +module_cache_settings_test_suite() + private_deps_test_suite() swift_through_non_swift_test_suite() +split_derived_files_test_suite() + test_suite( name = "all_tests", ) diff --git a/test/coverage_settings_tests.bzl b/test/coverage_settings_tests.bzl new file mode 100644 index 000000000..b14e58234 --- /dev/null +++ b/test/coverage_settings_tests.bzl @@ -0,0 +1,75 @@ +"""Tests for coverage-related command line flags under various configs.""" + +load( + "@build_bazel_rules_swift//test/rules:action_command_line_test.bzl", + "make_action_command_line_test_rule", +) + +default_coverage_test = make_action_command_line_test_rule( + config_settings = { + "//command_line_option:collect_code_coverage": "true", + }, +) + +coverage_prefix_map_test = make_action_command_line_test_rule( + config_settings = { + "//command_line_option:collect_code_coverage": "true", + "//command_line_option:features": [ + "swift.coverage_prefix_map", + ], + }, +) + +coverage_xcode_prefix_map_test = make_action_command_line_test_rule( + config_settings = { + "//command_line_option:collect_code_coverage": "true", + "//command_line_option:features": [ + "swift.coverage_prefix_map", + "swift.remap_xcode_path", + ], + }, +) + +def coverage_settings_test_suite(name = "coverage_settings"): + """Test suite for coverage options. + + Args: + name: The name prefix for all the nested tests + """ + default_coverage_test( + name = "{}_default_coverage".format(name), + tags = [name], + expected_argv = [ + "-profile-generate", + "-profile-coverage-mapping", + ], + not_expected_argv = [ + "-Xwrapped-swift=-coverage-prefix-pwd-is-dot", + ], + mnemonic = "SwiftCompile", + target_under_test = "@build_bazel_rules_swift//test/fixtures/debug_settings:simple", + ) + + coverage_prefix_map_test( + name = "{}_prefix_map".format(name), + tags = [name], + expected_argv = [ + "-profile-generate", + "-profile-coverage-mapping", + "-Xwrapped-swift=-coverage-prefix-pwd-is-dot", + ], + mnemonic = "SwiftCompile", + target_under_test = "@build_bazel_rules_swift//test/fixtures/debug_settings:simple", + ) + + coverage_xcode_prefix_map_test( + name = "{}_xcode_prefix_map".format(name), + tags = [name], + expected_argv = [ + "-coverage-prefix-map", + "__BAZEL_XCODE_DEVELOPER_DIR__=DEVELOPER_DIR", + ], + target_compatible_with = ["@platforms//os:macos"], + mnemonic = "SwiftCompile", + target_under_test = "@build_bazel_rules_swift//test/fixtures/debug_settings:simple", + ) diff --git a/test/debug_settings_tests.bzl b/test/debug_settings_tests.bzl index 40530be67..49d5a61d6 100644 --- a/test/debug_settings_tests.bzl +++ b/test/debug_settings_tests.bzl @@ -90,6 +90,16 @@ cacheable_opt_action_command_line_test = make_action_command_line_test_rule( config_settings = CACHEABLE_OPT_CONFIG_SETTINGS, ) +xcode_remap_command_line_test = make_action_command_line_test_rule( + config_settings = { + "//command_line_option:compilation_mode": "dbg", + "//command_line_option:features": [ + "swift.debug_prefix_map", + "swift.remap_xcode_path", + ], + }, +) + def debug_settings_test_suite(name = "debug_settings"): """Test suite for serializing debugging options. @@ -213,6 +223,18 @@ def debug_settings_test_suite(name = "debug_settings"): target_under_test = "@build_bazel_rules_swift//test/fixtures/debug_settings:simple", ) + xcode_remap_command_line_test( + name = "{}_remap_xcode_path".format(name), + expected_argv = [ + "-debug-prefix-map", + "__BAZEL_XCODE_DEVELOPER_DIR__=DEVELOPER_DIR", + ], + target_compatible_with = ["@platforms//os:macos"], + mnemonic = "SwiftCompile", + tags = [name], + target_under_test = "@build_bazel_rules_swift//test/fixtures/debug_settings:simple", + ) + native.test_suite( name = name, tags = [name], diff --git a/test/fixtures/BUILD b/test/fixtures/BUILD index ec39f3b2b..2fa1a0273 100644 --- a/test/fixtures/BUILD +++ b/test/fixtures/BUILD @@ -3,7 +3,11 @@ load("@bazel_skylib//:bzl_library.bzl", "bzl_library") bzl_library( name = "starlark_tests_bzls", srcs = glob(["*.bzl"]), - visibility = [ - "//test:__pkg__", - ], + visibility = ["//test:__pkg__"], +) + +bzl_library( + name = "common", + srcs = ["common.bzl"], + visibility = ["//test:__pkg__"], ) diff --git a/test/fixtures/generated_header/BUILD b/test/fixtures/generated_header/BUILD index 1263edd94..a87575213 100644 --- a/test/fixtures/generated_header/BUILD +++ b/test/fixtures/generated_header/BUILD @@ -7,9 +7,17 @@ package( licenses(["notice"]) +swift_library( + name = "no_header", + srcs = ["Empty.swift"], + generates_header = False, + tags = FIXTURE_TAGS, +) + swift_library( name = "auto_header", srcs = ["Empty.swift"], + generates_header = True, tags = FIXTURE_TAGS, ) @@ -17,6 +25,7 @@ swift_library( name = "explicit_header", srcs = ["Empty.swift"], generated_header_name = "SomeOtherName.h", + generates_header = True, tags = FIXTURE_TAGS, ) @@ -24,12 +33,22 @@ swift_library( name = "invalid_extension", srcs = ["Empty.swift"], generated_header_name = "Invalid.extension", + generates_header = True, tags = FIXTURE_TAGS, ) swift_library( - name = "invalid_path_separator", + name = "valid_path_separator", srcs = ["Empty.swift"], - generated_header_name = "Invalid/Separator.h", + generated_header_name = "Valid/Separator.h", + generates_header = True, + tags = FIXTURE_TAGS, +) + +swift_library( + name = "invalid_attribute_combination", + srcs = ["Empty.swift"], + generated_header_name = "SomeOtherName.h", + generates_header = False, tags = FIXTURE_TAGS, ) diff --git a/test/fixtures/private_deps/BUILD b/test/fixtures/private_deps/BUILD index b616d30ea..16c4bfb96 100644 --- a/test/fixtures/private_deps/BUILD +++ b/test/fixtures/private_deps/BUILD @@ -14,18 +14,21 @@ licenses(["notice"]) swift_library( name = "private_swift", srcs = ["Empty.swift"], + generates_header = True, tags = FIXTURE_TAGS, ) swift_library( name = "public_swift", srcs = ["Empty.swift"], + generates_header = True, tags = FIXTURE_TAGS, ) swift_library( name = "client_swift_deps", srcs = ["Empty.swift"], + generates_header = True, private_deps = [ ":private_swift", ], @@ -68,13 +71,11 @@ cc_library( swift_library( name = "client_cc_deps", srcs = ["Empty.swift"], - features = [ - # Suppress the generated header/module map on platforms that support - # Objective-C interop so that we don't have to worry about - # conditionally checking for it in the transitive modules on just those - # platforms. - "swift.no_generated_header", - ], + # Suppress the generated header/module map on platforms that support + # Objective-C interop so that we don't have to worry about + # conditionally checking for it in the transitive modules on just those + # platforms. + generates_header = False, private_deps = [ ":private_cc", ], diff --git a/test/generated_header_tests.bzl b/test/generated_header_tests.bzl index bd7ec4c4a..8a9903e92 100644 --- a/test/generated_header_tests.bzl +++ b/test/generated_header_tests.bzl @@ -16,44 +16,15 @@ load( "@build_bazel_rules_swift//test/rules:analysis_failure_test.bzl", - "make_analysis_failure_test_rule", + "analysis_failure_test", ) load( "@build_bazel_rules_swift//test/rules:provider_test.bzl", - "make_provider_test_rule", -) - -# A configuration that forces header and module map generation, regardless of -# the toolchain's default feature set. -GENERATE_HEADER_AND_MODULE_MAP_CONFIG_SETTINGS = { - "//command_line_option:features": [ - "-swift.no_generated_header", - "-swift.no_generated_module_map", - ], -} - -# A configuration that disables header (and therefore module map) generation, -# regardless of the toolchain's default feature set. -NO_GENERATE_HEADER_CONFIG_SETTINGS = { - "//command_line_option:features": [ - "swift.no_generated_header", - ], -} - -generate_header_and_module_map_provider_test = make_provider_test_rule( - config_settings = GENERATE_HEADER_AND_MODULE_MAP_CONFIG_SETTINGS, -) - -generate_header_and_module_map_failure_test = make_analysis_failure_test_rule( - config_settings = GENERATE_HEADER_AND_MODULE_MAP_CONFIG_SETTINGS, -) - -no_generate_header_provider_test = make_provider_test_rule( - config_settings = NO_GENERATE_HEADER_CONFIG_SETTINGS, + "provider_test", ) def generated_header_test_suite(name = "generated_header"): - """Test suite for `swift_library.generated_header`. + """Test suite for `swift_library` generated headers. Args: name: The name prefix for all the nested tests @@ -61,7 +32,7 @@ def generated_header_test_suite(name = "generated_header"): # Verify that the generated header by default gets an automatically # generated name and is an output of the rule. - generate_header_and_module_map_provider_test( + provider_test( name = "{}_automatically_named_header_is_rule_output".format(name), expected_files = [ "test/fixtures/generated_header/auto_header-Swift.h", @@ -73,34 +44,23 @@ def generated_header_test_suite(name = "generated_header"): target_under_test = "@build_bazel_rules_swift//test/fixtures/generated_header:auto_header", ) - # Verify that the generated header is propagated in `SwiftInfo`. - generate_header_and_module_map_provider_test( - name = "{}_automatically_named_header_is_propagated".format(name), + # Verify that no generated header is created if the target doesn't request + # it. + provider_test( + name = "{}_no_header".format(name), expected_files = [ - "test/fixtures/generated_header/auto_header-Swift.h", + "-test/fixtures/generated_header/no_header-Swift.h", + "*", ], - field = "transitive_generated_headers", - provider = "SwiftInfo", + field = "files", + provider = "DefaultInfo", tags = [name], - target_under_test = "@build_bazel_rules_swift//test/fixtures/generated_header:auto_header", + target_under_test = "@build_bazel_rules_swift//test/fixtures/generated_header:no_header", ) - # Verify that the generated module map is propagated in `apple_common.Objc`. - # TODO(b/148604334): Enable this when it analyzes correctly on all platforms. - # generate_header_and_module_map_provider_test( - # name = "{}_automatically_named_header_modulemap_is_propagated".format(name), - # expected_files = [ - # "test/fixtures/generated_header/auto_header.modulemaps/module.modulemap", - # ], - # field = "direct_module_maps", - # provider = "apple_common.Objc", - # tags = [name], - # target_under_test = "@build_bazel_rules_swift//test/fixtures/generated_header:auto_header", - # ) - # Verify that the explicit generated header is an output of the rule and # that the automatically named one is *not*. - generate_header_and_module_map_provider_test( + provider_test( name = "{}_explicit_header".format(name), expected_files = [ "test/fixtures/generated_header/SomeOtherName.h", @@ -114,48 +74,33 @@ def generated_header_test_suite(name = "generated_header"): ) # Verify that the build fails to analyze if an invalid extension is used. - generate_header_and_module_map_failure_test( + analysis_failure_test( name = "{}_invalid_extension".format(name), expected_message = "The generated header for a Swift module must have a '.h' extension", tags = [name], target_under_test = "@build_bazel_rules_swift//test/fixtures/generated_header:invalid_extension", ) - # Verify that the build fails to analyze if a path separator is used. - generate_header_and_module_map_failure_test( - name = "{}_invalid_path_separator".format(name), - expected_message = "The generated header for a Swift module may not contain directory components", - tags = [name], - target_under_test = "@build_bazel_rules_swift//test/fixtures/generated_header:invalid_path_separator", - ) - - # Verify that the header is not generated if the feature - # `swift.no_generated_header` set, when using an automatically named header. - no_generate_header_provider_test( - name = "{}_no_header".format(name), + # Verify that the build analyzes if a path separator is used. + provider_test( + name = "{}_valid_path_separator".format(name), expected_files = [ - "-test/fixtures/generated_header/auto_header-Swift.h", + "test/fixtures/generated_header/Valid/Separator.h", "*", ], field = "files", provider = "DefaultInfo", tags = [name], - target_under_test = "@build_bazel_rules_swift//test/fixtures/generated_header:auto_header", + target_under_test = "@build_bazel_rules_swift//test/fixtures/generated_header:valid_path_separator", ) - # Verify that the header is not generated if the feature - # `swift.no_generated_header` set, even when specifying an explicit header - # name. - no_generate_header_provider_test( - name = "{}_no_explicit_header".format(name), - expected_files = [ - "-test/fixtures/generated_header/SomeOtherName.h", - "*", - ], - field = "files", - provider = "DefaultInfo", + # Verify that the build fails if `generated_header_name` is set when + # `generates_header` is False. + analysis_failure_test( + name = "{}_fails_when_name_provided_but_generates_header_is_false".format(name), + expected_message = "'generated_header_name' may only be provided when 'generates_header' is True", tags = [name], - target_under_test = "@build_bazel_rules_swift//test/fixtures/generated_header:explicit_header", + target_under_test = "@build_bazel_rules_swift//test/fixtures/generated_header:invalid_attribute_combination", ) native.test_suite( diff --git a/test/module_cache_settings_tests.bzl b/test/module_cache_settings_tests.bzl new file mode 100644 index 000000000..da5cde047 --- /dev/null +++ b/test/module_cache_settings_tests.bzl @@ -0,0 +1,103 @@ +# Copyright 2021 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. + +"""Tests for module cache related command line flags under various configs.""" + +load( + "@build_bazel_rules_swift//test/rules:action_command_line_test.bzl", + "make_action_command_line_test_rule", +) + +use_global_module_cache_action_command_line_test = make_action_command_line_test_rule( + config_settings = { + "//command_line_option:features": [ + "swift.use_global_module_cache", + ], + }, +) + +global_module_cache_uses_tmpdir_action_command_line_test = make_action_command_line_test_rule( + config_settings = { + "//command_line_option:features": [ + "swift.global_module_cache_uses_tmpdir", + "swift.use_global_module_cache", + ], + }, +) + +incorrect_global_module_cache_uses_tmpdir_action_command_line_test = make_action_command_line_test_rule( + config_settings = { + "//command_line_option:features": [ + "swift.global_module_cache_uses_tmpdir", + ], + }, +) + +def module_cache_settings_test_suite(name = "module_cache_settings"): + """Test suite for module cache options. + + Args: + name: The name prefix for all the nested tests + """ + + # Verify that a global module cache path is passed to swiftc. + use_global_module_cache_action_command_line_test( + name = "{}_global_module_cache_build".format(name), + expected_argv = [ + "-module-cache-path", + # Starlark doesn't have support for regular expression yet, so we + # can't verify the whole argument here. + "/bin/_swift_module_cache", + ], + not_expected_argv = [ + "-Xwrapped-swift=-ephemeral-module-cache", + "/tmp/__build_bazel_rules_swift/swift_module_cache/build_bazel_rules_swift", + ], + mnemonic = "SwiftCompile", + tags = [name], + target_under_test = "@build_bazel_rules_swift//test/fixtures/debug_settings:simple", + ) + + # Verify that a pre-defined shared module cache path in `/tmp` is + # passed to swiftc. + global_module_cache_uses_tmpdir_action_command_line_test( + name = "{}_tmpdir_module_cache_build".format(name), + expected_argv = [ + "-module-cache-path", + "/tmp/__build_bazel_rules_swift/swift_module_cache/build_bazel_rules_swift", + ], + not_expected_argv = [ + "-Xwrapped-swift=-ephemeral-module-cache", + ], + mnemonic = "SwiftCompile", + tags = [name], + target_under_test = "@build_bazel_rules_swift//test/fixtures/debug_settings:simple", + ) + + # Verify that no module cache path is used when only + # `swift.global_module_cache_uses_tmpdir` is passed. + incorrect_global_module_cache_uses_tmpdir_action_command_line_test( + name = "{}_incorrect_tmpdir_module_cache_build".format(name), + not_expected_argv = [ + "/tmp/__build_bazel_rules_swift/swift_module_cache/build_bazel_rules_swift", + ], + mnemonic = "SwiftCompile", + tags = [name], + target_under_test = "@build_bazel_rules_swift//test/fixtures/debug_settings:simple", + ) + + native.test_suite( + name = name, + tags = [name], + ) diff --git a/test/private_deps_tests.bzl b/test/private_deps_tests.bzl index 1afd6b10e..e594cb852 100644 --- a/test/private_deps_tests.bzl +++ b/test/private_deps_tests.bzl @@ -40,7 +40,7 @@ def private_deps_test_suite(name = "private_deps"): expected_files = [ "test_fixtures_private_deps_private_swift.swiftmodule", ], - field = "transitive_swiftmodules", + field = "transitive_modules.swift!.swiftmodule", provider = "SwiftInfo", tags = [name], target_under_test = "@build_bazel_rules_swift//test/fixtures/private_deps:private_swift", @@ -51,7 +51,7 @@ def private_deps_test_suite(name = "private_deps"): expected_files = [ "test_fixtures_private_deps_public_swift.swiftmodule", ], - field = "transitive_swiftmodules", + field = "transitive_modules.swift!.swiftmodule", provider = "SwiftInfo", tags = [name], target_under_test = "@build_bazel_rules_swift//test/fixtures/private_deps:public_swift", @@ -66,7 +66,7 @@ def private_deps_test_suite(name = "private_deps"): "test_fixtures_private_deps_public_swift.swiftmodule", "-test_fixtures_private_deps_private_swift.swiftmodule", ], - field = "transitive_swiftmodules", + field = "transitive_modules.swift!.swiftmodule", provider = "SwiftInfo", tags = [name], target_under_test = "@build_bazel_rules_swift//test/fixtures/private_deps:client_swift_deps", @@ -94,10 +94,10 @@ def private_deps_test_suite(name = "private_deps"): private_deps_provider_test( name = "{}_client_cc_deps_modulemaps".format(name), expected_files = [ - "/test/fixtures/private_deps/public_cc.modulemaps/module.modulemap", - "-/test/fixtures/private_deps/private_cc.modulemaps/module.modulemap", + "/test/fixtures/private_deps/public_cc.swift.modulemap", + "-/test/fixtures/private_deps/private_cc.swift.modulemap", ], - field = "transitive_modulemaps", + field = "transitive_modules.clang!.module_map", provider = "SwiftInfo", tags = [name], target_under_test = "@build_bazel_rules_swift//test/fixtures/private_deps:client_cc_deps", @@ -115,7 +115,7 @@ def private_deps_test_suite(name = "private_deps"): # dependencies, which we need to ignore. "*", ], - field = "linking_context.libraries_to_link.static_library!", + field = "linking_context.linker_inputs.libraries.static_library!", provider = "CcInfo", tags = [name], target_under_test = "@build_bazel_rules_swift//test/fixtures/private_deps:client_cc_deps", diff --git a/test/rules/action_command_line_test.bzl b/test/rules/action_command_line_test.bzl index 8701901c8..539957dc4 100644 --- a/test/rules/action_command_line_test.bzl +++ b/test/rules/action_command_line_test.bzl @@ -69,18 +69,20 @@ def _action_command_line_test_impl(ctx): if expected + " " not in concatenated_args: unittest.fail( env, - "{}expected argv to contain '{}', but it did not".format( + "{}expected argv to contain '{}', but it did not: {}".format( message_prefix, expected, + concatenated_args, ), ) for not_expected in ctx.attr.not_expected_argv: if not_expected + " " in concatenated_args: unittest.fail( env, - "{}expected argv to not contain '{}', but it did".format( + "{}expected argv to not contain '{}', but it did: {}".format( message_prefix, not_expected, + concatenated_args, ), ) diff --git a/test/rules/provider_test.bzl b/test/rules/provider_test.bzl index eba66af58..f9744e98d 100644 --- a/test/rules/provider_test.bzl +++ b/test/rules/provider_test.bzl @@ -23,15 +23,19 @@ load( "unittest", ) -# A sentinel value returned by `_evaluate_field` when a `None` value is -# encountered during the evaluation of a dotted path on any component other than -# the last component. This allows the caller to distinguish between a legitimate -# `None` value being returned by the entire path vs. an unexpected `None` in an -# earlier component. -# -# A `provider` is used here because it is a simple way of getting a known unique -# object from Bazel that cannot be equal to any other object. -_EVALUATE_FIELD_FAILED = provider() +_EVALUATE_FIELD_FAILED = provider( + doc = """ +A sentinel value returned by `_evaluate_field` when a `None` value is +encountered during the evaluation of a dotted path on any component other than +the last component. This allows the caller to distinguish between a legitimate +`None` value being returned by the entire path vs. an unexpected `None` in an +earlier component. + +A `provider` is used here because it is a simple way of getting a known unique +object from Bazel that cannot be equal to any other object. +""", + fields = [], +) def _evaluate_field(env, source, field): """Evaluates a field or field path on an object and returns its value. @@ -81,7 +85,16 @@ def _evaluate_field(env, source, field): ) return _EVALUATE_FIELD_FAILED - source = [getattr(item, component, None) for item in source] + # If the elements are lists or depsets, flatten the whole thing into + # a single list. + flattened = [] + for item in source: + item = _normalize_collection(item) + if types.is_list(item): + flattened.extend(item) + else: + flattened.append(item) + source = [getattr(item, component, None) for item in flattened] if filter_nones: source = [item for item in source if item != None] else: diff --git a/test/split_derived_files_tests.bzl b/test/split_derived_files_tests.bzl new file mode 100644 index 000000000..5aa8243a5 --- /dev/null +++ b/test/split_derived_files_tests.bzl @@ -0,0 +1,316 @@ +"""Tests for derived files related command line flags under various configs.""" + +load( + "@build_bazel_rules_swift//test/rules:action_command_line_test.bzl", + "make_action_command_line_test_rule", +) +load( + "@build_bazel_rules_swift//test/rules:provider_test.bzl", + "make_provider_test_rule", +) + +default_no_split_test = make_action_command_line_test_rule() +default_no_split_provider_test = make_provider_test_rule() +split_swiftmodule_test = make_action_command_line_test_rule( + config_settings = { + "//command_line_option:features": [ + "swift.split_derived_files_generation", + ], + }, +) +split_swiftmodule_provider_test = make_provider_test_rule( + config_settings = { + "//command_line_option:features": [ + "swift.split_derived_files_generation", + ], + }, +) +split_swiftmodule_wmo_test = make_action_command_line_test_rule( + config_settings = { + "//command_line_option:swiftcopt": [ + "-whole-module-optimization", + ], + "//command_line_option:features": [ + "swift.split_derived_files_generation", + ], + }, +) +split_swiftmodule_wmo_provider_test = make_provider_test_rule( + config_settings = { + "//command_line_option:swiftcopt": [ + "-whole-module-optimization", + ], + "//command_line_option:features": [ + "swift.split_derived_files_generation", + ], + }, +) +split_swiftmodule_skip_function_bodies_test = make_action_command_line_test_rule( + config_settings = { + "//command_line_option:swiftcopt": [ + "-whole-module-optimization", + ], + "//command_line_option:features": [ + "swift.split_derived_files_generation", + "swift.enable_skip_function_bodies", + ], + }, +) +split_swiftmodule_indexing_test = make_action_command_line_test_rule( + config_settings = { + "//command_line_option:features": [ + "swift.index_while_building", + "swift.split_derived_files_generation", + ], + }, +) +split_swiftmodule_bitcode_test = make_action_command_line_test_rule( + config_settings = { + "//command_line_option:apple_bitcode": "embedded", + "//command_line_option:features": [ + "swift.split_derived_files_generation", + ], + }, +) +split_swiftmodule_bitcode_markers_test = make_action_command_line_test_rule( + config_settings = { + "//command_line_option:apple_bitcode": "embedded_markers", + "//command_line_option:features": [ + "swift.split_derived_files_generation", + ], + }, +) + +def split_derived_files_test_suite(name = "split_derived_files"): + """Test suite for split derived files options. + + Args: + name: The name prefix for all the nested tests + """ + default_no_split_test( + name = "{}_default_no_split_args".format(name), + expected_argv = [ + "-emit-module-path", + "-emit-object", + ], + mnemonic = "SwiftCompile", + tags = [name], + target_under_test = "@build_bazel_rules_swift//test/fixtures/debug_settings:simple", + ) + + default_no_split_provider_test( + name = "{}_default_no_split_provider".format(name), + expected_files = [ + "test_fixtures_debug_settings_simple.swiftmodule", + ], + field = "direct_modules.swift.swiftmodule", + provider = "SwiftInfo", + tags = [name], + target_under_test = "@build_bazel_rules_swift//test/fixtures/debug_settings:simple", + ) + + default_no_split_provider_test( + name = "{}_default_no_split_provider_ccinfo".format(name), + expected_files = [ + "libsimple.a", + ], + field = "linking_context.linker_inputs.libraries.pic_static_library!", + provider = "CcInfo", + tags = [name], + target_under_test = "@build_bazel_rules_swift//test/fixtures/debug_settings:simple", + ) + + split_swiftmodule_test( + name = "{}_object_only".format(name), + expected_argv = [ + "-emit-object", + ], + mnemonic = "SwiftCompile", + not_expected_argv = [ + "-emit-module-path", + ], + tags = [name], + target_under_test = "@build_bazel_rules_swift//test/fixtures/debug_settings:simple", + ) + + split_swiftmodule_test( + name = "{}_swiftmodule_only".format(name), + expected_argv = [ + "-emit-module-path", + ], + mnemonic = "SwiftDeriveFiles", + not_expected_argv = [ + "-emit-object", + ], + tags = [name], + target_under_test = "@build_bazel_rules_swift//test/fixtures/debug_settings:simple", + ) + + split_swiftmodule_provider_test( + name = "{}_split_provider".format(name), + expected_files = [ + "test_fixtures_debug_settings_simple.swiftmodule", + ], + field = "direct_modules.swift.swiftmodule", + provider = "SwiftInfo", + tags = [name], + target_under_test = "@build_bazel_rules_swift//test/fixtures/debug_settings:simple", + ) + + split_swiftmodule_provider_test( + name = "{}_split_provider_ccinfo".format(name), + expected_files = [ + "libsimple.a", + ], + field = "linking_context.linker_inputs.libraries.pic_static_library!", + provider = "CcInfo", + tags = [name], + target_under_test = "@build_bazel_rules_swift//test/fixtures/debug_settings:simple", + ) + + split_swiftmodule_wmo_test( + name = "{}_object_only_wmo".format(name), + expected_argv = [ + "-emit-object", + "-whole-module-optimization", + ], + mnemonic = "SwiftCompile", + not_expected_argv = [ + "-emit-module-path", + ], + tags = [name], + target_under_test = "@build_bazel_rules_swift//test/fixtures/debug_settings:simple", + ) + + split_swiftmodule_wmo_test( + name = "{}_swiftmodule_only_wmo".format(name), + expected_argv = [ + "-emit-module-path", + "-whole-module-optimization", + ], + mnemonic = "SwiftDeriveFiles", + not_expected_argv = [ + "-emit-object", + ], + tags = [name], + target_under_test = "@build_bazel_rules_swift//test/fixtures/debug_settings:simple", + ) + + split_swiftmodule_wmo_provider_test( + name = "{}_split_wmo_provider".format(name), + expected_files = [ + "test_fixtures_debug_settings_simple.swiftmodule", + ], + field = "direct_modules.swift.swiftmodule", + provider = "SwiftInfo", + tags = [name], + target_under_test = "@build_bazel_rules_swift//test/fixtures/debug_settings:simple", + ) + + split_swiftmodule_wmo_provider_test( + name = "{}_split_wmo_provider_ccinfo".format(name), + expected_files = [ + "libsimple.a", + ], + field = "linking_context.linker_inputs.libraries.pic_static_library!", + provider = "CcInfo", + tags = [name], + target_under_test = "@build_bazel_rules_swift//test/fixtures/debug_settings:simple", + ) + + split_swiftmodule_skip_function_bodies_test( + name = "{}_no_skip_function_bodies".format(name), + expected_argv = [ + "-emit-object", + ], + mnemonic = "SwiftCompile", + not_expected_argv = [ + "-experimental-skip-non-inlinable-function-bodies", + ], + tags = [name], + target_under_test = "@build_bazel_rules_swift//test/fixtures/debug_settings:simple", + ) + + split_swiftmodule_skip_function_bodies_test( + name = "{}_skip_function_bodies".format(name), + expected_argv = [ + "-experimental-skip-non-inlinable-function-bodies", + ], + mnemonic = "SwiftDeriveFiles", + not_expected_argv = [ + "-emit-object", + ], + tags = [name], + target_under_test = "@build_bazel_rules_swift//test/fixtures/debug_settings:simple", + ) + + split_swiftmodule_indexing_test( + name = "{}_object_only_indexing".format(name), + expected_argv = [ + "-emit-object", + "-index-store-path", + ], + mnemonic = "SwiftCompile", + not_expected_argv = [ + "-emit-module-path", + ], + tags = [name], + target_under_test = "@build_bazel_rules_swift//test/fixtures/debug_settings:simple", + ) + + split_swiftmodule_indexing_test( + name = "{}_swiftmodule_only_indexing".format(name), + expected_argv = [ + "-emit-module-path", + ], + mnemonic = "SwiftDeriveFiles", + not_expected_argv = [ + "-emit-object", + "-index-store-path", + ], + tags = [name], + target_under_test = "@build_bazel_rules_swift//test/fixtures/debug_settings:simple", + ) + + split_swiftmodule_bitcode_test( + name = "{}_bitcode_compile".format(name), + expected_argv = ["-embed-bitcode"], + target_compatible_with = ["@platforms//os:macos"], + mnemonic = "SwiftCompile", + tags = [name], + target_under_test = "@build_bazel_rules_swift//test/fixtures/debug_settings:simple", + ) + + split_swiftmodule_bitcode_test( + name = "{}_bitcode_derive_files".format(name), + not_expected_argv = [ + "-embed-bitcode", + ], + mnemonic = "SwiftDeriveFiles", + tags = [name], + target_under_test = "@build_bazel_rules_swift//test/fixtures/debug_settings:simple", + ) + + split_swiftmodule_bitcode_markers_test( + name = "{}_bitcode_markers_compile".format(name), + expected_argv = ["-embed-bitcode-marker"], + target_compatible_with = ["@platforms//os:macos"], + mnemonic = "SwiftCompile", + tags = [name], + target_under_test = "@build_bazel_rules_swift//test/fixtures/debug_settings:simple", + ) + + split_swiftmodule_bitcode_markers_test( + name = "{}_bitcode_markers_derive_files".format(name), + not_expected_argv = [ + "-embed-bitcode-marker", + ], + mnemonic = "SwiftDeriveFiles", + tags = [name], + target_under_test = "@build_bazel_rules_swift//test/fixtures/debug_settings:simple", + ) + + native.test_suite( + name = name, + tags = [name], + ) diff --git a/test/swift_through_non_swift_tests.bzl b/test/swift_through_non_swift_tests.bzl index c0906260e..92b31fb8e 100644 --- a/test/swift_through_non_swift_tests.bzl +++ b/test/swift_through_non_swift_tests.bzl @@ -34,7 +34,7 @@ def swift_through_non_swift_test_suite(name = "swift_through_non_swift"): "test_fixtures_swift_through_non_swift_lower.swiftmodule", "test_fixtures_swift_through_non_swift_upper.swiftmodule", ], - field = "transitive_swiftmodules", + field = "transitive_modules.swift!.swiftmodule", provider = "SwiftInfo", tags = [name], target_under_test = "@build_bazel_rules_swift//test/fixtures/swift_through_non_swift:upper", diff --git a/third_party/BUILD b/third_party/BUILD index e69de29bb..cd09ba89b 100644 --- a/third_party/BUILD +++ b/third_party/BUILD @@ -0,0 +1,11 @@ +# Consumed by Bazel integration tests. +filegroup( + name = "for_bazel_tests", + testonly = 1, + srcs = glob(["**"]) + [ + "//third_party/bazel_protos:for_bazel_tests", + ], + visibility = [ + "//:__pkg__", + ], +) diff --git a/third_party/bazel_protos/BUILD b/third_party/bazel_protos/BUILD index 3fb0a2c80..553d41bc6 100644 --- a/third_party/bazel_protos/BUILD +++ b/third_party/bazel_protos/BUILD @@ -13,3 +13,13 @@ cc_proto_library( visibility = ["//tools/worker:__pkg__"], deps = [":worker_protocol_proto"], ) + +# Consumed by Bazel integration tests. +filegroup( + name = "for_bazel_tests", + testonly = 1, + srcs = glob(["**"]), + visibility = [ + "//third_party:__pkg__", + ], +) diff --git a/third_party/bazel_protos/README.md b/third_party/bazel_protos/README.md index 442e981f4..8eaca51dd 100644 --- a/third_party/bazel_protos/README.md +++ b/third_party/bazel_protos/README.md @@ -1,3 +1,4 @@ -This directory contains protocol buffers vendored from the main Bazel -repository, so that rules_swift does not need to depend on the entire +This directory contains protocol buffers vendored from the +[main Bazel repository](https://raw.githubusercontent.com/bazelbuild/bazel/master/src/main/protobuf/worker_protocol.proto), +so that rules_swift does not need to depend on the entire `@io_bazel` workspace, which is approximately 100MB. diff --git a/third_party/bazel_protos/worker_protocol.proto b/third_party/bazel_protos/worker_protocol.proto index 0a777a96b..2381df106 100644 --- a/third_party/bazel_protos/worker_protocol.proto +++ b/third_party/bazel_protos/worker_protocol.proto @@ -31,16 +31,31 @@ message Input { bytes digest = 2; } -// This represents a single work unit that Bazel sends to the worker. +// This represents a single work unit that Blaze sends to the worker. message WorkRequest { repeated string arguments = 1; // The inputs that the worker is allowed to read during execution of this // request. repeated Input inputs = 2; + + // Each WorkRequest must have either a unique + // request_id or request_id = 0. If request_id is 0, this WorkRequest must be + // processed alone (singleplex), otherwise the worker may process multiple + // WorkRequests in parallel (multiplexing). As an exception to the above, if + // the cancel field is true, the request_id must be the same as a previously + // sent WorkRequest. The request_id must be attached unchanged to the + // corresponding WorkResponse. Only one singleplex request may be sent to a + // worker at a time. + int32 request_id = 3; + + // EXPERIMENTAL: When true, this is a cancel request, indicating that a + // previously sent WorkRequest with the same request_id should be cancelled. + // The arguments and inputs fields must be empty and should be ignored. + bool cancel = 4; } -// The worker sends this message to Bazel when it finished its work on the +// The worker sends this message to Blaze when it finished its work on the // WorkRequest message. message WorkResponse { int32 exit_code = 1; @@ -49,4 +64,18 @@ message WorkResponse { // supposed to contain compiler warnings / errors etc. - thus we'll use a // string type here, which gives us UTF-8 encoding. string output = 2; + + // This field must be set to the same request_id as the WorkRequest it is a + // response to. Since worker processes which support multiplex worker will + // handle multiple WorkRequests in parallel, this ID will be used to + // determined which WorkerProxy does this WorkResponse belong to. + int32 request_id = 3; + + // EXPERIMENTAL When true, indicates that this response was sent due to + // receiving a cancel request. The exit_code and output fields should be empty + // and will be ignored. Exactly one WorkResponse must be sent for each + // non-cancelling WorkRequest received by the worker, but if the worker + // received a cancel request, it doesn't matter if it replies with a regular + // WorkResponse or with one where was_cancelled = true. + bool was_cancelled = 4; } diff --git a/third_party/build_bazel_rules_swift_index_import/BUILD.overlay b/third_party/build_bazel_rules_swift_index_import/BUILD.overlay new file mode 100644 index 000000000..13d2855a7 --- /dev/null +++ b/third_party/build_bazel_rules_swift_index_import/BUILD.overlay @@ -0,0 +1,9 @@ +load("@bazel_skylib//rules:native_binary.bzl", "native_binary") + +package(default_visibility = ["//visibility:public"]) + +native_binary( + name = "index_import", + src = "index-import", + out = "index-import", +) diff --git a/tools/common/BUILD b/tools/common/BUILD index a627524a9..83eff15a9 100644 --- a/tools/common/BUILD +++ b/tools/common/BUILD @@ -1,13 +1,17 @@ load("@rules_cc//cc:defs.bzl", "cc_library") package( - default_visibility = [ - "//tools/worker:__pkg__", - ], + default_visibility = ["//tools/worker:__pkg__"], ) licenses(["notice"]) +cc_library( + name = "bazel_substitutions", + srcs = ["bazel_substitutions.cc"], + hdrs = ["bazel_substitutions.h"], +) + cc_library( name = "file_system", srcs = ["file_system.cc"], @@ -32,12 +36,6 @@ cc_library( ], ) -cc_library( - name = "string_utils", - srcs = ["string_utils.cc"], - hdrs = ["string_utils.h"], -) - cc_library( name = "temp_file", hdrs = ["temp_file.h"], diff --git a/tools/common/bazel_substitutions.cc b/tools/common/bazel_substitutions.cc new file mode 100644 index 000000000..25f3c1b6a --- /dev/null +++ b/tools/common/bazel_substitutions.cc @@ -0,0 +1,99 @@ +// Copyright 2021 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. + +#include "tools/common/bazel_substitutions.h" + +#include +#include +#include +#include + +namespace bazel_rules_swift { +namespace { + +// The placeholder string used by Bazel that should be replaced by +// `DEVELOPER_DIR` at runtime. +static const char kBazelXcodeDeveloperDir[] = + "__BAZEL_XCODE_DEVELOPER_DIR__"; + +// The placeholder string used by Bazel that should be replaced by `SDKROOT` +// at runtime. +static const char kBazelXcodeSdkRoot[] = "__BAZEL_XCODE_SDKROOT__"; + +// Returns the value of the given environment variable, or the empty string if +// it wasn't set. +std::string GetEnvironmentVariable(const char *name) { + char *env_value = getenv(name); + if (env_value == nullptr) { + return ""; + } + return env_value; +} + +} // namespace + +BazelPlaceholderSubstitutions::BazelPlaceholderSubstitutions() { + // When targeting Apple platforms, replace the magic Bazel placeholders with + // the path in the corresponding environment variable. These should be set by + // the build rules; only attempt to retrieve them if they're actually seen in + // the argument list. + placeholder_resolvers_ = { + {kBazelXcodeDeveloperDir, PlaceholderResolver([]() { + return GetEnvironmentVariable("DEVELOPER_DIR"); + })}, + {kBazelXcodeSdkRoot, + PlaceholderResolver([]() { return GetEnvironmentVariable("SDKROOT"); })}, + }; +} + +BazelPlaceholderSubstitutions::BazelPlaceholderSubstitutions( + const std::string &developer_dir, const std::string &sdk_root) { + placeholder_resolvers_ = { + {kBazelXcodeDeveloperDir, + PlaceholderResolver([=]() { return developer_dir; })}, + {kBazelXcodeSdkRoot, + PlaceholderResolver([=]() { return sdk_root; })}, + }; +} + +bool BazelPlaceholderSubstitutions::Apply(std::string &arg) { + bool changed = false; + + // Replace placeholders in the string with their actual values. + for (auto& pair : placeholder_resolvers_) { + changed |= FindAndReplace(pair.first, pair.second, arg); + } + + return changed; +} + +bool BazelPlaceholderSubstitutions::FindAndReplace( + const std::string &placeholder, + BazelPlaceholderSubstitutions::PlaceholderResolver &resolver, + std::string &str) { + int start = 0; + bool changed = false; + while ((start = str.find(placeholder, start)) != std::string::npos) { + std::string resolved_value = resolver.get(); + if (resolved_value.empty()) { + return false; + } + changed = true; + str.replace(start, placeholder.length(), resolved_value); + start += resolved_value.length(); + } + return changed; +} + +} // namespace bazel_rules_swift diff --git a/tools/common/bazel_substitutions.h b/tools/common/bazel_substitutions.h new file mode 100644 index 000000000..8ba54586b --- /dev/null +++ b/tools/common/bazel_substitutions.h @@ -0,0 +1,85 @@ +// Copyright 2021 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. + +#ifndef BUILD_BAZEL_RULES_SWIFT_TOOLS_COMMON_BAZEL_SUBSTITUTIONS_H_ +#define BUILD_BAZEL_RULES_SWIFT_TOOLS_COMMON_BAZEL_SUBSTITUTIONS_H_ + +#include +#include +#include + +namespace bazel_rules_swift { + +// Manages the substitution of special Bazel placeholder strings in command line +// arguments that are used to defer the determination of Apple developer and SDK +// paths until execution time. +class BazelPlaceholderSubstitutions { + public: + // Initializes the substitutions by looking them up in the process's + // environment when they are first requested. + BazelPlaceholderSubstitutions(); + + // Initializes the substitutions with the given fixed strings. Intended to be + // used for testing. + BazelPlaceholderSubstitutions(const std::string &developer_dir, + const std::string &sdk_root); + + // Applies any necessary substitutions to `arg` and returns true if this + // caused the string to change. + bool Apply(std::string &arg); + + private: + // A resolver for a Bazel placeholder string that retrieves and caches the + // value the first time it is requested. + class PlaceholderResolver { + public: + explicit PlaceholderResolver(std::function fn) + : function_(fn), initialized_(false) {} + + // Returns the requested placeholder value, caching it for future + // retrievals. + std::string get() { + if (!initialized_) { + value_ = function_(); + initialized_ = true; + } + return value_; + } + + private: + // The function that returns the value of the placeholder, or the empty + // string if the placeholder should not be replaced. + std::function function_; + + // Indicates whether the value of the placeholder has been requested yet and + // and is therefore initialized. + bool initialized_; + + // The cached value of the placeholder if `initialized_` is true. + std::string value_; + }; + + // Finds and replaces all instances of `placeholder` with the value provided + // by `resolver`, in-place on `str`. Returns true if the string was changed. + bool FindAndReplace(const std::string &placeholder, + PlaceholderResolver &resolver, std::string &str); + + // A mapping from Bazel placeholder strings to resolvers that provide their + // values. + std::map placeholder_resolvers_; +}; + +} // namespace bazel_rules_swift + +#endif // BUILD_BAZEL_RULES_SWIFT_TOOLS_COMMON_BAZEL_SUBSTITUTIONS_H_ diff --git a/tools/common/file_system.cc b/tools/common/file_system.cc index f04f55dab..081c82750 100644 --- a/tools/common/file_system.cc +++ b/tools/common/file_system.cc @@ -18,10 +18,13 @@ #include #include +#include #include +#include #ifdef __APPLE__ #include +#include #else #include #include @@ -37,6 +40,20 @@ std::string GetCurrentDirectory() { return cwd; } +bool FileExists(const std::string &path) { + return access(path.c_str(), 0) == 0; +} + +bool RemoveFile(const std::string &path) { +#ifdef __APPLE__ + return removefile(path.c_str(), nullptr, 0); +#elif __unix__ + return remove(path.c_str()); +#else +#error Only macOS and Unix are supported at this time. +#endif +} + bool CopyFile(const std::string &src, const std::string &dest) { #ifdef __APPLE__ // The `copyfile` function with `COPYFILE_ALL` mode preserves permissions and @@ -84,7 +101,13 @@ bool MakeDirs(const std::string &path, int mode) { struct stat dir_stats; if (stat(path.c_str(), &dir_stats) == 0) { // Return true if the directory already exists. - return S_ISDIR(dir_stats.st_mode); + if (!S_ISDIR(dir_stats.st_mode)) { + std::cerr << "error: path exists and isn't a directory " + << strerror(errno) << " (" << path.c_str() << ") \n"; + return false; + } else { + return true; + } } // Recurse to create the parent directory. @@ -93,5 +116,29 @@ bool MakeDirs(const std::string &path, int mode) { } // Create the directory that was requested. - return mkdir(path.c_str(), mode) == 0; + int mkdir_ret = mkdir(path.c_str(), mode); + if (mkdir_ret == 0) { + return true; + } + + // Save the mkdir errno for better reporting. + int mkdir_errno = errno; + + // Deal with a race condition when 2 `MakeDirs` are running at the same time: + // one `mkdir` invocation will fail in each recursive call at different + // points. Don't recurse here to avoid an infinite loop in failure cases. + if (errno == EEXIST && stat(path.c_str(), &dir_stats) == 0) { + if (!S_ISDIR(dir_stats.st_mode)) { + std::cerr << "error: path exists and isn't a directory " + << strerror(errno) << " (" << path.c_str() << ") \n"; + return false; + } else { + // Return true if the directory already exists. + return true; + } + } + + std::cerr << "error:" << strerror(mkdir_errno) << " (" << path.c_str() + << ") \n"; + return false; } diff --git a/tools/common/file_system.h b/tools/common/file_system.h index 2d233809a..644917de3 100644 --- a/tools/common/file_system.h +++ b/tools/common/file_system.h @@ -20,6 +20,12 @@ // Gets the path to the current working directory. std::string GetCurrentDirectory(); +// Returns true if something exists at path. +bool FileExists(const std::string &path); + +// Removes the file at path. Returns true if successful. +bool RemoveFile(const std::string &path); + // Copies the file at src to dest. Returns true if successful. bool CopyFile(const std::string &src, const std::string &dest); diff --git a/tools/common/string_utils.cc b/tools/common/string_utils.cc deleted file mode 100644 index 07c137faa..000000000 --- a/tools/common/string_utils.cc +++ /dev/null @@ -1,42 +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. - -#include -#include - -// Finds and replaces all instances of oldsub with newsub, in-place on str. -// Returns true if the string was changed. -static bool FindAndReplace(const std::string &oldsub, const std::string &newsub, - std::string *str) { - int start = 0; - bool changed = false; - while ((start = str->find(oldsub, start)) != std::string::npos) { - changed = true; - str->replace(start, oldsub.length(), newsub); - start += newsub.length(); - } - return changed; -} - -bool MakeSubstitutions(std::string *arg, - const std::map &mappings) { - bool changed = false; - - // Replace placeholders in the string with their actual values. - for (const std::pair &mapping : mappings) { - changed |= FindAndReplace(mapping.first, mapping.second, arg); - } - - return changed; -} diff --git a/tools/common/string_utils.h b/tools/common/string_utils.h deleted file mode 100644 index 46715e511..000000000 --- a/tools/common/string_utils.h +++ /dev/null @@ -1,25 +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. - -#ifndef BUILD_BAZEL_RULES_SWIFT_TOOLS_COMMON_STRING_UTILS_H_ -#define BUILD_BAZEL_RULES_SWIFT_TOOLS_COMMON_STRING_UTILS_H_ - -#include -#include - -// Rewrites the given argument by replacing any Bazel path placeholders. -bool MakeSubstitutions(std::string *arg, - const std::map &mappings); - -#endif // BUILD_BAZEL_RULES_SWIFT_TOOLS_COMMON_STRING_UTILS_H_ diff --git a/tools/common/temp_file.h b/tools/common/temp_file.h index 04d9f7029..1429a9de9 100644 --- a/tools/common/temp_file.h +++ b/tools/common/temp_file.h @@ -41,8 +41,8 @@ class TempFile { snprintf(path.get(), size, "%s/%s", tmpDir, path_template.c_str()); if (mkstemp(path.get()) == -1) { - std::cerr << "Failed to create temporary file '" << path.get() << "': " - << strerror(errno) << "\n"; + std::cerr << "Failed to create temporary file '" << path.get() + << "': " << strerror(errno) << "\n"; return nullptr; } return std::unique_ptr(new TempFile(path.get())); diff --git a/tools/compile_stats/BUILD b/tools/compile_stats/BUILD deleted file mode 100644 index fea2dd826..000000000 --- a/tools/compile_stats/BUILD +++ /dev/null @@ -1,6 +0,0 @@ -load("//swift:swift.bzl", "swift_binary") - -swift_binary( - name = "stats_processor", - srcs = ["main.swift"], -) diff --git a/tools/compile_stats/README.md b/tools/compile_stats/README.md deleted file mode 100644 index 8e771dad4..000000000 --- a/tools/compile_stats/README.md +++ /dev/null @@ -1,18 +0,0 @@ -# compile_stats/build.sh helper - -This script conveniently wraps up the various Bazel arguments needed to collect -timing statistics from the Swift compiler. - -## Usage - -``` -build.sh [arguments] -``` - -where `[arguments]` is a list of arguments that should be passed directly to -Bazel (such as targets to build, or additional flags). - -The output of this script will be a Markdown-formatted consolidated report that -shows the driver and frontend timings for the jobs that were invoked across -_every_ Swift target in the build, with the slowest driver invocations at the -top (and the slowest frontend invocations at the top within those groups). diff --git a/tools/compile_stats/build.sh b/tools/compile_stats/build.sh deleted file mode 100755 index 9c0669e65..000000000 --- a/tools/compile_stats/build.sh +++ /dev/null @@ -1,62 +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 - -manifest="$(mktemp "${TMPDIR:-/tmp}/compile_stats_manifest.XXXXXX")" -trap "rm -f $manifest" EXIT - -collect_stats_flags=( - --features=swift.compile_stats - --output_groups=swift_compile_stats - --aspects=@build_bazel_rules_swift//swift:stats.bzl%collect_swift_compile_stats -) - -# Build the desired targets, with stderr being output as normal. -bazel build "${collect_stats_flags[@]}" "$@" - -# Build the targets *again*, this time with `--experimental_show_artifacts`. -# This should be a null build since we just built everything above. The reason -# we split this is because the artifact output also goes to stderr, so we'd have -# to either capture the whole thing or redirect it to `tee` with color/curses -# support disabled, which would be difficult for the user to read. -# -# A human-readable explanation of the code below: -# -# 1. Build the requested targets while collecting the stats outputs. Use the -# `--experimental_show_artifacts` flag to get a scrapeable dump of the output -# files at the end. -# 2. Pipe stderr into a `sed` command that ignores all lines before the line -# "Build artifacts:", which signifies the beginning of the listing. -# 3. Pipe that into a `sed` command that ignores any lines that don't start with -# ">>>" and strips that prefix off the ones that do. This is the list of -# stats directories that were created for each built target. -# 4. Pass those directories into `find` to print just the filenames of the -# contents of those directories. - -bazel build --experimental_show_artifacts "${collect_stats_flags[@]}" "$@" \ - 2>&1 > /dev/null \ - | sed -e '/Build artifacts:/,$!d' \ - | sed -e 's/^>>>//' -e 't' -e 'd' \ - | while read statsdir ; do - find "$statsdir" -type f - done > "$manifest" - -# Run the report generating tool. -bazel run \ - --apple_platform_type=macos \ - @build_bazel_rules_swift//tools/compile_stats:stats_processor -- \ - "$manifest" diff --git a/tools/compile_stats/main.swift b/tools/compile_stats/main.swift deleted file mode 100644 index 8a5df0fd1..000000000 --- a/tools/compile_stats/main.swift +++ /dev/null @@ -1,242 +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. - -import Foundation - -/// Encapsulates the wall, user, and sys clock times of a process's execution. -struct Timing: Comparable { - /// Total wall clock time. - var wall: TimeInterval - - /// Amount of time spent in user-mode code. - var user: TimeInterval - - /// Amount of time spent in kernel code. - var sys: TimeInterval - - static func < (lhs: Timing, rhs: Timing) -> Bool { - return lhs.wall < rhs.wall - } - - /// Creates a new timing with the given values. - init(wall: TimeInterval = 0, user: TimeInterval = 0, sys: TimeInterval = 0) { - self.wall = wall - self.user = user - self.sys = sys - } - - /// Gets or sets one of the clock values in this timing. - /// - /// - Parameter key: The timing to update. Must be `"wall"`, `"user"`, or `"sys"`. - subscript(key: String) -> Double { - get { return self[keyPath: keyPath(for: key)] } - set { - let keyPath = self.keyPath(for: key) - self[keyPath: keyPath] = newValue - } - } - - /// Gets a keypath that can be used to get or set the given timing. - /// - /// - Precondition: `name` must be `"wall"`, `"user"`, or `"sys"`. - private func keyPath(for name: String) -> WritableKeyPath { - switch name { - case "wall": return \.wall - case "user": return \.user - case "sys": return \.sys - default: fatalError("Unknown timing '\(name)'") - } - } -} - -/// Statistics about an invocation of the Swift frontend. -struct FrontendStats { - /// The name of the Swift module being compiled. - let moduleName: String - - /// The source file being compiled, or `"all"` if the invocation involved multiple files. - let sourceFile: String - - /// The start time of frontend invocation. - let startTime: Date - - /// The timings of various tasks (such as type checking, code generation, etc.) that occurred - /// during compilation. - private(set) var taskTimings = [String: Timing]() - - /// The total timing of the frontend invocation. - private(set) var frontendTiming = Timing() - - /// The timings of various tasks sorted such that the slowest ones are first. - func sortedTaskTimings(interestingOnly: Bool) -> [(String, Timing)] { - var allTimings = Array(taskTimings) - - if interestingOnly { - allTimings = allTimings.filter { - switch $0.0 { - case "AST verification", - "Name binding", - "LLVM pipeline", - "Parsing", - "Serialization, swiftdoc", - "Serialization, swiftmodule", - "SILGen", - "SIL optimization", - "SIL verification, post-optimization", - "SIL verification, pre-optimization", - "Type checking and Semantic analysis": - return true - default: return false - } - } - } - - allTimings.sort { $0.1.wall > $1.1.wall } - return allTimings - } - - /// Creates a new `FrontendStats` with information from the given JSON report. - /// - /// - Parameter url: A URL to the frontend report JSON file. - /// - Throws: If there was an error reading or decoding the report. - init(contentsOf url: URL) throws { - let reportData = try Data(contentsOf: url) - let jsonDictionary = try JSONSerialization.jsonObject(with: reportData) as! [String: Any] - - let pathParts = url.lastPathComponent.split(separator: "-") - self.moduleName = String(pathParts[4]) - self.sourceFile = String(pathParts[5]) - self.startTime = Date(timeIntervalSince1970: TimeInterval(pathParts[1])! / 10e9) - - for (key, value) in jsonDictionary { - let keyParts = key.split(separator: ".") - - if key.hasPrefix("time.swift.") { - let category = String(keyParts[2]) - let timingName = String(keyParts[3]) - self.taskTimings[category, default: Timing()][timingName] = value as! Double - } else if key.hasPrefix("time.swift-frontend.") { - // The filename and target triple embedded in this string might contain dots, which screws - // up our string splitting. Instead, we can just get the last component. - let timingName = String(keyParts.last!) - self.frontendTiming[timingName] = value as! Double - } - } - } -} - -/// Statistics about an invocation of the Swift compiler driver. -struct DriverStats { - /// The name of the Swift module being compiled. - let moduleName: String - - /// The start time of the driver invocation. - let startTime: Date - - /// The total timing of the driver invocation. - private(set) var driverTiming = Timing() - - /// Creates a new `DriverStats` with information from the given JSON report. - /// - /// - Parameter url: A URL to the driver report JSON file. - /// - Throws: If there was an error reading or decoding the report. - init(contentsOf url: URL) throws { - let reportData = try Data(contentsOf: url) - let jsonDictionary = try JSONSerialization.jsonObject(with: reportData) as! [String: Any] - - let pathParts = url.lastPathComponent.split(separator: "-") - self.moduleName = String(pathParts[4]) - self.startTime = Date(timeIntervalSince1970: TimeInterval(pathParts[1])! / 10e9) - - for (key, value) in jsonDictionary { - let keyParts = key.split(separator: ".") - - if key.hasPrefix("time.swift-driver.") { - // The filename and target triple embedded in this string might contain dots, which screws - // up our string splitting. Instead, we can just get the last component. - let timingName = String(keyParts.last!) - self.driverTiming[timingName] = value as! Double - } - } - } -} - -/// Returns a formatted string for the given time interval, appropriate for tabular output. -func formattedSeconds(_ value: TimeInterval) -> String { - return String(format: "%8.3fs", value) -} - -/// Processes the reports described in the manifest file and outputs formatted tables to standard -/// output. -func processReports(fromManifest url: URL) throws { - let manifestContents = try String(contentsOf: url).trimmingCharacters(in: .whitespacesAndNewlines) - let reportPaths = manifestContents.split(separator: "\n") - - var allDriverStats = [DriverStats]() - var allFrontendStats = [String: [FrontendStats]]() - - for reportPath in reportPaths.lazy.map(String.init) { - let reportURL = URL(fileURLWithPath: reportPath) - if reportPath.contains("swift-driver") { - let stats = try DriverStats(contentsOf: reportURL) - allDriverStats.append(stats) - } else if reportPath.contains("swift-frontend") { - let stats = try FrontendStats(contentsOf: reportURL) - allFrontendStats[stats.moduleName, default: []].append(stats) - } - } - - // Sort the driver stats so that the slowest compiles come first. - allDriverStats.sort { $0.driverTiming.wall > $1.driverTiming.wall } - - for driverStats in allDriverStats { - let totalDriverTime = String(format: "%0.3fs", driverStats.driverTiming.wall) - print("# Driver invocation for module \(driverStats.moduleName) (\(totalDriverTime))") - print() - - guard var frontendStatsForModule = allFrontendStats[driverStats.moduleName] else { continue } - frontendStatsForModule.sort { $0.frontendTiming.wall > $1.frontendTiming.wall } - - for frontendStats in frontendStatsForModule { - let totalFrontendTime = String(format: "%0.3fs", frontendStats.frontendTiming.wall) - print( - """ - ## Frontend invocation for \ - \(frontendStats.moduleName)/\(frontendStats.sourceFile) \ - (\(totalFrontendTime)) - """) - print() - print("| Task | Wall | User | Sys |") - print("| ----------------------------------- | --------- | --------- | --------- |") - - for (category, taskTiming) in frontendStats.sortedTaskTimings(interestingOnly: true) { - let formattedCategory = category.padding(toLength: 35, withPad: " ", startingAt: 0) - let formattedWall = formattedSeconds(taskTiming.wall) - let formattedUser = formattedSeconds(taskTiming.user) - let formattedSys = formattedSeconds(taskTiming.sys) - print("| \(formattedCategory) | \(formattedWall) | \(formattedUser) | \(formattedSys) |") - } - - print() - } - } -} - -guard CommandLine.arguments.count == 2 else { - print("USAGE: stats_processor ") - exit(1) -} - -let manifestURL = URL(fileURLWithPath: CommandLine.arguments[1]) -try processReports(fromManifest: manifestURL) diff --git a/tools/dump_toolchains/dump_toolchains.sh b/tools/dump_toolchains/dump_toolchains.sh index 18f008211..9a97fc793 100755 --- a/tools/dump_toolchains/dump_toolchains.sh +++ b/tools/dump_toolchains/dump_toolchains.sh @@ -21,22 +21,23 @@ if [[ "$(uname)" != Darwin ]]; then exit 1 fi -toolchain_directory=/Library/Developer/Toolchains -if [[ ! -d "$toolchain_directory" ]]; then - echo "error: '$toolchain_directory' doesn't exist" - exit 1 -fi +readonly toolchain_directories=( + /Library/Developer/Toolchains + ~/Library/Developer/Toolchains +) -for toolchain in "$toolchain_directory"/*.xctoolchain +for toolchain_directory in "${toolchain_directories[@]}" do - plist_path="$toolchain/Info.plist" + 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 + 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") - toolchain_name=$(basename "$toolchain") - echo "$toolchain_name -> $bundle_id" + bundle_id=$(/usr/libexec/PlistBuddy -c "print :CFBundleIdentifier" "$plist_path") + echo "$toolchain -> $bundle_id" + done done diff --git a/tools/worker/BUILD b/tools/worker/BUILD index 23bb274ae..3cd73c71c 100644 --- a/tools/worker/BUILD +++ b/tools/worker/BUILD @@ -18,12 +18,18 @@ config_setting( }, ) +# Internal hinge for index while building V2 feature +config_setting( + name = "use_global_index_store", + values = { + "features": "swift.use_global_index_store", + }, +) + cc_library( name = "compile_with_worker", srcs = [ "compile_with_worker.cc", - "output_file_map.cc", - "output_file_map.h", "work_processor.cc", "work_processor.h", ], @@ -33,9 +39,7 @@ cc_library( "//third_party/bazel_protos:worker_protocol_cc_proto", "//tools/common:file_system", "//tools/common:path_utils", - "//tools/common:string_utils", "//tools/common:temp_file", - "@com_github_nlohmann_json//:json", "@com_google_protobuf//:protobuf", ], ) @@ -51,13 +55,30 @@ cc_library( cc_library( name = "swift_runner", - srcs = ["swift_runner.cc"], + srcs = [ + "output_file_map.cc", + "output_file_map.h", + "swift_runner.cc", + ], hdrs = ["swift_runner.h"], + copts = select({ + ":use_global_index_store": [ + "-DINDEX_IMPORT_PATH=\\\"$(rootpath @build_bazel_rules_swift_index_import//:index_import)\\\"", + ], + "//conditions:default": [], + }), + data = select({ + ":use_global_index_store": [ + "@build_bazel_rules_swift_index_import//:index_import", + ], + "//conditions:default": [], + }), deps = [ + "//tools/common:bazel_substitutions", "//tools/common:file_system", "//tools/common:process", - "//tools/common:string_utils", "//tools/common:temp_file", + "@com_github_nlohmann_json//:json", ], ) diff --git a/tools/worker/compile_with_worker.cc b/tools/worker/compile_with_worker.cc index e17288846..36f873787 100644 --- a/tools/worker/compile_with_worker.cc +++ b/tools/worker/compile_with_worker.cc @@ -14,12 +14,12 @@ #include "tools/worker/compile_with_worker.h" +#include +#include #include #include -#include -#include #include "third_party/bazel_protos/worker_protocol.pb.h" #include "tools/worker/work_processor.h" diff --git a/tools/worker/output_file_map.cc b/tools/worker/output_file_map.cc index a8649099d..126d366d2 100644 --- a/tools/worker/output_file_map.cc +++ b/tools/worker/output_file_map.cc @@ -17,10 +17,10 @@ #include #include #include +#include #include #include "tools/common/path_utils.h" -#include namespace { @@ -57,6 +57,7 @@ void OutputFileMap::WriteToPath(const std::string &path) { void OutputFileMap::UpdateForIncremental(const std::string &path) { nlohmann::json new_output_file_map; std::map incremental_outputs; + std::map incremental_inputs; // The empty string key is used to represent outputs that are for the whole // module, rather than for a particular source file. @@ -111,6 +112,15 @@ void OutputFileMap::UpdateForIncremental(const std::string &path) { new_output_file_map[src] = src_map; } + auto swiftmodule_path = ReplaceExtension(path, ".swiftmodule", /*all_extensions=*/true); + auto copied_swiftmodule_path = MakeIncrementalOutputPath(swiftmodule_path); + incremental_inputs[swiftmodule_path] = copied_swiftmodule_path; + + auto swiftdoc_path = ReplaceExtension(path, ".swiftdoc", /*all_extensions=*/true); + auto copied_swiftdoc_path = MakeIncrementalOutputPath(swiftdoc_path); + incremental_inputs[swiftdoc_path] = copied_swiftdoc_path; + json_ = new_output_file_map; incremental_outputs_ = incremental_outputs; + incremental_inputs_ = incremental_inputs; } diff --git a/tools/worker/output_file_map.h b/tools/worker/output_file_map.h index a81907112..4e17f16fe 100644 --- a/tools/worker/output_file_map.h +++ b/tools/worker/output_file_map.h @@ -16,9 +16,8 @@ #define BUILD_BAZEL_RULES_SWIFT_TOOLS_WORKER_OUTPUT_FILE_MAP_H #include -#include - #include +#include // Supports loading and rewriting a `swiftc` output file map to support // incremental compilation. @@ -40,6 +39,14 @@ class OutputFileMap { return incremental_outputs_; } + // A map containing expected output files that will be generated in the + // non-incremental storage area, but need to be copied back at the start of + // the next compile. The key is the original object path; the corresponding + // value is its location in the incremental storage area. + const std::map incremental_inputs() const { + return incremental_inputs_; + } + // Reads the output file map from the JSON file at the given path, and updates // it to support incremental builds. void ReadFromPath(const std::string &path); @@ -54,6 +61,7 @@ class OutputFileMap { nlohmann::json json_; std::map incremental_outputs_; + std::map incremental_inputs_; }; #endif // BUILD_BAZEL_RULES_SWIFT_TOOLS_WORKER_OUTPUT_FILE_MAP_H_ diff --git a/tools/worker/swift_runner.cc b/tools/worker/swift_runner.cc index eca0f2993..41068581c 100644 --- a/tools/worker/swift_runner.cc +++ b/tools/worker/swift_runner.cc @@ -16,26 +16,14 @@ #include +#include "tools/common/bazel_substitutions.h" #include "tools/common/file_system.h" #include "tools/common/process.h" -#include "tools/common/string_utils.h" #include "tools/common/temp_file.h" +#include "tools/worker/output_file_map.h" namespace { -#if __APPLE__ -// Returns the requested environment variable in the current process's -// environment. Aborts if this variable is unset. -static std::string GetMandatoryEnvVar(const std::string &var_name) { - char *env_value = getenv(var_name.c_str()); - if (env_value == nullptr) { - std::cerr << "Error: " << var_name << " not set.\n"; - abort(); - } - return env_value; -} -#endif - // Creates a temporary file and writes the given arguments to it, one per line. static std::unique_ptr WriteResponseFile( const std::vector &args) { @@ -99,52 +87,148 @@ static std::string Unescape(const std::string &arg) { return result; } +// If `str` starts with `prefix`, `str` is mutated to remove `prefix` and the +// function returns true. Otherwise, `str` is left unmodified and the function +// returns `false`. +static bool StripPrefix(const std::string &prefix, std::string &str) { + if (str.find(prefix) != 0) { + return false; + } + str.erase(0, prefix.size()); + return true; +} + } // namespace SwiftRunner::SwiftRunner(const std::vector &args, bool force_response_file) : force_response_file_(force_response_file) { -#if __APPLE__ - // On Apple platforms, replace the magic Bazel placeholders with the path - // in the corresponding environment variable. - std::string developer_dir = GetMandatoryEnvVar("DEVELOPER_DIR"); - std::string sdk_root = GetMandatoryEnvVar("SDKROOT"); - - bazel_placeholder_substitutions_ = { - {"__BAZEL_XCODE_DEVELOPER_DIR__", developer_dir}, - {"__BAZEL_XCODE_SDKROOT__", sdk_root}, - }; -#else - // We don't have these placeholder strings on non-Apple platforms. -#endif args_ = ProcessArguments(args); } int SwiftRunner::Run(std::ostream *stderr_stream, bool stdout_to_stderr) { int exit_code = RunSubProcess(args_, stderr_stream, stdout_to_stderr); + if (exit_code != 0) { + return exit_code; + } + + if (!generated_header_rewriter_path_.empty()) { +#if __APPLE__ + // Skip the `xcrun` argument that's added when running on Apple platforms. + int initial_args_to_skip = 1; +#else + int initial_args_to_skip = 0; +#endif + + std::vector rewriter_args; + rewriter_args.reserve(args_.size() + 2 - initial_args_to_skip); + rewriter_args.push_back(generated_header_rewriter_path_); + rewriter_args.push_back("--"); + rewriter_args.insert(rewriter_args.end(), + args_.begin() + initial_args_to_skip, args_.end()); + + exit_code = RunSubProcess(rewriter_args, stderr_stream, stdout_to_stderr); + } + + auto enable_global_index_store = global_index_store_import_path_ != ""; + if (enable_global_index_store) { + OutputFileMap output_file_map; + output_file_map.ReadFromPath(output_file_map_path_); + + auto outputs = output_file_map.incremental_outputs(); + std::map::iterator it; + + std::vector ii_args; +// The index-import runfile path is passed as a define +#if defined(INDEX_IMPORT_PATH) + ii_args.push_back(INDEX_IMPORT_PATH); +#else + // Logical error + std::cerr << "Incorrectly compiled work_processor.cc"; + exit_code = EXIT_FAILURE; + return exit_code; +#endif + + for (it = outputs.begin(); it != outputs.end(); it++) { + // Need the actual output paths of the compiler - not bazel + auto output_path = it->first; + auto file_type = output_path.substr(output_path.find_last_of(".") + 1); + if (file_type == "o") { + ii_args.push_back("-import-output-file"); + ii_args.push_back(output_path); + } + } + + auto exec_root = GetCurrentDirectory(); + // Copy back from the global index store to bazel's index store + ii_args.push_back(exec_root + "/" + global_index_store_import_path_); + ii_args.push_back(exec_root + "/" + index_store_path_); + exit_code = + RunSubProcess(ii_args, stderr_stream, /*stdout_to_stderr=*/true); + } return exit_code; } +// Marker for end of iteration +class StreamIteratorEnd {}; + +// Basic iterator over an ifstream +class StreamIterator { + public: + StreamIterator(std::ifstream &file) : file_{file} { next(); } + + const std::string &operator*() const { return str_; } + + StreamIterator &operator++() { + next(); + return *this; + } + + bool operator!=(StreamIteratorEnd) const { return !!file_; } + + private: + void next() { std::getline(file_, str_); } + + std::ifstream &file_; + std::string str_; +}; + +class ArgsFile { + public: + ArgsFile(std::ifstream &file) : file_(file) {} + + StreamIterator begin() { return StreamIterator{file_}; } + + StreamIteratorEnd end() { return StreamIteratorEnd{}; } + + private: + std::ifstream &file_; +}; + bool SwiftRunner::ProcessPossibleResponseFile( const std::string &arg, std::function consumer) { auto path = arg.substr(1); std::ifstream original_file(path); + ArgsFile args_file(original_file); + // If we couldn't open it, maybe it's not a file; maybe it's just some other - // argument that starts with "@". (Unlikely, but it's safer to check.) + // argument that starts with "@" such as "@loader_path/..." if (!original_file.good()) { consumer(arg); return false; } + // Read the file to a vector to prevent double I/O + auto args = ParseArguments(args_file); + // If we're forcing response files, process and send the arguments from this // file directly to the consumer; they'll all get written to the same response // file at the end of processing all the arguments. if (force_response_file_) { - std::string arg_from_file; - while (std::getline(original_file, arg_from_file)) { + for (auto it = args.begin(); it != args.end(); ++it) { // Arguments in response files might be quoted/escaped, so we need to // unescape them ourselves. - ProcessArgument(Unescape(arg_from_file), consumer); + ProcessArgument(it, Unescape(*it), consumer); } return true; } @@ -154,12 +238,10 @@ bool SwiftRunner::ProcessPossibleResponseFile( bool changed = false; std::string arg_from_file; std::vector new_args; - - while (std::getline(original_file, arg_from_file)) { - changed |= - ProcessArgument(arg_from_file, [&](const std::string &processed_arg) { - new_args.push_back(processed_arg); - }); + for (auto it = args.begin(); it != args.end(); ++it) { + changed |= ProcessArgument(it, *it, [&](const std::string &processed_arg) { + new_args.push_back(processed_arg); + }); } if (changed) { @@ -175,45 +257,114 @@ bool SwiftRunner::ProcessPossibleResponseFile( return changed; } +template bool SwiftRunner::ProcessArgument( - const std::string &arg, std::function consumer) { + Iterator &itr, const std::string &arg, + std::function consumer) { bool changed = false; - if (arg[0] == '@') { changed = ProcessPossibleResponseFile(arg, consumer); - } else if (arg == "-Xwrapped-swift=-debug-prefix-pwd-is-dot") { - // Get the actual current working directory (the workspace root), which we - // didn't know at analysis time. - consumer("-debug-prefix-map"); - consumer(GetCurrentDirectory() + "=."); - changed = true; - } else if (arg == "-Xwrapped-swift=-ephemeral-module-cache") { - // Create a temporary directory to hold the module cache, which will be - // deleted after compilation is finished. - auto module_cache_dir = TempDirectory::Create("swift_module_cache.XXXXXX"); - consumer("-module-cache-path"); - consumer(module_cache_dir->GetPath()); - temp_directories_.push_back(std::move(module_cache_dir)); - changed = true; - } else if (arg.find("-Xwrapped-swift=") == 0) { - // TODO(allevato): Report that an unknown wrapper arg was found and give the - // caller a way to exit gracefully. - changed = true; } else { - // Apply any other text substitutions needed in the argument (i.e., for - // Apple toolchains). - auto new_arg = arg; - // Bazel doesn't quote arguments in multi-line params files, so we need to - // ensure that our defensive quoting kicks in if an argument contains a - // space, even if no other changes would have been made. - changed = MakeSubstitutions(&new_arg, bazel_placeholder_substitutions_) || - new_arg.find_first_of(' ') != std::string::npos; - consumer(new_arg); + std::string new_arg = arg; + if (StripPrefix("-Xwrapped-swift=", new_arg)) { + if (new_arg == "-debug-prefix-pwd-is-dot") { + // Get the actual current working directory (the workspace root), which + // we didn't know at analysis time. + consumer("-debug-prefix-map"); + consumer(GetCurrentDirectory() + "=."); + changed = true; + } else if (new_arg == "-coverage-prefix-pwd-is-dot") { + // Get the actual current working directory (the workspace root), which + // we didn't know at analysis time. + consumer("-coverage-prefix-map"); + consumer(GetCurrentDirectory() + "=."); + changed = true; + } else if (new_arg == "-ephemeral-module-cache") { + // Create a temporary directory to hold the module cache, which will be + // deleted after compilation is finished. + auto module_cache_dir = + TempDirectory::Create("swift_module_cache.XXXXXX"); + consumer("-module-cache-path"); + consumer(module_cache_dir->GetPath()); + temp_directories_.push_back(std::move(module_cache_dir)); + changed = true; + } else if (StripPrefix("-generated-header-rewriter=", new_arg)) { + changed = true; + } else if (StripPrefix("-global-index-store-import-path=", new_arg)) { + changed = true; + } else { + // TODO(allevato): Report that an unknown wrapper arg was found and give + // the caller a way to exit gracefully. + changed = true; + } + } else { + // Process default arguments + if (arg == "-index-store-path") { + consumer("-index-store-path"); + ++itr; + + // If there was a global index store set, pass that to swiftc. + // Otherwise, pass the users. We later copy index data onto the users. + if (global_index_store_import_path_ != "") { + new_arg = global_index_store_import_path_; + } else { + new_arg = index_store_path_; + } + changed = true; + } else if (arg == "-output-file-map") { + // Save the output file map to the value proceeding + // `-output-file-map` + consumer("-output-file-map"); + ++itr; + new_arg = output_file_map_path_; + changed = true; + } + + // Apply any other text substitutions needed in the argument (i.e., for + // Apple toolchains). + // + // Bazel doesn't quote arguments in multi-line params files, so we need to + // ensure that our defensive quoting kicks in if an argument contains a + // space, even if no other changes would have been made. + changed = bazel_placeholder_substitutions_.Apply(new_arg) || + new_arg.find_first_of(' ') != std::string::npos; + consumer(new_arg); + } } return changed; } +template +std::vector SwiftRunner::ParseArguments(Iterator itr) { + std::vector out_args; + for (auto it = itr.begin(); it != itr.end(); ++it) { + auto arg = *it; + out_args.push_back(arg); + + if (StripPrefix("-Xwrapped-swift=", arg)) { + if (StripPrefix("-global-index-store-import-path=", arg)) { + global_index_store_import_path_ = arg; + } else if (StripPrefix("-generated-header-rewriter=", arg)) { + generated_header_rewriter_path_ = arg; + } + } else { + if (arg == "-output-file-map") { + ++it; + arg = *it; + output_file_map_path_ = arg; + out_args.push_back(arg); + } else if (arg == "-index-store-path") { + ++it; + arg = *it; + index_store_path_ = arg; + out_args.push_back(arg); + } + } + } + return out_args; +} + std::vector SwiftRunner::ProcessArguments( const std::vector &args) { std::vector new_args; @@ -226,16 +377,19 @@ std::vector SwiftRunner::ProcessArguments( #endif // The tool is assumed to be the first argument. Push it directly. - auto it = args.begin(); + auto parsed_args = ParseArguments(args); + + auto it = parsed_args.begin(); new_args.push_back(*it++); // If we're forcing response files, push the remaining processed args onto a // different vector that we write out below. If not, push them directly onto // the vector being returned. auto &args_destination = force_response_file_ ? response_file_args : new_args; - while (it != args.end()) { - ProcessArgument( - *it, [&](const std::string &arg) { args_destination.push_back(arg); }); + while (it != parsed_args.end()) { + ProcessArgument(it, *it, [&](const std::string &arg) { + args_destination.push_back(arg); + }); ++it; } diff --git a/tools/worker/swift_runner.h b/tools/worker/swift_runner.h index 55a4ba31b..ad5389576 100644 --- a/tools/worker/swift_runner.h +++ b/tools/worker/swift_runner.h @@ -22,6 +22,7 @@ #include #include +#include "tools/common/bazel_substitutions.h" #include "tools/common/temp_file.h" // Handles spawning the Swift compiler driver, making any required substitutions @@ -96,9 +97,15 @@ class SwiftRunner { // // This method has file system side effects, creating temporary files and // directories as needed for a particular substitution. - bool ProcessArgument(const std::string &arg, + template + bool ProcessArgument(Iterator &itr, const std::string &arg, std::function consumer); + // Parses arguments to ivars and returns a vector of strings from the iterator. + // This method doesn't actually mutate any of the arguments. + template + std::vector ParseArguments(Iterator itr); + // Applies substitutions to the given command line arguments, returning the // results in a new vector. std::vector ProcessArguments( @@ -106,7 +113,8 @@ class SwiftRunner { // A mapping of Bazel placeholder strings to the actual paths that should be // substituted for them. Supports Xcode resolution on Apple OSes. - std::map bazel_placeholder_substitutions_; + bazel_rules_swift::BazelPlaceholderSubstitutions + bazel_placeholder_substitutions_; // The arguments, post-substitution, passed to the spawner. std::vector args_; @@ -122,6 +130,22 @@ class SwiftRunner { // Arguments will be unconditionally written into a response file and passed // to the tool that way. bool force_response_file_; + + // The path to the generated header rewriter tool, if one is being used for + // this compilation. + std::string generated_header_rewriter_path_; + + // The path of the output map file + std::string output_file_map_path_; + + // The index store path argument passed to the runner + std::string index_store_path_; + + // The path of the global index store when using + // swift.use_global_index_store. When set, this is passed to `swiftc` as the + // `-index-store-path`. After running `swiftc` `index-import` copies relevant + // index outputs into the `index_store_path` to integrate outputs with Bazel. + std::string global_index_store_import_path_; }; #endif // BUILD_BAZEL_RULES_SWIFT_TOOLS_WORKER_SWIFT_RUNNER_H_ diff --git a/tools/worker/work_processor.cc b/tools/worker/work_processor.cc index 2a91764b6..6e4c8fdb1 100644 --- a/tools/worker/work_processor.cc +++ b/tools/worker/work_processor.cc @@ -12,23 +12,21 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "tools/worker/work_processor.h" - +#include #include #include #include +#include #include #include -#include #include "tools/common/file_system.h" #include "tools/common/path_utils.h" -#include "tools/common/string_utils.h" #include "tools/common/temp_file.h" #include "tools/worker/output_file_map.h" #include "tools/worker/swift_runner.h" -#include +#include "tools/worker/work_processor.h" namespace { @@ -39,6 +37,15 @@ static bool ArgumentEnablesWMO(const std::string &arg) { arg == "-force-single-frontend-invocation"; } +static void FinalizeWorkRequest(const blaze::worker::WorkRequest &request, + blaze::worker::WorkResponse *response, + int exit_code, + const std::ostringstream &output) { + response->set_exit_code(exit_code); + response->set_output(output.str()); + response->set_request_id(request.request_id()); +} + }; // end namespace WorkProcessor::WorkProcessor(const std::vector &args) { @@ -72,7 +79,6 @@ void WorkProcessor::ProcessWorkRequest( arg.clear(); } else if (prev_arg == "-output-file-map") { output_file_map_path = arg; - output_file_map.ReadFromPath(output_file_map_path); arg.clear(); } else if (ArgumentEnablesWMO(arg)) { is_wmo = true; @@ -87,6 +93,8 @@ void WorkProcessor::ProcessWorkRequest( if (!output_file_map_path.empty()) { if (!is_wmo) { + output_file_map.ReadFromPath(output_file_map_path); + // Rewrite the output file map to use the incremental storage area and // pass the compiler the path to the rewritten file. auto new_path = @@ -111,6 +119,8 @@ void WorkProcessor::ProcessWorkRequest( processed_args.push_back("@" + params_file->GetPath()); params_file_stream.close(); + std::ostringstream stderr_stream; + if (!is_wmo) { for (const auto &expected_object_pair : output_file_map.incremental_outputs()) { @@ -119,15 +129,32 @@ void WorkProcessor::ProcessWorkRequest( // incremental storage area. auto dir_path = Dirname(expected_object_pair.second); if (!MakeDirs(dir_path, S_IRWXU)) { - std::cerr << "Could not create directory " << dir_path << " (errno " - << errno << ")\n"; + stderr_stream << "swift_worker: Could not create directory " << dir_path + << " (errno " << errno << ")\n"; + FinalizeWorkRequest(request, response, EXIT_FAILURE, stderr_stream); + return; + } + } + + // Copy some input files from the incremental storage area to the locations + // where Bazel will generate them. + for (const auto &expected_object_pair : + output_file_map.incremental_inputs()) { + if (FileExists(expected_object_pair.second)) { + if (!CopyFile(expected_object_pair.second, + expected_object_pair.first)) { + stderr_stream << "swift_worker: Could not copy " + << expected_object_pair.second << " to " + << expected_object_pair.first << " (errno " << errno + << ")\n"; + FinalizeWorkRequest(request, response, EXIT_FAILURE, stderr_stream); + return; + } } } } - std::ostringstream stderr_stream; SwiftRunner swift_runner(processed_args, /*force_response_file=*/true); - int exit_code = swift_runner.Run(&stderr_stream, /*stdout_to_stderr=*/true); if (!is_wmo) { @@ -136,13 +163,36 @@ void WorkProcessor::ProcessWorkRequest( for (const auto &expected_object_pair : output_file_map.incremental_outputs()) { if (!CopyFile(expected_object_pair.second, expected_object_pair.first)) { - std::cerr << "Could not copy " << expected_object_pair.second << " to " - << expected_object_pair.first << " (errno " << errno << ")\n"; - exit_code = EXIT_FAILURE; + stderr_stream << "swift_worker: Could not copy " + << expected_object_pair.second << " to " + << expected_object_pair.first << " (errno " << errno + << ")\n"; + FinalizeWorkRequest(request, response, EXIT_FAILURE, stderr_stream); + return; + } + } + + // Copy the replaced input files back to the incremental storage for the + // next run. + for (const auto &expected_object_pair : + output_file_map.incremental_inputs()) { + if (FileExists(expected_object_pair.first)) { + if (FileExists(expected_object_pair.second)) { + // CopyFile fails if the file already exists + RemoveFile(expected_object_pair.second); + } + if (!CopyFile(expected_object_pair.first, + expected_object_pair.second)) { + stderr_stream << "swift_worker: Could not copy " + << expected_object_pair.first << " to " + << expected_object_pair.second << " (errno " << errno + << ")\n"; + FinalizeWorkRequest(request, response, EXIT_FAILURE, stderr_stream); + return; + } } } } - response->set_exit_code(exit_code); - response->set_output(stderr_stream.str()); + FinalizeWorkRequest(request, response, exit_code, stderr_stream); }