From e76d714c98ad2b621bd571faeefb836d263fd460 Mon Sep 17 00:00:00 2001 From: Brentley Jones Date: Mon, 21 Oct 2024 16:44:34 -0500 Subject: [PATCH 01/92] Re-apply reverted cherry-picks (#1415) Re-applying ee8b483e5fe887fd49429bb5503f882748749f9e and a0392b3a1b1e84f0c4244eac5cbc7460bdb1e2c5. Signed-off-by: Brentley Jones --- doc/api.md | 355 ---------------------------------- proto/BUILD | 1 - proto/swift_proto_library.bzl | 9 +- swift/BUILD | 4 - swift/swift_common.bzl | 36 +--- 5 files changed, 7 insertions(+), 398 deletions(-) diff --git a/doc/api.md b/doc/api.md index 0054bfc2c..756be148a 100644 --- a/doc/api.md +++ b/doc/api.md @@ -4,51 +4,6 @@ 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. - - -## swift_common.create_swift_info - -
-swift_common.create_swift_info(direct_modules, transitive_modules)
-
- -Contains information about the compiled artifacts of a Swift module. - -This provider has a custom initializer that will merge the transitive modules of -a list of `SwiftInfo` providers, rather than a separate "merge" function. The -correct signature when _creating_ a new `SwiftInfo` provider is the following: - -```build -SwiftInfo( - direct_swift_infos, - modules, - swift_infos, -) -``` - -where the arguments are: - -* `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 `create_swift_module_context`) - 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. - -When reading an existing `SwiftInfo` provider, it has the two fields described -below. - -**FIELDS** - - -| Name | Description | -| :------------- | :------------- | -| direct_modules | `List` of values returned from `create_swift_module_context`. The modules (both Swift and C/Objective-C) emitted by the library that propagated this provider. | -| transitive_modules | `Depset` of values returned from `create_swift_module_context`. The transitive modules (both Swift and C/Objective-C) emitted by the library that propagated this provider and all of its dependencies. | - - ## swift_common.cc_feature_configuration @@ -73,59 +28,6 @@ A C++ `FeatureConfiguration` value (see for more information). - - -## swift_common.compilation_attrs - -
-swift_common.compilation_attrs(additional_deps_aspects, additional_deps_providers,
-                               include_dev_srch_paths_attrib, requires_srcs)
-
- -Returns an attribute dictionary for rules that compile Swift code. - -The returned dictionary contains the subset of attributes that are shared by -the `swift_binary`, `swift_library`, and `swift_test` rules that deal with -inputs and options for compilation. Users who are authoring custom rules -that compile Swift code but not as a library can add this dictionary to -their own rule's attributes to give it a familiar API. - -Do note, however, that it is the responsibility of the rule implementation -to retrieve the values of those attributes and pass them correctly to the -other `swift_common` APIs. - -There is a hierarchy to the attribute sets offered by the `swift_common` -API: - -1. If you only need access to the toolchain for its tools and libraries but - are not doing any compilation, use `toolchain_attrs`. -2. If you need to invoke compilation actions but are not making the - resulting object files into a static or shared library, use - `compilation_attrs`. -3. If you want to provide a rule interface that is suitable as a drop-in - replacement for `swift_library`, use `library_rule_attrs`. - -Each of the attribute functions in the list above also contains the -attributes from the earlier items in the list. - - -**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. | `[]` | -| additional_deps_providers | A list of lists representing additional providers that should be allowed by the `deps` attribute of the rule. | `[]` | -| include_dev_srch_paths_attrib | A `bool` that indicates whether to include the `always_include_developer_search_paths` attribute. | `False` | -| 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`. - - ## swift_common.compile @@ -285,32 +187,6 @@ An opaque value representing the feature configuration that can be 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,
-                                 strict_includes)
-
- -Creates a value representing a Clang module used as a Swift dependency. - -**PARAMETERS** - - -| Name | Description | Default Value | -| :------------- | :------------- | :------------- | -| compilation_context | A `CcCompilationContext` that contains the header files and other context (such as include paths, preprocessor defines, and so forth) needed to compile this module as an explicit 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` | -| strict_includes | A `depset` of strings representing additional Clang include paths that should be passed to the compiler when this module is a _direct_ dependency of the module being compiled. May be `None`. **This field only exists to support a specific legacy use case and should otherwise not be used, as it is fundamentally incompatible with Swift's import model.** | `None` | - -**RETURNS** - -A `struct` containing the values provided as arguments. - - ## swift_common.create_compilation_context @@ -393,186 +269,6 @@ A tuple of `(CcLinkingContext, CcLinkingOutputs)` containing the linking artifact representing the library that was linked, respectively. - - -## swift_common.create_module - -
-swift_common.create_module(name, clang, const_gather_protocols, compilation_context, is_framework,
-                           is_system, swift)
-
- -Creates a value containing Clang/Swift module artifacts of a dependency. - -It is possible for both `clang` and `swift` to be present; this is the case -for Swift modules that generate an Objective-C header, where the Swift -module artifacts are propagated in the `swift` context and the generated -header and module map are propagated in the `clang` context. - -Though rare, it is also permitted for both the `clang` and `swift` arguments -to be `None`. One example of how this can be used is to model system -dependencies (like Apple SDK frameworks) that are implicitly available as -part of a non-hermetic SDK (Xcode) but do not propagate any artifacts of -their own. This would only apply in a build using implicit modules, however; -when using explicit modules, one would propagate the module artifacts -explicitly. But allowing for the empty case keeps the build graph consistent -if switching between the two modes is necessary, since it will not change -the set of transitive module names that are propagated by dependencies -(which other build rules may want to depend on for their own analysis). - - -**PARAMETERS** - - -| Name | Description | Default Value | -| :------------- | :------------- | :------------- | -| name | The name of the module. | none | -| clang | A value returned by `create_clang_module_inputs` 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` | -| const_gather_protocols | A list of protocol names from which constant values should be extracted from source code that takes this module as a *direct* dependency. | `[]` | -| compilation_context | A value returned from `swift_common.create_compilation_context` that contains the context needed to compile the module being built. This may be `None` if the module wasn't compiled from sources. | `None` | -| is_framework | Indictates whether the module is a framework module. The default value is `False`. | `False` | -| 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 `create_swift_module_inputs` 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 given values provided as arguments. - - - - -## swift_common.create_swift_interop_info - -
-swift_common.create_swift_interop_info(exclude_headers, module_map, module_name, requested_features,
-                                       suppressed, 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 do the following: - -* Add `swift_clang_module_aspect` to any attribute that provides - dependencies of the code that needs to interop with Swift (typically - `deps`, but could be other attributes as well, such as attributes - providing additional support libraries). -* Have the rule implementation call `create_swift_interop_info`, passing - it only the list of `SwiftInfo` providers from its dependencies. This - tells `swift_clang_module_aspect` when it runs on *this* rule's target - to derive the module name from the target label and create a module map - using the headers from the compilation context of the `CcInfo` you - propagate. - -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** collect dependency providers automatically. This allows the -rule to not only add extra dependencies (such as support libraries from -implicit attributes) but also to exclude dependencies if necessary. - - -**PARAMETERS** - - -| Name | Description | Default Value | -| :------------- | :------------- | :------------- | -| exclude_headers | A `list` of `File`s representing headers that should be excluded from the module if the module map is generated. | `[]` | -| 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. | `[]` | -| suppressed | A `bool` indicating whether the module that the aspect would create for the target should instead be suppressed. | `False` | -| 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(ast_files, const_protocols_to_gather, defines, generated_header,
-                                 indexstore, original_module_name, plugins, private_swiftinterface,
-                                 swiftdoc, swiftinterface, swiftmodule, swiftsourceinfo)
-
- -Creates a value representing a Swift module use as a Swift dependency. - -**PARAMETERS** - - -| Name | Description | Default Value | -| :------------- | :------------- | :------------- | -| ast_files | A list of `File`s output from the `DUMP_AST` action. | `[]` | -| const_protocols_to_gather | A list of protocol names from which constant values should be extracted from source code that takes this module as a *direct* dependency. | `[]` | -| 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. | `[]` | -| generated_header | A `File` representing the Swift generated header. | `None` | -| indexstore | A `File` representing the directory that contains the index store data generated by the compiler if the `"swift.index_while_building"` feature is enabled, otherwise this will be `None`. | `None` | -| original_module_name | The original name of the module if it was changed by a module mapping; otherwise, `None`. | `None` | -| plugins | A list of `SwiftCompilerPluginInfo` providers representing compiler plugins that are required by this module and should be loaded by the compiler when this module is directly depended on. | `[]` | -| private_swiftinterface | The `.private.swiftinterface` file emitted by the compiler for this module. May be `None` if no private module interface file was emitted. | `None` | -| swiftdoc | The `.swiftdoc` file emitted by the compiler for this module. | none | -| swiftinterface | The `.swiftinterface` file emitted by the compiler for this module. May be `None` if no module interface file was emitted. | `None` | -| swiftmodule | The `.swiftmodule` file emitted by the compiler for this module. | none | -| swiftsourceinfo | The `.swiftsourceinfo` file emitted by the compiler for this module. May be `None` if no source info file was emitted. | `None` | - -**RETURNS** - -A `struct` containing the values provided as arguments. - - - - -## swift_common.derive_module_name - -
-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 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.extract_symbol_graph @@ -656,57 +352,6 @@ check it. `True` if the given feature is enabled in the feature configuration. - - -## swift_common.library_rule_attrs - -
-swift_common.library_rule_attrs(additional_deps_aspects, requires_srcs)
-
- -Returns an attribute dictionary for `swift_library`-like rules. - -The returned dictionary contains the same attributes that are defined by the -`swift_library` rule (including the private `_toolchain` attribute that -specifies the toolchain dependency). Users who are authoring custom rules -can use this dictionary verbatim or add other custom attributes to it in -order to make their rule a drop-in replacement for `swift_library` (for -example, if writing a custom rule that does some preprocessing or generation -of sources and then compiles them). - -Do note, however, that it is the responsibility of the rule implementation -to retrieve the values of those attributes and pass them correctly to the -other `swift_common` APIs. - -There is a hierarchy to the attribute sets offered by the `swift_common` -API: - -1. If you only need access to the toolchain for its tools and libraries but - are not doing any compilation, use `toolchain_attrs`. -2. If you need to invoke compilation actions but are not making the - resulting object files into a static or shared library, use - `compilation_attrs`. -3. If you want to provide a rule interface that is suitable as a drop-in - replacement for `swift_library`, use `library_rule_attrs`. - -Each of the attribute functions in the list above also contains the -attributes from the earlier items in the list. - - -**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`. - - ## swift_common.precompile_clang_module diff --git a/proto/BUILD b/proto/BUILD index 9576bf187..17555d5dc 100644 --- a/proto/BUILD +++ b/proto/BUILD @@ -35,7 +35,6 @@ bzl_library( ":swift_proto_utils", "//swift:module_name", "//swift:swift_clang_module_aspect", - "//swift:swift_common", "//swift/internal:attrs", "//swift/internal:toolchain_utils", "//swift/internal:utils", diff --git a/proto/swift_proto_library.bzl b/proto/swift_proto_library.bzl index f90fd600a..c8bc6e047 100644 --- a/proto/swift_proto_library.bzl +++ b/proto/swift_proto_library.bzl @@ -27,10 +27,13 @@ load( load("//swift:module_name.bzl", "derive_swift_module_name") load("//swift:providers.bzl", "SwiftProtoCompilerInfo") load("//swift:swift_clang_module_aspect.bzl", "swift_clang_module_aspect") -load("//swift:swift_common.bzl", "swift_common") # buildifier: disable=bzl-visibility -load("//swift/internal:attrs.bzl", "swift_deps_attr") +load( + "//swift/internal:attrs.bzl", + "swift_deps_attr", + "swift_library_rule_attrs", +) # buildifier: disable=bzl-visibility load("//swift/internal:toolchain_utils.bzl", "use_swift_toolchain") @@ -106,7 +109,7 @@ def _swift_proto_library_impl(ctx): swift_proto_library = rule( attrs = dicts.add( - swift_common.library_rule_attrs( + swift_library_rule_attrs( additional_deps_aspects = [ swift_clang_module_aspect, ], diff --git a/swift/BUILD b/swift/BUILD index 8925c515b..0239353f5 100644 --- a/swift/BUILD +++ b/swift/BUILD @@ -91,14 +91,10 @@ bzl_library( name = "swift_common", srcs = ["swift_common.bzl"], deps = [ - ":module_name", - ":providers", - ":swift_interop_info", "//swift/internal:attrs", "//swift/internal:compiling", "//swift/internal:features", "//swift/internal:linking", - "//swift/internal:swift_interop_info", "//swift/internal:symbol_graph_extracting", "//swift/internal:toolchain_utils", ], diff --git a/swift/swift_common.bzl b/swift/swift_common.bzl index 8d95a3b96..aec38dd6b 100644 --- a/swift/swift_common.bzl +++ b/swift/swift_common.bzl @@ -20,12 +20,7 @@ example, `swift_proto_library` generates Swift source code from `.proto` files and then needs to compile them. This module provides that lower-level interface. """ -load( - "//swift/internal:attrs.bzl", - "swift_compilation_attrs", - "swift_library_rule_attrs", - "swift_toolchain_attrs", -) +load("//swift/internal:attrs.bzl", "swift_toolchain_attrs") load( "//swift/internal:compiling.bzl", "compile", @@ -52,48 +47,19 @@ load( "get_swift_toolchain", "use_swift_toolchain", ) -load(":module_name.bzl", "derive_swift_module_name") -load( - ":providers.bzl", - "SwiftInfo", - "create_clang_module_inputs", - "create_swift_module_context", - "create_swift_module_inputs", -) -load(":swift_interop_info.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. swift_common = struct( cc_feature_configuration = get_cc_feature_configuration, - compilation_attrs = swift_compilation_attrs, compile = compile, compile_module_interface = compile_module_interface, configure_features = configure_features, - # TODO(b/261445197): Remove this after everyone is migrated to the free - # function. - create_clang_module = create_clang_module_inputs, create_compilation_context = create_compilation_context, create_linking_context_from_compilation_outputs = create_linking_context_from_compilation_outputs, - # TODO(b/261445197): Remove this after everyone is migrated to the free - # function. - create_module = create_swift_module_context, - # TODO(b/261445197): Remove this after everyone is migrated to the free - # function. - create_swift_info = SwiftInfo, - # TODO(b/261445197): Remove this after everyone is migrated to the free - # function. - create_swift_interop_info = create_swift_interop_info, - # TODO(b/261445197): Remove this after everyone is migrated to the free - # function. - create_swift_module = create_swift_module_inputs, - # TODO(b/261444771): Remove this after everyone is migrated to the free - # function. - derive_module_name = derive_swift_module_name, extract_symbol_graph = extract_symbol_graph, get_toolchain = get_swift_toolchain, is_enabled = is_feature_enabled, - library_rule_attrs = swift_library_rule_attrs, precompile_clang_module = precompile_clang_module, toolchain_attrs = swift_toolchain_attrs, use_toolchain = use_swift_toolchain, From 0702028ff2738f0bab26f8542b4705c8759bcf30 Mon Sep 17 00:00:00 2001 From: Brentley Jones Date: Mon, 21 Oct 2024 16:50:37 -0500 Subject: [PATCH 02/92] Stop running macro tests on BCR presubmit (#1421) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We can’t depend on dev dependences in these tests. Signed-off-by: Brentley Jones --- .bcr/presubmit.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.bcr/presubmit.yml b/.bcr/presubmit.yml index 9eee45986..5c1ab28d3 100644 --- a/.bcr/presubmit.yml +++ b/.bcr/presubmit.yml @@ -24,6 +24,7 @@ tasks: - "--action_env=PATH" build_targets: - "@rules_swift//examples/xplatform/..." + - "-@rules_swift//examples/xplatform/macros/..." # Has a dev dependency - "-@rules_swift//examples/xplatform/grpc/..." # TODO: Fix grpc on Linux - "-@rules_swift//examples/xplatform/proto_library_group/..." # TODO: Fix grpc on Linux verify_targets_macos: From 3ce0160b2713a66ac84c501e1fd41e33f7d3953c Mon Sep 17 00:00:00 2001 From: Brentley Jones Date: Mon, 21 Oct 2024 16:51:23 -0500 Subject: [PATCH 03/92] Drop support for Bazel 6 (#1422) Signed-off-by: Brentley Jones --- .bazelci/presubmit.yml | 32 ++++++++++++++------------------ README.md | 2 +- 2 files changed, 15 insertions(+), 19 deletions(-) diff --git a/.bazelci/presubmit.yml b/.bazelci/presubmit.yml index fd663d542..5daf6508e 100644 --- a/.bazelci/presubmit.yml +++ b/.bazelci/presubmit.yml @@ -48,16 +48,11 @@ x_defaults: - "//tools/..." tasks: - macos_6: - name: "6.x LTS" - bazel: 6.x - <<: *mac_common - build_flags: - # Bazel 6.x has dependencies that produce warnings now - - "--features=-treat_warnings_as_errors" - test_flags: - # Bazel 6.x has dependencies that produce warnings now - - "--features=-treat_warnings_as_errors" + # TODO: Uncomment once Bazel 8 is released + # macos_7: + # name: "Previous LTS" + # bazel: 7.x + # <<: *mac_common macos_latest: name: "Current LTS" @@ -83,14 +78,15 @@ tasks: - .bazelci/update_workspace_to_deps_heads.sh <<: *mac_common - ubuntu2004_lts: - name: "Previous LTS" - bazel: latest-1 - shell_commands: - - "echo --- Downloading and extracting Swift $SWIFT_VERSION to $SWIFT_HOME" - - "mkdir $SWIFT_HOME" - - "curl https://download.swift.org/swift-${SWIFT_VERSION}-release/ubuntu2004/swift-${SWIFT_VERSION}-RELEASE/swift-${SWIFT_VERSION}-RELEASE-ubuntu20.04.tar.gz | tar xvz --strip-components=1 -C $SWIFT_HOME" - <<: *linux_common + # TODO: Uncomment once Bazel 8 is released + # ubuntu2004_7: + # name: "Previous LTS" + # bazel: 7.x + # shell_commands: + # - "echo --- Downloading and extracting Swift $SWIFT_VERSION to $SWIFT_HOME" + # - "mkdir $SWIFT_HOME" + # - "curl https://download.swift.org/swift-${SWIFT_VERSION}-release/ubuntu2004/swift-${SWIFT_VERSION}-RELEASE/swift-${SWIFT_VERSION}-RELEASE-ubuntu20.04.tar.gz | tar xvz --strip-components=1 -C $SWIFT_HOME" + # <<: *linux_common ubuntu2004_latest: name: "Current LTS" diff --git a/README.md b/README.md index b20618a47..def30fa64 100644 --- a/README.md +++ b/README.md @@ -126,7 +126,7 @@ as best as we can since the 1.0.0 release. |:-------------------:|:-------------------:|:-------------------------:| | 8.x (most recent rolling) | 0.27.0 | current | | 7.x | 0.27.0 | current | -| 6.x | 0.27.0 | current | +| 6.x | 0.27.0 | 2.2.0 | | 5.x | 0.25.0 | 1.14.0 | | 4.x | 0.19.0 | 0.24.0 | | 3.x | 0.14.0 | 0.18.0 | From 0179dac04e4e58045f77562775e9424b9f9ad5c1 Mon Sep 17 00:00:00 2001 From: Tony Allevato Date: Wed, 11 Jan 2023 07:32:00 -0800 Subject: [PATCH 04/92] Add execution groups for actions that don't have to use the same toolchain used when building the target Once we migrate to platforms/toolchains, this will avoid the default behavior where a toolchain's `exec_compatible_with` determines the action routing for all actions under that rule. PiperOrigin-RevId: 501277236 (cherry picked from commit 11e5b45a3cff3ec240bb9fbbabb90d6572c86e54) Signed-off-by: Brentley Jones --- swift/swift_test.bzl | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/swift/swift_test.bzl b/swift/swift_test.bzl index 4beaccc27..0a3b6e7c7 100644 --- a/swift/swift_test.bzl +++ b/swift/swift_test.bzl @@ -60,6 +60,9 @@ load( "create_swift_module_context", ) +# Name of the execution group used for `SwiftTestDiscovery` actions. +_DISCOVER_TESTS_EXEC_GROUP = "discover_tests" + _test_discovery_symbol_graph_aspect = make_swift_symbol_graph_aspect( default_emit_extension_block_symbols = "0", default_minimum_access_level = "internal", @@ -251,6 +254,7 @@ def _generate_test_discovery_srcs( actions.run( arguments = [args], executable = test_discoverer, + exec_group = _DISCOVER_TESTS_EXEC_GROUP, inputs = inputs, mnemonic = "SwiftTestDiscovery", outputs = outputs, @@ -663,7 +667,7 @@ standard executable binary that is invoked directly. ), ), "_test_discoverer": attr.label( - cfg = "exec", + cfg = config.exec(_DISCOVER_TESTS_EXEC_GROUP), default = Label("//tools/test_discoverer"), executable = True, ), @@ -738,6 +742,13 @@ Multiple such filters can be separated by commas. For example: bazel test --test_filter=AModule,BModule.SomeTests,BModule.OtherTests/testX //my/package/... ``` """, + exec_groups = { + # Define an execution group for `SwiftTestDiscovery` actions that does + # not have constraints, so that test discovery using the already + # generated symbol graphs can be routed to any platform that supports it + # (even one with a different toolchain). + _DISCOVER_TESTS_EXEC_GROUP: exec_group(), + }, executable = True, fragments = ["cpp"], test = True, From 0d8c253723ab7bbe486af1d9cc9c08c0d51292b3 Mon Sep 17 00:00:00 2001 From: Brentley Jones Date: Tue, 22 Oct 2024 08:15:11 -0500 Subject: [PATCH 05/92] Upgrade protobuf for dev and allow empty globs to fix HEAD CI (#1420) Signed-off-by: Brentley Jones --- .bazelci/presubmit.yml | 4 ++++ MODULE.bazel | 17 +++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/.bazelci/presubmit.yml b/.bazelci/presubmit.yml index 5daf6508e..bdd1b662e 100644 --- a/.bazelci/presubmit.yml +++ b/.bazelci/presubmit.yml @@ -134,5 +134,9 @@ tasks: - "curl https://download.swift.org/swift-${SWIFT_VERSION}-release/ubuntu2004/swift-${SWIFT_VERSION}-RELEASE/swift-${SWIFT_VERSION}-RELEASE-ubuntu20.04.tar.gz | tar xvz --strip-components=1 -C $SWIFT_HOME" test_targets: - "doc/..." + # TODO: Remove this once stardoc is updated to 0.7.1+ (https://github.com/bazelbuild/stardoc/pull/238) + test_flags: + - "--action_env=PATH" + - "--noincompatible_disallow_empty_glob" buildifier: 6.3.2 diff --git a/MODULE.bazel b/MODULE.bazel index 3e6fd2f15..ba159bfa3 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -54,3 +54,20 @@ http_archive( strip_prefix = "swift-syntax-01fc3e3ed4d26121c06790abf8fe5ddaa22a4cc5", url = "https://github.com/apple/swift-syntax/archive/01fc3e3ed4d26121c06790abf8fe5ddaa22a4cc5.tar.gz", ) + +# TODO: Remove override when a protobuf release is available that supports +# Bazel 8 +archive_override( + module_name = "protobuf", + integrity = "sha256-+dloYVexGlGsxKLTARuU4KXZ5ORo/BWPR6obFk73d+Q=", + strip_prefix = "protobuf-b93b8e5f64ed922d101759380d7c6a2bbe474e26", + urls = ["https://github.com/protocolbuffers/protobuf/archive/b93b8e5f64ed922d101759380d7c6a2bbe474e26.zip"], +) + +# TODO: Remove override when a protobuf release that marks `stardoc` as a +# dev_dependency is available, until then it's upgrading our stardoc version +# so override it here. +single_version_override( + module_name = "stardoc", + version = "0.6.2", +) From e831c3a7f80f097bafdd830a57e1a08b9442b675 Mon Sep 17 00:00:00 2001 From: Brentley Jones Date: Tue, 22 Oct 2024 08:18:37 -0500 Subject: [PATCH 06/92] Fix `build_file` labels (#1425) Regressed for WORKSPACE users in 7fafc9bc47e80ca9fb08affeb44d135818c8c8db. --------- Signed-off-by: Brentley Jones --- swift/repositories.bzl | 54 +++++++++++++++++++++++++++++++----------- 1 file changed, 40 insertions(+), 14 deletions(-) diff --git a/swift/repositories.bzl b/swift/repositories.bzl index 5301fb8e3..bcbad3974 100644 --- a/swift/repositories.bzl +++ b/swift/repositories.bzl @@ -79,7 +79,9 @@ def swift_rules_dependencies(include_bzlmod_ready_dependencies = True): ], sha256 = "69cc88207ce91347ea530b227ff0776db82dcb8de6704e1a3d74f4841bc651cf", type = "zip", - build_file = "//third_party:com_github_nlohmann_json/BUILD.overlay", + build_file = Label( + "//third_party:com_github_nlohmann_json/BUILD.overlay", + ), ) _maybe( @@ -96,7 +98,9 @@ def swift_rules_dependencies(include_bzlmod_ready_dependencies = True): urls = ["https://github.com/apple/swift-argument-parser/archive/refs/tags/1.3.0.tar.gz"], sha256 = "e5010ff37b542807346927ba68b7f06365a53cf49d36a6df13cef50d86018204", strip_prefix = "swift-argument-parser-1.3.0", - build_file = "//third_party:com_github_apple_swift_argument_parser/BUILD.overlay", + build_file = Label( + "//third_party:com_github_apple_swift_argument_parser/BUILD.overlay", + ), ) _maybe( @@ -105,7 +109,9 @@ def swift_rules_dependencies(include_bzlmod_ready_dependencies = True): urls = ["https://github.com/apple/swift-protobuf/archive/1.20.2.tar.gz"], # pinned to grpc-swift version sha256 = "3fb50bd4d293337f202d917b6ada22f9548a0a0aed9d9a4d791e6fbd8a246ebb", strip_prefix = "swift-protobuf-1.20.2/", - build_file = "//third_party:com_github_apple_swift_protobuf/BUILD.overlay", + build_file = Label( + "//third_party:com_github_apple_swift_protobuf/BUILD.overlay", + ), ) _maybe( @@ -114,7 +120,9 @@ def swift_rules_dependencies(include_bzlmod_ready_dependencies = True): urls = ["https://github.com/grpc/grpc-swift/archive/1.16.0.tar.gz"], # latest at time of writing sha256 = "58b60431d0064969f9679411264b82e40a217ae6bd34e17096d92cc4e47556a5", strip_prefix = "grpc-swift-1.16.0/", - build_file = "//third_party:com_github_grpc_grpc_swift/BUILD.overlay", + build_file = Label( + "//third_party:com_github_grpc_grpc_swift/BUILD.overlay", + ), ) _maybe( @@ -123,7 +131,9 @@ def swift_rules_dependencies(include_bzlmod_ready_dependencies = True): urls = ["https://github.com/apple/swift-docc-symbolkit/archive/refs/tags/swift-5.10-RELEASE.tar.gz"], sha256 = "de1d4b6940468ddb53b89df7aa1a81323b9712775b0e33e8254fa0f6f7469a97", strip_prefix = "swift-docc-symbolkit-swift-5.10-RELEASE", - build_file = "//third_party:com_github_apple_swift_docc_symbolkit/BUILD.overlay", + build_file = Label( + "//third_party:com_github_apple_swift_docc_symbolkit/BUILD.overlay", + ), ) _maybe( @@ -132,7 +142,9 @@ def swift_rules_dependencies(include_bzlmod_ready_dependencies = True): urls = ["https://github.com/apple/swift-nio/archive/2.42.0.tar.gz"], # pinned to grpc swift version sha256 = "e3304bc3fb53aea74a3e54bd005ede11f6dc357117d9b1db642d03aea87194a0", strip_prefix = "swift-nio-2.42.0/", - build_file = "//third_party:com_github_apple_swift_nio/BUILD.overlay", + build_file = Label( + "//third_party:com_github_apple_swift_nio/BUILD.overlay", + ), ) _maybe( @@ -141,7 +153,9 @@ def swift_rules_dependencies(include_bzlmod_ready_dependencies = True): urls = ["https://github.com/apple/swift-nio-http2/archive/1.26.0.tar.gz"], # pinned to grpc-swift version sha256 = "f0edfc9d6a7be1d587e5b403f2d04264bdfae59aac1d74f7d974a9022c6d2b25", strip_prefix = "swift-nio-http2-1.26.0/", - build_file = "//third_party:com_github_apple_swift_nio_http2/BUILD.overlay", + build_file = Label( + "//third_party:com_github_apple_swift_nio_http2/BUILD.overlay", + ), ) _maybe( @@ -150,7 +164,9 @@ def swift_rules_dependencies(include_bzlmod_ready_dependencies = True): urls = ["https://github.com/apple/swift-nio-transport-services/archive/1.15.0.tar.gz"], # pinned to grpc-swift version sha256 = "f3498dafa633751a52b9b7f741f7ac30c42bcbeb3b9edca6d447e0da8e693262", strip_prefix = "swift-nio-transport-services-1.15.0/", - build_file = "//third_party:com_github_apple_swift_nio_transport_services/BUILD.overlay", + build_file = Label( + "//third_party:com_github_apple_swift_nio_transport_services/BUILD.overlay", + ), ) _maybe( @@ -159,7 +175,9 @@ def swift_rules_dependencies(include_bzlmod_ready_dependencies = True): urls = ["https://github.com/apple/swift-nio-extras/archive/1.4.0.tar.gz"], # pinned to grpc-swift version sha256 = "4684b52951d9d9937bb3e8ccd6b5daedd777021ef2519ea2f18c4c922843b52b", strip_prefix = "swift-nio-extras-1.4.0/", - build_file = "//third_party:com_github_apple_swift_nio_extras/BUILD.overlay", + build_file = Label( + "//third_party:com_github_apple_swift_nio_extras/BUILD.overlay", + ), ) _maybe( @@ -168,7 +186,9 @@ def swift_rules_dependencies(include_bzlmod_ready_dependencies = True): urls = ["https://github.com/apple/swift-log/archive/1.4.4.tar.gz"], # pinned to grpc-swift version sha256 = "48fe66426c784c0c20031f15dc17faf9f4c9037c192bfac2f643f65cb2321ba0", strip_prefix = "swift-log-1.4.4/", - build_file = "//third_party:com_github_apple_swift_log/BUILD.overlay", + build_file = Label( + "//third_party:com_github_apple_swift_log/BUILD.overlay", + ), ) _maybe( @@ -177,7 +197,9 @@ def swift_rules_dependencies(include_bzlmod_ready_dependencies = True): urls = ["https://github.com/apple/swift-nio-ssl/archive/2.23.0.tar.gz"], # pinned to grpc swift version sha256 = "4787c63f61dd04d99e498adc3d1a628193387e41efddf8de19b8db04544d016d", strip_prefix = "swift-nio-ssl-2.23.0/", - build_file = "//third_party:com_github_apple_swift_nio_ssl/BUILD.overlay", + build_file = Label( + "//third_party:com_github_apple_swift_nio_ssl/BUILD.overlay", + ), ) _maybe( @@ -186,7 +208,9 @@ def swift_rules_dependencies(include_bzlmod_ready_dependencies = True): urls = ["https://github.com/apple/swift-collections/archive/1.0.4.tar.gz"], # pinned to swift-nio @ grpc-swift version sha256 = "d9e4c8a91c60fb9c92a04caccbb10ded42f4cb47b26a212bc6b39cc390a4b096", strip_prefix = "swift-collections-1.0.4/", - build_file = "//third_party:com_github_apple_swift_collections/BUILD.overlay", + build_file = Label( + "//third_party:com_github_apple_swift_collections/BUILD.overlay", + ), ) _maybe( @@ -195,7 +219,9 @@ def swift_rules_dependencies(include_bzlmod_ready_dependencies = True): urls = ["https://github.com/apple/swift-atomics/archive/1.1.0.tar.gz"], # pinned to swift-nio @ grpc-swift version sha256 = "1bee7f469f7e8dc49f11cfa4da07182fbc79eab000ec2c17bfdce468c5d276fb", strip_prefix = "swift-atomics-1.1.0/", - build_file = "//third_party:com_github_apple_swift_atomics/BUILD.overlay", + build_file = Label( + "//third_party:com_github_apple_swift_atomics/BUILD.overlay", + ), ) # It relies on `index-import` to import indexes into Bazel's remote @@ -204,7 +230,7 @@ def swift_rules_dependencies(include_bzlmod_ready_dependencies = True): _maybe( http_archive, name = "build_bazel_rules_swift_index_import", - build_file = "//third_party:build_bazel_rules_swift_index_import/BUILD.overlay", + build_file = Label("//third_party:build_bazel_rules_swift_index_import/BUILD.overlay"), canonical_id = "index-import-5.8", urls = ["https://github.com/MobileNativeFoundation/index-import/releases/download/5.8.0.1/index-import.tar.gz"], sha256 = "28c1ffa39d99e74ed70623899b207b41f79214c498c603915aef55972a851a15", From 23752b17f2fd5d0299c9c1af8a6f409eaa2847ca Mon Sep 17 00:00:00 2001 From: Brentley Jones Date: Tue, 22 Oct 2024 08:48:05 -0500 Subject: [PATCH 07/92] Disable Bazel 8 in BCR presubmit for now (#1424) Signed-off-by: Brentley Jones --- .bcr/presubmit.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.bcr/presubmit.yml b/.bcr/presubmit.yml index 5c1ab28d3..130e94264 100644 --- a/.bcr/presubmit.yml +++ b/.bcr/presubmit.yml @@ -6,7 +6,10 @@ shell_commands: &shell_commands matrix: bazel: - 7.x - - last_green + # TODO: Uncomment once Bazel 8 is released + # - 8.x + # TODO: Uncomment once we support HEAD again + # - latest platform: - ubuntu2004 tasks: From c6553ef6982948481d6bc63c7bfeae99d7084dda Mon Sep 17 00:00:00 2001 From: Tony Allevato Date: Mon, 31 Oct 2022 14:23:27 -0700 Subject: [PATCH 08/92] Remove support for the `objc_library.module_map` attribute in `swift_clang_module_aspect` Targets should use `swift_interop_hint` instead to support custom module maps. PiperOrigin-RevId: 485148245 (cherry picked from commit b75dac9116710dfe330aa5306bbbc8efcb8a15fe) Signed-off-by: Brentley Jones --- swift/swift_clang_module_aspect.bzl | 49 ++++++++++++++--------------- 1 file changed, 23 insertions(+), 26 deletions(-) diff --git a/swift/swift_clang_module_aspect.bzl b/swift/swift_clang_module_aspect.bzl index 1b8d12dbe..0946afb94 100644 --- a/swift/swift_clang_module_aspect.bzl +++ b/swift/swift_clang_module_aspect.bzl @@ -189,15 +189,14 @@ def _generate_module_map( return module_map_file def _objc_library_module_info(aspect_ctx): - """Returns the `module_name` and `module_map` attrs for an `objc_library`. + """Returns the `module_name` attribute for an `objc_library`. Args: aspect_ctx: The aspect context. Returns: - A tuple containing the module name (a string) and the module map file (a - `File`) specified as attributes on the `objc_library`. These values may - be `None`. + The module name (a string) specified as an attribute on the + `objc_library`. This may be `None`. """ attr = aspect_ctx.rule.attr @@ -205,15 +204,16 @@ def _objc_library_module_info(aspect_ctx): # `swift_interop_hint` to customize `objc_*` targets' module names and # module maps. module_name = getattr(attr, "module_name", None) - module_map_file = None - module_map_target = getattr(attr, "module_map", None) - if module_map_target: - module_map_files = module_map_target.files.to_list() - if module_map_files: - module_map_file = module_map_files[0] + # TODO(b/195019413): Remove this when the `module_map` attribute is deleted. + if getattr(attr, "module_map", None): + fail( + "The `module_map` attribute on `objc_library` is no longer " + + "supported. Use `swift_interop_hint` instead to customize the " + + "module map for a target.", + ) - return module_name, module_map_file + return module_name def _module_info_for_target( target, @@ -253,14 +253,12 @@ def _module_info_for_target( ): return None, None - module_map_file = None - if not module_name: if apple_common.Objc not in target: return None, None if aspect_ctx.rule.kind == "objc_library": - module_name, module_map_file = _objc_library_module_info(aspect_ctx) + module_name = _objc_library_module_info(aspect_ctx) # If it was an `objc_library` without an explicit module name, or it # was some other `Objc`-providing target, derive the module name @@ -268,18 +266,17 @@ def _module_info_for_target( if not module_name: module_name = derive_swift_module_name(target.label) - # If we didn't get a module map above, generate it now. - if not module_map_file: - module_map_file = _generate_module_map( - actions = aspect_ctx.actions, - aspect_ctx = aspect_ctx, - compilation_context = compilation_context, - dependent_module_names = dependent_module_names, - exclude_headers = exclude_headers, - feature_configuration = feature_configuration, - module_name = module_name, - target = target, - ) + module_map_file = _generate_module_map( + actions = aspect_ctx.actions, + aspect_ctx = aspect_ctx, + compilation_context = compilation_context, + dependent_module_names = dependent_module_names, + exclude_headers = exclude_headers, + feature_configuration = feature_configuration, + module_name = module_name, + target = target, + ) + return module_name, module_map_file def _handle_module( From 9ef2a1fe2c5d51b49a60b57fea599cd459c722fb Mon Sep 17 00:00:00 2001 From: Brentley Jones Date: Tue, 22 Oct 2024 08:55:58 -0500 Subject: [PATCH 09/92] Allow existing `SwiftInfo` arguments until 3.0 (#1419) Signed-off-by: Brentley Jones --- swift/providers.bzl | 37 +++++++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/swift/providers.bzl b/swift/providers.bzl index 26141e6a4..45f22e934 100644 --- a/swift/providers.bzl +++ b/swift/providers.bzl @@ -82,16 +82,33 @@ def _swift_info_init( *, direct_swift_infos = [], modules = [], - swift_infos = []): - direct_modules = modules + [ - module - for provider in direct_swift_infos - for module in provider.direct_modules - ] - transitive_modules = [ - provider.transitive_modules - for provider in direct_swift_infos + swift_infos - ] + swift_infos = [], + # TODO: Remove as part of rules_swift 3.0 + direct_modules = [], + transitive_modules = []): + if direct_modules or transitive_modules: + # buildifier: disable=print + print("Using 'direct_modules' and 'transitive_modules' is deprecated " + + "and will be removed in a future rules_swift release; use " + + "'direct_swift_infos', 'modules', and 'swift_infos' instead.") + if direct_modules and (direct_swift_infos or modules or swift_infos): + fail("Can't use legacy 'direct_modules' attribute with new " + + "'direct_swift_infos', 'modules', and 'swift_infos' " + + "attributes.") + if transitive_modules and (direct_swift_infos or modules or swift_infos): + fail("Can't use legacy 'transitive_modules' attribute with new " + + "'direct_swift_infos', 'modules', and 'swift_infos' " + + "attributes.") + else: + direct_modules = modules + [ + module + for provider in direct_swift_infos + for module in provider.direct_modules + ] + transitive_modules = [ + provider.transitive_modules + for provider in direct_swift_infos + swift_infos + ] return { "direct_modules": direct_modules, From 58e0b9e7ccafde4281bd7d02544a3a7956e231e7 Mon Sep 17 00:00:00 2001 From: Brentley Jones Date: Tue, 22 Oct 2024 08:58:08 -0500 Subject: [PATCH 10/92] Remove old `SwiftInfo` arguments (#1427) This reverts commit 9ef2a1fe2c5d51b49a60b57fea599cd459c722fb. Signed-off-by: Brentley Jones --- swift/providers.bzl | 37 ++++++++++--------------------------- 1 file changed, 10 insertions(+), 27 deletions(-) diff --git a/swift/providers.bzl b/swift/providers.bzl index 45f22e934..26141e6a4 100644 --- a/swift/providers.bzl +++ b/swift/providers.bzl @@ -82,33 +82,16 @@ def _swift_info_init( *, direct_swift_infos = [], modules = [], - swift_infos = [], - # TODO: Remove as part of rules_swift 3.0 - direct_modules = [], - transitive_modules = []): - if direct_modules or transitive_modules: - # buildifier: disable=print - print("Using 'direct_modules' and 'transitive_modules' is deprecated " + - "and will be removed in a future rules_swift release; use " + - "'direct_swift_infos', 'modules', and 'swift_infos' instead.") - if direct_modules and (direct_swift_infos or modules or swift_infos): - fail("Can't use legacy 'direct_modules' attribute with new " + - "'direct_swift_infos', 'modules', and 'swift_infos' " + - "attributes.") - if transitive_modules and (direct_swift_infos or modules or swift_infos): - fail("Can't use legacy 'transitive_modules' attribute with new " + - "'direct_swift_infos', 'modules', and 'swift_infos' " + - "attributes.") - else: - direct_modules = modules + [ - module - for provider in direct_swift_infos - for module in provider.direct_modules - ] - transitive_modules = [ - provider.transitive_modules - for provider in direct_swift_infos + swift_infos - ] + swift_infos = []): + direct_modules = modules + [ + module + for provider in direct_swift_infos + for module in provider.direct_modules + ] + transitive_modules = [ + provider.transitive_modules + for provider in direct_swift_infos + swift_infos + ] return { "direct_modules": direct_modules, From 62f546b598c93f167fe17096ba80e0ad89afe3bf Mon Sep 17 00:00:00 2001 From: Tony Allevato Date: Thu, 19 Jan 2023 08:10:26 -0800 Subject: [PATCH 11/92] Remove `apple_common.Objc` provider usage related to linking now that the Apple linking logic in Bazel gets everything from `CcInfo` There are still a couple uses in `swift_clang_module_aspect` unrelated to linking that will be cleaned up separately. PiperOrigin-RevId: 503167994 (cherry picked from commit 40e1e63922e4ae71c21128bcc3525454169659bf) Cherry-pick notes: Also removes left over `apple_common.Objc` provider usage related to compiling. Signed-off-by: Brentley Jones --- doc/api.md | 6 +- doc/providers.md | 6 +- doc/rules.md | 18 +- mixed_language/internal/library.bzl | 11 -- proto/swift_proto_library.bzl | 1 - proto/swift_proto_library_group.bzl | 1 - proto/swift_proto_utils.bzl | 32 +--- swift/internal/BUILD | 2 - swift/internal/attrs.bzl | 15 +- swift/internal/compiling.bzl | 11 -- swift/internal/linking.bzl | 203 ++------------------- swift/internal/utils.bzl | 13 +- swift/providers.bzl | 9 - swift/swift_binary.bzl | 3 +- swift/swift_compiler_plugin.bzl | 3 +- swift/swift_import.bzl | 19 +- swift/swift_library.bzl | 24 +-- swift/swift_module_alias.bzl | 22 +-- swift/swift_test.bzl | 11 +- swift/toolchains/config/compile_config.bzl | 34 ++-- swift/toolchains/xcode_swift_toolchain.bzl | 48 ++--- test/fixtures/linking/fake_framework.bzl | 72 ++++---- test/rules/provider_test.bzl | 4 - 23 files changed, 101 insertions(+), 467 deletions(-) diff --git a/doc/api.md b/doc/api.md index 756be148a..9a5846d52 100644 --- a/doc/api.md +++ b/doc/api.md @@ -35,9 +35,8 @@ A C++ `FeatureConfiguration` value (see
 swift_common.compile(actions, additional_inputs, cc_infos, copts, defines, exec_group,
                      extra_swift_infos, feature_configuration, generated_header_name, is_test,
-                     include_dev_srch_paths, module_name, objc_infos, package_name, plugins,
-                     private_swift_infos, srcs, swift_infos, swift_toolchain, target_name,
-                     workspace_name)
+                     include_dev_srch_paths, module_name, package_name, plugins, private_swift_infos,
+                     srcs, swift_infos, swift_toolchain, target_name, workspace_name)
 
Compiles a Swift module. @@ -59,7 +58,6 @@ Compiles a Swift module. | is_test | Deprecated. This argument will be removed in the next major release. Use the `include_dev_srch_paths` attribute instead. Represents if the `testonly` value of the context. | `None` | | include_dev_srch_paths | A `bool` that indicates whether the developer framework search paths will be added to the compilation command. | `None` | | module_name | The name of the Swift module being compiled. This must be present and valid; use `derive_swift_module_name` to generate a default from the target's label if needed. | none | -| objc_infos | A list of `apple_common.ObjC` providers that represent C/Objective-C requirements of the target being compiled, such as Swift-compatible preprocessor defines, header search paths, and so forth. These are typically retrieved from a target's dependencies. | none | | package_name | The semantic package of the name of the Swift module being compiled. | none | | plugins | A list of `SwiftCompilerPluginInfo` providers that represent plugins that should be loaded by the compiler. | `[]` | | private_swift_infos | A list of `SwiftInfo` providers from private (implementation-only) dependencies of the target being compiled. The modules defined by these providers are used as dependencies of the Swift module being compiled but not of the Clang module for the generated header. | `[]` | diff --git a/doc/providers.md b/doc/providers.md index 7292973fe..e32836f6d 100644 --- a/doc/providers.md +++ b/doc/providers.md @@ -123,15 +123,15 @@ that use the toolchain. | action_configs | This field is an internal implementation detail of the build rules. | | cc_language | The `language` that should be passed to `cc_common` APIs that take it as an argument. | | 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. | +| 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.

* `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. | | const_protocols_to_gather | `File`. A JSON file specifying a list of protocols for extraction of conformances' const values. | | cross_import_overlays | A list of `SwiftCrossImportOverlayInfo` providers whose `SwiftInfo` providers will be automatically injected into the dependencies of Swift compilations if their declaring module and bystanding module are both already declared as dependencies. | | debug_outputs_provider | An optional function that provides toolchain-specific logic around the handling of additional debug outputs for `swift_binary` and `swift_test` targets.

If specified, this function must take the following keyword arguments:

* `ctx`: The rule context of the calling binary or test rule.

It must return a `struct` with the following fields:

* `additional_outputs`: Additional outputs expected from the linking action.

* `variables_extension`: A dictionary of additional crosstool variables to pass to the linking action. | | developer_dirs | A list of `structs` containing the following fields:

* `developer_path_label`: A `string` representing the type of developer path.

* `path`: A `string` representing the path to the developer framework. | | entry_point_linkopts_provider | A function that returns flags that should be passed to the linker to control the name of the entry point of a linked binary for rules that customize their entry point.

This function must take the following keyword arguments:

* `entry_point_name`: The name of the entry point function, as was passed to the Swift compiler using the `-entry-point-function-name` flag.

It must return a `struct` with the following fields:

* `linkopts`: A list of strings that will be passed as additional linker flags when linking a binary with a custom entry point. | | 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. | +| 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.

* `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.

* `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. | | module_aliases | A `SwiftModuleAliasesInfo` provider that defines the module aliases to use during compilation. | | package_configurations | A list of `SwiftPackageConfigurationInfo` providers that specify additional compilation configuration options that are applied to targets on a per-package basis. | | requested_features | `List` of `string`s. 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). | diff --git a/doc/rules.md b/doc/rules.md index 117be3360..03b22c660 100644 --- a/doc/rules.md +++ b/doc/rules.md @@ -65,7 +65,7 @@ please use one of the platform-specific application rules in | 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.

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_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 | `[]` | +| 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_library` (or anything propagating `SwiftInfo`)

* `cc_library` and `objc_library` (or anything propagating `CcInfo`) | List of labels | optional | `[]` | | srcs | A list of `.swift` source files that will be compiled into the library. | List of labels | 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 | `[]` | | 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 | `[]` | @@ -153,7 +153,7 @@ swift_library( | 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.

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_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 | `[]` | +| 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_library` (or anything propagating `SwiftInfo`)

* `cc_library` and `objc_library` (or anything propagating `CcInfo`) | List of labels | optional | `[]` | | srcs | A list of `.swift` source files that will be compiled into the library. | List of labels | 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 | `[]` | | 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 | `[]` | @@ -293,7 +293,7 @@ uses the API marked with `@_spi`. | 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.

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_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 | `[]` | +| 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_library` (or anything propagating `SwiftInfo`)

* `cc_library` and `objc_library` (or anything propagating `CcInfo`) | List of labels | 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 | `[]` | | archives | The list of `.a` or `.lo` files provided to Swift targets that depend on this target. | List of labels | optional | `[]` | | module_name | The name of the module represented by this target. | String | required | | @@ -463,7 +463,7 @@ Compiles and links Swift code into a static library and Swift module. | 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.

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_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 | `[]` | +| 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_library` (or anything propagating `SwiftInfo`)

* `cc_library` and `objc_library` (or anything propagating `CcInfo`) | List of labels | optional | `[]` | | srcs | A list of `.swift` source files that will be compiled into the library. | 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 | `[]` | | always_include_developer_search_paths | If `True`, the developer framework search paths will be added to the compilation command. This enables a Swift module to access `XCTest` without having to mark the target as `testonly = True`. | Boolean | optional | `False` | @@ -478,7 +478,7 @@ Compiles and links Swift code into a static library and Swift module. | 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 | `""` | | package_name | The semantic package of the Swift target being built. Targets with the same package_name can access APIs using the 'package' access control modifier in Swift 5.9+. | String | optional | `""` | | plugins | A list of `swift_compiler_plugin` targets that should be loaded by the compiler when compiling this module and any modules that directly depend on it. | List of labels | 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_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 | `[]` | +| 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_library` (or anything propagating `SwiftInfo`)

* `cc_library` and `objc_library` (or anything propagating `CcInfo`) | List of labels | optional | `[]` | | swiftc_inputs | Additional files that are referenced using `$(location ...)` in attributes that support location expansion. | List of labels | optional | `[]` | @@ -503,7 +503,7 @@ need to import the grouped libraries directly. | Name | Description | Type | Mandatory | Default | | :------------- | :------------- | :------------- | :------------- | :------------- | | name | A unique name for this target. | Name | required | | -| deps | A list of targets that should be included in the group. Allowed kinds of dependencies are:

* `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 | `[]` | +| deps | A list of targets that should be included in the group. Allowed kinds of dependencies are:

* `swift_library` (or anything propagating `SwiftInfo`)

* `cc_library` and `objc_library` (or anything propagating `CcInfo`) | List of labels | optional | `[]` | @@ -747,10 +747,10 @@ swift_proto_library( | 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.

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_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 | `[]` | +| 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_library` (or anything propagating `SwiftInfo`)

* `cc_library` and `objc_library` (or anything propagating `CcInfo`) | List of labels | optional | `[]` | | srcs | A list of `.swift` source files that will be compiled into the library. | List of labels | 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 | `[]` | -| additional_compiler_deps | List of additional dependencies required by the generated Swift code at compile time, whose SwiftProtoInfo will be ignored.

Allowed kinds of dependencies are:

* `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 | `[]` | +| additional_compiler_deps | List of additional dependencies required by the generated Swift code at compile time, whose SwiftProtoInfo will be ignored.

Allowed kinds of dependencies are:

* `swift_library` (or anything propagating `SwiftInfo`)

* `cc_library` and `objc_library` (or anything propagating `CcInfo`) | List of labels | optional | `[]` | | additional_compiler_info | Dictionary of additional information passed to the compiler targets. See the documentation of the respective compiler rules for more information on which fields are accepted and how they are used. | Dictionary: String -> String | optional | `{}` | | always_include_developer_search_paths | If `True`, the developer framework search paths will be added to the compilation command. This enables a Swift module to access `XCTest` without having to mark the target as `testonly = True`. | Boolean | optional | `False` | | 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` | @@ -919,7 +919,7 @@ bazel test --test_filter=AModule,BModule.SomeTests,BModule.OtherTests/testX //my | 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.

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_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 | `[]` | +| 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_library` (or anything propagating `SwiftInfo`)

* `cc_library` and `objc_library` (or anything propagating `CcInfo`) | List of labels | optional | `[]` | | srcs | A list of `.swift` source files that will be compiled into the library. | List of labels | 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 | `[]` | | 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 | `[]` | diff --git a/mixed_language/internal/library.bzl b/mixed_language/internal/library.bzl index c22fc8eb2..2a59f6240 100644 --- a/mixed_language/internal/library.bzl +++ b/mixed_language/internal/library.bzl @@ -194,17 +194,6 @@ def _mixed_language_library_impl(ctx): dependency_attributes = ["deps"], ), swift_info, - # Propagate an `apple_common.Objc` provider with linking info about the - # library so that linking with Apple Starlark APIs/rules works - # correctly. - # TODO(b/171413861): This can be removed when the Obj-C rules are - # migrated to use `CcLinkingContext`. - apple_common.new_objc_provider( - providers = get_providers( - [swift_target, clang_target], - apple_common.Objc, - ), - ), ] mixed_language_library = rule( diff --git a/proto/swift_proto_library.bzl b/proto/swift_proto_library.bzl index c8bc6e047..6b71147ea 100644 --- a/proto/swift_proto_library.bzl +++ b/proto/swift_proto_library.bzl @@ -103,7 +103,6 @@ def _swift_proto_library_impl(ctx): direct_output_group_info, direct_swift_info, direct_swift_proto_cc_info.cc_info, - direct_swift_proto_cc_info.objc_info, direct_swift_proto_info, ] diff --git a/proto/swift_proto_library_group.bzl b/proto/swift_proto_library_group.bzl index 30986a6ec..4acd101f8 100644 --- a/proto/swift_proto_library_group.bzl +++ b/proto/swift_proto_library_group.bzl @@ -130,7 +130,6 @@ def _swift_proto_library_group_impl(ctx): direct_output_group_info, direct_swift_info, direct_swift_proto_cc_info.cc_info, - direct_swift_proto_cc_info.objc_info, direct_swift_proto_info, ] diff --git a/proto/swift_proto_utils.bzl b/proto/swift_proto_utils.bzl index d459bd85d..d27742b46 100644 --- a/proto/swift_proto_utils.bzl +++ b/proto/swift_proto_utils.bzl @@ -28,12 +28,6 @@ load( ) load("//swift:swift_common.bzl", "swift_common") -# buildifier: disable=bzl-visibility -load( - "//swift/internal:linking.bzl", - "new_objc_provider", -) - # buildifier: disable=bzl-visibility load( "//swift/internal:output_groups.bzl", @@ -189,7 +183,6 @@ This provider is an implementation detail not meant to be used by clients. """, fields = { "cc_info": "The underlying `CcInfo` provider.", - "objc_info": "The underlying `apple_common.Objc` provider.", }, ) @@ -274,7 +267,6 @@ def compile_swift_protos_for_target( feature_configuration = feature_configuration, include_dev_srch_paths = include_dev_srch_paths, module_name = module_name, - objc_infos = get_providers(compiler_deps, apple_common.Objc), package_name = None, srcs = generated_swift_srcs, swift_toolchain = swift_toolchain, @@ -288,7 +280,7 @@ def compile_swift_protos_for_target( supplemental_outputs = compile_result.supplemental_outputs # Create the linking context from the compilation outputs: - linking_context, linking_output = ( + linking_context, _ = ( swift_common.create_linking_context_from_compilation_outputs( actions = ctx.actions, compilation_outputs = compilation_outputs, @@ -321,13 +313,6 @@ def compile_swift_protos_for_target( lambda proto_cc_info: proto_cc_info.cc_info, ) + get_providers(compiler_deps, CcInfo) - # Gather the transitive objc info providers: - transitive_objc_infos = get_providers( - compiler_deps, - SwiftProtoCcInfo, - lambda proto_cc_info: proto_cc_info.objc_info, - ) + get_providers(compiler_deps, apple_common.Objc) - # Gather the transitive swift info providers: transitive_swift_infos = get_providers( compiler_deps, @@ -351,20 +336,6 @@ def compile_swift_protos_for_target( cc_infos = transitive_cc_infos, ) - # Create the direct objc info provider: - direct_objc_info = new_objc_provider( - additional_objc_infos = ( - transitive_objc_infos + - swift_toolchain.implicit_deps_providers.objc_infos - ), - deps = [], - feature_configuration = feature_configuration, - is_test = False, - module_context = module_context, - libraries_to_link = [linking_output.library_to_link], - swift_toolchain = swift_toolchain, - ) - # Create the direct swift info provider: direct_swift_info = SwiftInfo( modules = [module_context], @@ -381,7 +352,6 @@ def compile_swift_protos_for_target( # Create the direct swift proto cc info provider: direct_swift_proto_cc_info = SwiftProtoCcInfo( cc_info = direct_cc_info, - objc_info = direct_objc_info, ) # Create the direct swift proto info: diff --git a/swift/internal/BUILD b/swift/internal/BUILD index 61ca86b9a..0dc92fa70 100644 --- a/swift/internal/BUILD +++ b/swift/internal/BUILD @@ -168,9 +168,7 @@ bzl_library( ":developer_dirs", ":feature_names", ":features", - ":utils", "//swift:swift_clang_module_aspect", - "@bazel_skylib//lib:collections", "@bazel_skylib//lib:dicts", "@bazel_tools//tools/build_defs/cc:action_names.bzl", ], diff --git a/swift/internal/attrs.bzl b/swift/internal/attrs.bzl index dec20bdb7..944a991a0 100644 --- a/swift/internal/attrs.bzl +++ b/swift/internal/attrs.bzl @@ -202,7 +202,7 @@ def swift_deps_attr(*, additional_deps_providers = [], doc, **kwargs): """Returns an attribute suitable for representing Swift rule dependencies. The returned attribute will be configured to accept targets that propagate - `CcInfo`, `SwiftInfo`, or `apple_common.Objc` providers. + `CcInfo` or `SwiftInfo` providers. Args: additional_deps_providers: A list of lists representing additional @@ -224,18 +224,9 @@ Allowed kinds of dependencies are: * `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.** +* `cc_library` and `objc_library` (or anything propagating `CcInfo`) """, - providers = [ - [CcInfo], - [SwiftInfo], - [apple_common.Objc], - ] + additional_deps_providers, + providers = [[CcInfo], [SwiftInfo]] + additional_deps_providers, **kwargs ) diff --git a/swift/internal/compiling.bzl b/swift/internal/compiling.bzl index b2fa3b47d..58c114943 100644 --- a/swift/internal/compiling.bzl +++ b/swift/internal/compiling.bzl @@ -258,7 +258,6 @@ def compile_module_interface( is_swift = True, module_name = module_name, objc_include_paths_workaround = depset(), - objc_info = None, source_files = [swiftinterface_file], swiftmodule_file = swiftmodule_file, target_label = feature_configuration._label, @@ -314,7 +313,6 @@ def compile( is_test = None, include_dev_srch_paths = None, module_name, - objc_infos, package_name, plugins = [], private_swift_infos = [], @@ -358,10 +356,6 @@ def compile( module_name: The name of the Swift module being compiled. This must be present and valid; use `derive_swift_module_name` to generate a default from the target's label if needed. - objc_infos: A list of `apple_common.ObjC` providers that represent - C/Objective-C requirements of the target being compiled, such as - Swift-compatible preprocessor defines, header search paths, and so - forth. These are typically retrieved from a target's dependencies. package_name: The semantic package of the name of the Swift module being compiled. plugins: A list of `SwiftCompilerPluginInfo` providers that represent @@ -542,9 +536,6 @@ def compile( merged_cc_info = cc_common.merge_cc_infos( cc_infos = cc_infos + swift_toolchain.implicit_deps_providers.cc_infos, ) - merged_objc_info = apple_common.new_objc_provider( - providers = objc_infos + swift_toolchain.implicit_deps_providers.objc_infos, - ) transitive_swiftmodules = [] defines_set = sets.make(defines) @@ -645,7 +636,6 @@ to use swift_common.compile(include_dev_srch_paths = ...) instead.\ include_dev_srch_paths = include_dev_srch_paths_value, is_swift = True, module_name = module_name, - objc_info = merged_objc_info, original_module_name = original_module_name, package_name = package_name, plugins = used_plugins, @@ -987,7 +977,6 @@ def _precompile_clang_module( is_swift_generated_header = is_swift_generated_header, module_name = module_name, package_name = None, - objc_info = apple_common.new_objc_provider(), pcm_file = precompiled_module, source_files = [module_map_file], target_label = feature_configuration._label, diff --git a/swift/internal/linking.bzl b/swift/internal/linking.bzl index d4305ff8b..77ed337b9 100644 --- a/swift/internal/linking.bzl +++ b/swift/internal/linking.bzl @@ -14,7 +14,6 @@ """Implementation of linking logic for Swift.""" -load("@bazel_skylib//lib:collections.bzl", "collections") load("@bazel_skylib//lib:dicts.bzl", "dicts") load( "//swift:swift_clang_module_aspect.bzl", @@ -45,10 +44,6 @@ load( "get_cc_feature_configuration", "is_feature_enabled", ) -load(":utils.bzl", "get_providers") - -# TODO: Remove once we drop bazel 7.x -_OBJC_PROVIDER_LINKING = hasattr(apple_common.new_objc_provider(), "linkopt") def binary_rule_attrs( *, @@ -454,118 +449,6 @@ def malloc_linking_context(ctx): malloc = ctx.attr._custom_malloc or ctx.attr.malloc return malloc[CcInfo].linking_context -def new_objc_provider( - *, - additional_link_inputs = [], - additional_objc_infos = [], - alwayslink = False, - deps, - feature_configuration, - is_test, - libraries_to_link, - module_context, - user_link_flags = [], - swift_toolchain): - """Creates an `apple_common.Objc` provider for a Swift target. - - Args: - additional_link_inputs: Additional linker input files that should be - propagated to dependents. - additional_objc_infos: Additional `apple_common.Objc` providers from - transitive dependencies not provided by the `deps` argument. - alwayslink: If True, any binary that depends on the providers returned - by this function will link in all of the library's object files, - even if some contain no symbols referenced by the binary. - deps: The dependencies of the target being built, whose `Objc` providers - will be passed to the new one in order to propagate the correct - transitive fields. - feature_configuration: The Swift feature configuration. - is_test: Represents if the `testonly` value of the context. - libraries_to_link: A list (typically of one element) of the - `LibraryToLink` objects from which the static archives (`.a` files) - containing the target's compiled code will be retrieved. - module_context: The module context as returned by - `compile`. - user_link_flags: Linker options that should be propagated to dependents. - swift_toolchain: The `SwiftToolchainInfo` provider of the toolchain. - - Returns: - An `apple_common.Objc` provider that should be returned by the calling - rule. - """ - - # The link action registered by `apple_common.link_multi_arch_binary` only - # looks at `Objc` providers, not `CcInfo`, for libraries to link. - # Dependencies from an `objc_library` to a `cc_library` are handled as a - # special case, but other `cc_library` dependencies (such as `swift_library` - # to `cc_library`) would be lost since they do not receive the same - # treatment. Until those special cases are resolved via the unification of - # the Obj-C and C++ rules, we need to collect libraries from `CcInfo` and - # put them into the new `Objc` provider. - transitive_cc_libs = [] - for cc_info in get_providers(deps, CcInfo): - static_libs = [] - for linker_input in cc_info.linking_context.linker_inputs.to_list(): - for library_to_link in linker_input.libraries: - library = library_to_link.static_library - if library: - static_libs.append(library) - transitive_cc_libs.append(depset(static_libs, order = "topological")) - - direct_libraries = [] - force_load_libraries = [] - - for library_to_link in libraries_to_link: - library = library_to_link.static_library - if library: - direct_libraries.append(library) - if alwayslink: - force_load_libraries.append(library) - - extra_linkopts = [] - if feature_configuration and should_embed_swiftmodule_for_debugging( - feature_configuration = feature_configuration, - module_context = module_context, - ): - module_file = module_context.swift.swiftmodule - extra_linkopts.append("-Wl,-add_ast_path,{}".format(module_file.path)) - debug_link_inputs = [module_file] - else: - debug_link_inputs = [] - - if is_test: - extra_linkopts.extend(developer_dirs_linkopts(swift_toolchain.developer_dirs)) - - if feature_configuration and is_feature_enabled( - feature_configuration = feature_configuration, - feature_name = SWIFT_FEATURE_OBJC_LINK_FLAGS, - ): - extra_linkopts.append("-ObjC") - - kwargs = { - "providers": get_providers( - deps, - apple_common.Objc, - ) + additional_objc_infos, - } - - if _OBJC_PROVIDER_LINKING: - kwargs = dicts.add(kwargs, { - "force_load_library": depset( - force_load_libraries, - order = "topological", - ), - "library": depset( - direct_libraries, - transitive = transitive_cc_libs, - order = "topological", - ), - "link_inputs": depset(additional_link_inputs + debug_link_inputs), - "linkopt": depset(user_link_flags + extra_linkopts), - }) - - return apple_common.new_objc_provider(**kwargs) - def register_link_binary_action( *, actions, @@ -575,10 +458,10 @@ def register_link_binary_action( compilation_outputs, deps, feature_configuration, + label, name, module_contexts = [], output_type, - owner, stamp, swift_toolchain, user_link_flags = [], @@ -599,6 +482,8 @@ def register_link_binary_action( deps: A list of targets representing additional libraries that will be passed to the linker. feature_configuration: The Swift feature configuration. + label: The label of the target being linked, whose name is used to + derive the output artifact if the `name` argument is not provided. module_contexts: A list of module contexts resulting from the compilation of the sources in the binary target, which are embedded in the binary for debugging if this is a debug build. This list may @@ -607,7 +492,6 @@ def register_link_binary_action( output artifact. 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. @@ -623,80 +507,17 @@ def register_link_binary_action( `library_to_link` that was linked (depending on the value of the `output_type` argument). """ - linking_contexts = [] - - for dep in deps: - if CcInfo in dep: - cc_info = dep[CcInfo] - linking_contexts.append(cc_info.linking_context) - - # TODO(allevato): Remove all of this when `apple_common.Objc` goes away. - if apple_common.Objc in dep: - objc = dep[apple_common.Objc] - - def get_objc_list(objc, attr): - return getattr(objc, attr, depset([])).to_list() - - # We don't need to handle the `objc.sdk_framework` field here - # because those values have also been put into the user link flags - # of a CcInfo, but the others don't seem to have been. - dep_link_flags = [ - "-l{}".format(dylib) - for dylib in get_objc_list(objc, "sdk_dylib") - ] - dep_link_flags.extend([ - "-F{}".format(path) - for path in get_objc_list(objc, "dynamic_framework_paths") - ]) - dep_link_flags.extend(collections.before_each( - "-framework", - get_objc_list(objc, "dynamic_framework_names"), - )) - dep_link_flags.extend([ - "-F{}".format(path) - for path in get_objc_list(objc, "static_framework_paths") - ]) - dep_link_flags.extend(collections.before_each( - "-framework", - get_objc_list(objc, "static_framework_names"), - )) - - is_bazel_6 = hasattr(apple_common, "link_multi_arch_static_library") - if not hasattr(objc, "static_framework_file"): - additional_inputs = depset([]) - elif is_bazel_6: - additional_inputs = objc.static_framework_file - else: - additional_inputs = depset( - transitive = [ - objc.static_framework_file, - objc.imported_library, - ], - ) - dep_link_flags.extend([ - lib.path - for lib in objc.imported_library.to_list() - ]) - - linking_contexts.append( - cc_common.create_linking_context( - linker_inputs = depset([ - cc_common.create_linker_input( - owner = owner, - user_link_flags = dep_link_flags, - additional_inputs = additional_inputs, - ), - ]), - ), - ) - - linking_contexts.extend(additional_linking_contexts) + linking_contexts = [ + dep[CcInfo].linking_context + for dep in deps + if CcInfo in dep + ] + additional_linking_contexts for module_context in module_contexts: debugging_linking_context = _create_embedded_debugging_linking_context( actions = actions, feature_configuration = feature_configuration, - label = owner, + label = label, module_context = module_context, swift_toolchain = swift_toolchain, ) @@ -707,7 +528,7 @@ def register_link_binary_action( actions = actions, compilation_outputs = compilation_outputs, feature_configuration = feature_configuration, - label = owner, + label = label, name = name, swift_toolchain = swift_toolchain, ) @@ -722,7 +543,7 @@ def register_link_binary_action( cc_common.create_linking_context( linker_inputs = depset([ cc_common.create_linker_input( - owner = owner, + owner = label, user_link_flags = depset(["-Wl,-z,nostart-stop-gc"]), ), ]), @@ -738,7 +559,7 @@ def register_link_binary_action( cc_common.create_linking_context( linker_inputs = depset([ cc_common.create_linker_input( - owner = owner, + owner = label, user_link_flags = depset(["-ObjC"]), ), ]), diff --git a/swift/internal/utils.bzl b/swift/internal/utils.bzl index 3820fd98d..6a54c3ef9 100644 --- a/swift/internal/utils.bzl +++ b/swift/internal/utils.bzl @@ -17,10 +17,7 @@ load("@bazel_skylib//lib:paths.bzl", "paths") load("//swift:providers.bzl", "SwiftInfo") -def collect_implicit_deps_providers( - targets, - additional_cc_infos = [], - additional_objc_infos = []): +def collect_implicit_deps_providers(targets, additional_cc_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 @@ -30,33 +27,25 @@ def collect_implicit_deps_providers( 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, ) diff --git a/swift/providers.bzl b/swift/providers.bzl index 26141e6a4..18f818006 100644 --- a/swift/providers.bzl +++ b/swift/providers.bzl @@ -279,9 +279,6 @@ 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. @@ -347,9 +344,6 @@ 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. @@ -367,9 +361,6 @@ 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. diff --git a/swift/swift_binary.bzl b/swift/swift_binary.bzl index b9e5d69d6..775cd8fcd 100644 --- a/swift/swift_binary.bzl +++ b/swift/swift_binary.bzl @@ -119,7 +119,6 @@ def _swift_binary_impl(ctx): feature_configuration = feature_configuration, include_dev_srch_paths = include_dev_srch_paths, module_name = module_name, - objc_infos = get_providers(ctx.attr.deps, apple_common.Objc), package_name = ctx.attr.package_name, plugins = get_providers(ctx.attr.plugins, SwiftCompilerPluginInfo), srcs = srcs, @@ -182,10 +181,10 @@ def _swift_binary_impl(ctx): feature_configuration = feature_configuration, compilation_outputs = compilation_outputs, deps = ctx.attr.deps, + label = ctx.label, module_contexts = module_contexts, name = name, output_type = "executable", - owner = ctx.label, stamp = ctx.attr.stamp, swift_toolchain = swift_toolchain, user_link_flags = binary_link_flags + entry_point_linkopts, diff --git a/swift/swift_compiler_plugin.bzl b/swift/swift_compiler_plugin.bzl index ef0f284e0..20a4bb530 100644 --- a/swift/swift_compiler_plugin.bzl +++ b/swift/swift_compiler_plugin.bzl @@ -99,7 +99,6 @@ def _swift_compiler_plugin_impl(ctx): feature_configuration = feature_configuration, include_dev_srch_paths = ctx.attr.testonly, module_name = module_name, - objc_infos = get_providers(deps, apple_common.Objc), package_name = ctx.attr.package_name, plugins = get_providers(ctx.attr.plugins, SwiftCompilerPluginInfo), srcs = srcs, @@ -141,10 +140,10 @@ def _swift_compiler_plugin_impl(ctx): compilation_outputs = compilation_outputs, deps = deps, feature_configuration = feature_configuration, + label = ctx.label, module_contexts = module_contexts, name = name, output_type = "executable", - owner = ctx.label, stamp = ctx.attr.stamp, swift_toolchain = swift_toolchain, user_link_flags = expand_locations( diff --git a/swift/swift_import.bzl b/swift/swift_import.bzl index 9a5aac2de..d2b66691b 100644 --- a/swift/swift_import.bzl +++ b/swift/swift_import.bzl @@ -26,7 +26,6 @@ load( "configure_features", "get_cc_feature_configuration", ) -load("//swift/internal:linking.bzl", "new_objc_provider") load("//swift/internal:providers.bzl", "SwiftCompilerPluginInfo") load( "//swift/internal:toolchain_utils.bzl", @@ -138,7 +137,7 @@ def _swift_import_impl(ctx): ) swift_outputs = [swiftmodule] + compact([swiftdoc]) - providers = [ + return [ DefaultInfo( files = depset(archives + swift_outputs), runfiles = ctx.runfiles( @@ -152,24 +151,8 @@ def _swift_import_impl(ctx): swift_infos = swift_infos, ), cc_info, - # Propagate an `Objc` provider so that Apple-specific rules like - # apple_binary` will link the imported library properly. Typically we'd - # want to only propagate this if the toolchain reports that it supports - # Objective-C interop, but we would hit the same cyclic dependency - # mentioned above, so we propagate it unconditionally; it will be - # ignored on non-Apple platforms anyway. - new_objc_provider( - deps = deps, - feature_configuration = None, - is_test = ctx.attr.testonly, - libraries_to_link = libraries_to_link, - module_context = module_context, - swift_toolchain = swift_toolchain, - ), ] - return providers - swift_import = rule( attrs = dicts.add( swift_common_rule_attrs(), diff --git a/swift/swift_library.bzl b/swift/swift_library.bzl index 10e0abde7..8d45693c3 100644 --- a/swift/swift_library.bzl +++ b/swift/swift_library.bzl @@ -38,7 +38,6 @@ load("//swift/internal:features.bzl", "configure_features") load( "//swift/internal:linking.bzl", "create_linking_context_from_compilation_outputs", - "new_objc_provider", ) load( "//swift/internal:output_groups.bzl", @@ -197,7 +196,6 @@ def _swift_library_impl(ctx): generated_header_name = generated_header_name, include_dev_srch_paths = include_dev_srch_paths, module_name = module_name, - objc_infos = get_providers(ctx.attr.deps, apple_common.Objc), package_name = ctx.attr.package_name, plugins = get_providers(ctx.attr.plugins, SwiftCompilerPluginInfo), private_swift_infos = private_swift_infos, @@ -253,8 +251,7 @@ def _swift_library_impl(ctx): linking_output.library_to_link.pic_static_library, ]) - implicit_deps_providers = swift_toolchain.implicit_deps_providers - providers = [ + return [ DefaultInfo( files = depset(direct_output_files), runfiles = ctx.runfiles( @@ -279,25 +276,6 @@ def _swift_library_impl(ctx): ), ] - # Propagate an `apple_common.Objc` provider with linking info about the - # library so that linking with Apple Starlark APIs/rules works correctly. - # TODO(b/171413861): This can be removed when the Obj-C rules are migrated - # to use `CcLinkingContext`. - providers.append(new_objc_provider( - additional_link_inputs = additional_inputs, - additional_objc_infos = implicit_deps_providers.objc_infos, - alwayslink = ctx.attr.alwayslink, - deps = deps + private_deps, - feature_configuration = feature_configuration, - is_test = ctx.attr.testonly, - module_context = module_context, - libraries_to_link = [linking_output.library_to_link], - user_link_flags = linkopts, - swift_toolchain = swift_toolchain, - )) - - return providers - swift_library = rule( attrs = dicts.add( swift_library_rule_attrs(additional_deps_aspects = [ diff --git a/swift/swift_module_alias.bzl b/swift/swift_module_alias.bzl index 60c1199df..9e93e5ecc 100644 --- a/swift/swift_module_alias.bzl +++ b/swift/swift_module_alias.bzl @@ -21,7 +21,6 @@ load("//swift/internal:features.bzl", "configure_features") load( "//swift/internal:linking.bzl", "create_linking_context_from_compilation_outputs", - "new_objc_provider", ) load( "//swift/internal:output_groups.bzl", @@ -77,7 +76,6 @@ def _swift_module_alias_impl(ctx): feature_configuration = feature_configuration, include_dev_srch_paths = ctx.attr.testonly, module_name = module_name, - objc_infos = get_providers(ctx.attr.deps, apple_common.Objc), package_name = None, srcs = [reexport_src], swift_infos = swift_infos, @@ -107,7 +105,7 @@ def _swift_module_alias_impl(ctx): ) ) - providers = [ + return [ DefaultInfo( files = depset(compact([ module_context.swift.swiftdoc, @@ -132,24 +130,6 @@ def _swift_module_alias_impl(ctx): compile_result.swift_info, ] - # Propagate an `apple_common.Objc` provider with linking info about the - # library so that linking with Apple Starlark APIs/rules works correctly. - # TODO(b/171413861): This can be removed when the Obj-C rules are migrated - # to use `CcLinkingContext`. - providers.append(new_objc_provider( - additional_objc_infos = ( - swift_toolchain.implicit_deps_providers.objc_infos - ), - deps = deps, - feature_configuration = feature_configuration, - is_test = ctx.attr.testonly, - module_context = module_context, - libraries_to_link = [linking_output.library_to_link], - swift_toolchain = swift_toolchain, - )) - - return providers - swift_module_alias = rule( attrs = dicts.add( swift_toolchain_attrs(), diff --git a/swift/swift_test.bzl b/swift/swift_test.bzl index 0a3b6e7c7..bfd901954 100644 --- a/swift/swift_test.bzl +++ b/swift/swift_test.bzl @@ -271,7 +271,6 @@ def _do_compile( feature_configuration, include_dev_srch_paths, module_name, - objc_infos, name, package_name, plugins = [], @@ -293,8 +292,6 @@ def _do_compile( module_name: The name of the module being compiled. name: The target name or a value derived from the target name that is used to name output files generated by the action. - objc_infos: A list of `apple_common.ObjC` providers that should be - provided as inputs to the compilation action. package_name: The semantic package of the name of the Swift module being compiled. plugins: A list of `SwiftCompilerPluginInfo` providers that need to be @@ -325,7 +322,6 @@ def _do_compile( module_name = module_name, package_name = package_name, plugins = plugins, - objc_infos = objc_infos, srcs = srcs, swift_infos = swift_infos, swift_toolchain = swift_toolchain, @@ -378,15 +374,12 @@ def _swift_test_impl(ctx): # support testing those. deps_cc_infos = [] deps_compilation_contexts = [] - deps_objc_infos = [] deps_swift_infos = [] additional_linking_contexts = [] for dep in deps: if CcInfo in dep: deps_cc_infos.append(dep[CcInfo]) deps_compilation_contexts.append(dep[CcInfo].compilation_context) - if apple_common.Objc in dep: - deps_objc_infos.append(dep[apple_common.Objc]) if SwiftInfo in dep: deps_swift_infos.append(dep[SwiftInfo]) if SwiftBinaryInfo in dep: @@ -431,7 +424,6 @@ def _swift_test_impl(ctx): feature_configuration = feature_configuration, include_dev_srch_paths = include_dev_srch_paths, module_name = module_name, - objc_infos = deps_objc_infos, package_name = ctx.attr.package_name, plugins = get_providers(ctx.attr.plugins, SwiftCompilerPluginInfo), name = ctx.label.name, @@ -489,7 +481,6 @@ def _swift_test_impl(ctx): include_dev_srch_paths = include_dev_srch_paths, module_name = module_name + "__GeneratedTestDiscoveryRunner", name = ctx.label.name + "__GeneratedTestDiscoveryRunner", - objc_infos = deps_objc_infos, package_name = ctx.attr.package_name, srcs = discovery_srcs, swift_infos = ( @@ -540,10 +531,10 @@ def _swift_test_impl(ctx): compilation_outputs = compilation_outputs, deps = deps + additional_link_deps, feature_configuration = feature_configuration, + label = ctx.label, module_contexts = module_contexts, name = name, output_type = "executable", - owner = ctx.label, stamp = ctx.attr.stamp, swift_toolchain = swift_toolchain, user_link_flags = expand_locations( diff --git a/swift/toolchains/config/compile_config.bzl b/swift/toolchains/config/compile_config.bzl index ce7cc493a..bb118f212 100644 --- a/swift/toolchains/config/compile_config.bzl +++ b/swift/toolchains/config/compile_config.bzl @@ -1776,27 +1776,21 @@ def _frameworks_disable_autolink_configurator(prerequisites, args): errors since when linking the framework it will be passed directly as a library. """ - if hasattr(prerequisites.objc_info, "dynamic_framework_file"): - args.add_all( - depset(transitive = [prerequisites.objc_info.imported_library, prerequisites.objc_info.dynamic_framework_file]), - map_each = _disable_autolink_framework_copts, - ) - else: - libraries = [] - inputs = prerequisites.cc_linking_context.linker_inputs.to_list() - for linker_input in inputs: - for library in linker_input.libraries: - if library.dynamic_library: - libraries.append(library.dynamic_library) - if library.static_library: - libraries.append(library.static_library) - if library.pic_static_library: - libraries.append(library.pic_static_library) + libraries = [] + inputs = prerequisites.cc_linking_context.linker_inputs.to_list() + for linker_input in inputs: + for library in linker_input.libraries: + if library.dynamic_library: + libraries.append(library.dynamic_library) + if library.static_library: + libraries.append(library.static_library) + if library.pic_static_library: + libraries.append(library.pic_static_library) - args.add_all( - depset(transitive = [depset(libraries)]), - map_each = _disable_autolink_framework_copts, - ) + args.add_all( + depset(transitive = [depset(libraries)]), + map_each = _disable_autolink_framework_copts, + ) def _disable_autolink_framework_copts(library_path): """A `map_each` helper that potentially disables autolinking for the given library. diff --git a/swift/toolchains/xcode_swift_toolchain.bzl b/swift/toolchains/xcode_swift_toolchain.bzl index fc315c524..caca31197 100644 --- a/swift/toolchains/xcode_swift_toolchain.bzl +++ b/swift/toolchains/xcode_swift_toolchain.bzl @@ -98,9 +98,6 @@ load( ) load("//swift/toolchains/config:tool_config.bzl", "ToolConfigInfo") -# TODO: Remove once we drop bazel 7.x -_OBJC_PROVIDER_LINKING = hasattr(apple_common.new_objc_provider(), "linkopt") - def _platform_developer_framework_dir( apple_toolchain, target_triple): @@ -143,16 +140,16 @@ def _sdk_developer_framework_dir(apple_toolchain, target_triple): return paths.join(apple_toolchain.sdk_dir(), "Developer/Library/Frameworks") -def _swift_linkopts_providers( +def _swift_linkopts_cc_info( apple_toolchain, target_triple, toolchain_label, toolchain_root): - """Returns providers containing flags that should be passed to the linker. + """Returns a `CcInfo` containing flags that should be passed to the linker. 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. + dependencies of the toolchain to ensure that any binary containing Swift + code will link to the standard libraries correctly. Args: apple_toolchain: The `apple_common.apple_toolchain()` object. @@ -163,12 +160,8 @@ def _swift_linkopts_providers( libraries required to link the binary Returns: - 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. + A `CcInfo` provider that will provide linker flags to binaries that + depend on Swift targets. """ linkopts = [] if toolchain_root: @@ -197,23 +190,15 @@ def _swift_linkopts_providers( "-Wl,-rpath,/usr/lib/swift", ]) - if _OBJC_PROVIDER_LINKING: - objc_info = apple_common.new_objc_provider(linkopt = depset(linkopts)) - else: - objc_info = apple_common.new_objc_provider() - - 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), - ), - ]), - ), + 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), + ), + ]), ), - objc_info = objc_info, ) def _make_resource_directory_configurator(developer_dir): @@ -604,7 +589,7 @@ def _xcode_swift_toolchain_impl(ctx): elif custom_toolchain: custom_xcode_toolchain_root = "__BAZEL_CUSTOM_XCODE_TOOLCHAIN_PATH__" - swift_linkopts_providers = _swift_linkopts_providers( + swift_linkopts_cc_info = _swift_linkopts_cc_info( apple_toolchain = apple_toolchain, target_triple = target_triple, toolchain_label = ctx.label, @@ -717,8 +702,7 @@ def _xcode_swift_toolchain_impl(ctx): ), implicit_deps_providers = collect_implicit_deps_providers( ctx.attr.implicit_deps + ctx.attr.clang_implicit_deps, - additional_cc_infos = [swift_linkopts_providers.cc_info], - additional_objc_infos = [swift_linkopts_providers.objc_info], + additional_cc_infos = [swift_linkopts_cc_info], ), module_aliases = ( ctx.attr._module_mapping[SwiftModuleAliasesInfo].aliases diff --git a/test/fixtures/linking/fake_framework.bzl b/test/fixtures/linking/fake_framework.bzl index ed9dcea01..7b7869e61 100644 --- a/test/fixtures/linking/fake_framework.bzl +++ b/test/fixtures/linking/fake_framework.bzl @@ -9,46 +9,42 @@ load( def _impl(ctx): binary1 = ctx.actions.declare_file("framework1.framework/framework1") ctx.actions.write(binary1, "empty") + binary2 = ctx.actions.declare_file("framework2.framework/framework2") ctx.actions.write(binary2, "empty") - if hasattr(apple_common.new_objc_provider(), "static_framework_file"): - return apple_common.new_objc_provider( - static_framework_file = depset([binary1]), - imported_library = depset([binary1]), - dynamic_framework_file = depset([binary2]), - ) - else: - cc_toolchain = find_cpp_toolchain(ctx) - feature_configuration = cc_common.configure_features( - ctx = ctx, - cc_toolchain = cc_toolchain, - language = "objc", - requested_features = ctx.features, - unsupported_features = ctx.disabled_features, - ) - return CcInfo( - linking_context = cc_common.create_linking_context( - linker_inputs = depset([ - 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 = feature_configuration, - static_library = binary1, - ), - cc_common.create_library_to_link( - actions = ctx.actions, - cc_toolchain = cc_toolchain, - dynamic_library = binary2, - feature_configuration = feature_configuration, - ), - ]), - ), - ]), - ), - ) + + cc_toolchain = find_cpp_toolchain(ctx) + feature_configuration = cc_common.configure_features( + ctx = ctx, + cc_toolchain = cc_toolchain, + language = "objc", + requested_features = ctx.features, + unsupported_features = ctx.disabled_features, + ) + + return CcInfo( + linking_context = cc_common.create_linking_context( + linker_inputs = depset([ + 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 = feature_configuration, + static_library = binary1, + ), + cc_common.create_library_to_link( + actions = ctx.actions, + cc_toolchain = cc_toolchain, + dynamic_library = binary2, + feature_configuration = feature_configuration, + ), + ]), + ), + ]), + ), + ) fake_framework = rule( implementation = _impl, diff --git a/test/rules/provider_test.bzl b/test/rules/provider_test.bzl index 6e4529926..ac6152e8f 100644 --- a/test/rules/provider_test.bzl +++ b/test/rules/provider_test.bzl @@ -163,8 +163,6 @@ def _lookup_provider_by_name(env, target, provider_name): provider = OutputGroupInfo elif provider_name == "SwiftInfo": provider = SwiftInfo - elif provider_name == "apple_common.Objc": - provider = apple_common.Objc if not provider: unittest.fail( @@ -287,7 +285,6 @@ Currently, only the following providers are recognized: * `DefaultInfo` * `OutputGroupInfo` * `SwiftInfo` -* `apple_common.Objc` """, ), "expected_files": attr.string_list( @@ -340,7 +337,6 @@ Currently, only the following providers are recognized: * `DefaultInfo` * `OutputGroupInfo` * `SwiftInfo` -* `apple_common.Objc` """, ), }, From 2b7655fd3f9ef9ea0efe89bb55f0ba68dcf09e32 Mon Sep 17 00:00:00 2001 From: Thomas Van Lenten Date: Tue, 2 May 2023 11:13:33 -0700 Subject: [PATCH 12/92] Remove `swift_module_alias` Was originally intended to be like `alias` and just used for migration, but is easily misused to merge/reexport targets, which in turns causes problems for indexing, automation, etc. that try to map symbols back to targets. Since it generates a new files, if dependent on multiple things to merge, that ends up being a choke point that slows downs builds when someone really only needs a subset of the dependencies. So just remove it so it isn't something folks will try to reach for with many down sides. If someone really needs the rexport steps for a migration, they can manually make a targets/srcs to do it, and hopefully thus be aware of the costs so there will migrate off it as soon as possible. PiperOrigin-RevId: 528836559 (cherry picked from commit 4f27bd0d4217b96838190dfe9e3b99b32e420274) Signed-off-by: Brentley Jones --- doc/BUILD | 1 - doc/README.md | 1 - doc/doc.bzl | 5 - doc/rules.md | 41 +---- examples/xplatform/module_alias/BUILD | 19 --- examples/xplatform/module_alias/main.swift | 15 -- swift/BUILD | 18 --- swift/swift.bzl | 2 - swift/swift_library_group.bzl | 4 +- swift/swift_module_alias.bzl | 178 --------------------- 10 files changed, 4 insertions(+), 280 deletions(-) delete mode 100644 examples/xplatform/module_alias/BUILD delete mode 100644 examples/xplatform/module_alias/main.swift delete mode 100644 swift/swift_module_alias.bzl diff --git a/doc/BUILD b/doc/BUILD index e61f01e2c..212073658 100644 --- a/doc/BUILD +++ b/doc/BUILD @@ -26,7 +26,6 @@ _DOC_SRCS = { "swift_library", "swift_library_group", "mixed_language_library", - "swift_module_alias", "swift_module_mapping", "swift_module_mapping_test", "swift_package_configuration", diff --git a/doc/README.md b/doc/README.md index 8fbfdf7f1..8bf19d2fa 100644 --- a/doc/README.md +++ b/doc/README.md @@ -6,7 +6,6 @@ * [swift_import](rules.md#swift_import) * [swift_interop_hint](rules.md#swift_interop_hint) * [swift_library](rules.md#swift_library) -* [swift_module_alias](rules.md#swift_module_alias) * [swift_proto_library](rules.md#swift_proto_library) * [swift_test](rules.md#swift_test) diff --git a/doc/doc.bzl b/doc/doc.bzl index f9175dc36..f3f57cf37 100644 --- a/doc/doc.bzl +++ b/doc/doc.bzl @@ -86,10 +86,6 @@ load( "//swift:swift_library_group.bzl", _swift_library_group = "swift_library_group", ) -load( - "//swift:swift_module_alias.bzl", - _swift_module_alias = "swift_module_alias", -) load( "//swift:swift_module_mapping.bzl", _swift_module_mapping = "swift_module_mapping", @@ -130,7 +126,6 @@ swift_interop_hint = _swift_interop_hint swift_library = _swift_library swift_library_group = _swift_library_group mixed_language_library = _mixed_language_library -swift_module_alias = _swift_module_alias swift_module_mapping = _swift_module_mapping swift_module_mapping_test = _swift_module_mapping_test swift_package_configuration = _swift_package_configuration diff --git a/doc/rules.md b/doc/rules.md index 03b22c660..fdf8da713 100644 --- a/doc/rules.md +++ b/doc/rules.md @@ -25,7 +25,6 @@ On this page: * [swift_library](#swift_library) * [swift_library_group](#swift_library_group) * [mixed_language_library](#mixed_language_library) - * [swift_module_alias](#swift_module_alias) * [swift_module_mapping](#swift_module_mapping) * [swift_module_mapping_test](#swift_module_mapping_test) * [swift_package_configuration](#swift_package_configuration) @@ -494,8 +493,8 @@ Groups Swift compatible libraries (e.g. `swift_library` and `objc_library`). The target can be used anywhere a `swift_library` can be used. It behaves similar to source-less `{cc,obj}_library` targets. -Unlike `swift_module_alias`, a new module isn't created for this target, you -need to import the grouped libraries directly. +A new module isn't created for this target, you need to import the grouped +libraries directly. **ATTRIBUTES** @@ -506,42 +505,6 @@ need to import the grouped libraries directly. | deps | A list of targets that should be included in the group. Allowed kinds of dependencies are:

* `swift_library` (or anything propagating `SwiftInfo`)

* `cc_library` and `objc_library` (or anything propagating `CcInfo`) | List of labels | optional | `[]` | - - -## swift_module_alias - -
-swift_module_alias(name, deps, module_name)
-
- -Creates a Swift module that re-exports other modules. - -This rule effectively creates an "alias" for one or more modules such that a -client can import the alias module and it will implicitly import those -dependencies. It should be used primarily as a way to migrate users when a -module name is being changed. An alias that depends on more than one module can -be used to split a large module into smaller, more targeted modules. - -Symbols in the original modules can be accessed through either the original -module name or the alias module name, so callers can be migrated separately -after moving the physical build target as needed. (An exception to this is -runtime type metadata, which only encodes the module name of the type where the -symbol is defined; it is not repeated by the alias module.) - -> Caution: This rule uses the undocumented `@_exported` feature to re-export the -> `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 | 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_module_mapping diff --git a/examples/xplatform/module_alias/BUILD b/examples/xplatform/module_alias/BUILD deleted file mode 100644 index bdd567b28..000000000 --- a/examples/xplatform/module_alias/BUILD +++ /dev/null @@ -1,19 +0,0 @@ -load("//swift:swift_binary.bzl", "swift_binary") -load("//swift:swift_library.bzl", "swift_library") -load("//swift:swift_module_alias.bzl", "swift_module_alias") - -swift_binary( - name = "hello_world", - deps = [":main_alias"], -) - -swift_library( - name = "main", - srcs = ["main.swift"], -) - -swift_module_alias( - name = "main_alias", - module_name = "Main_Alias", - deps = [":main"], -) diff --git a/examples/xplatform/module_alias/main.swift b/examples/xplatform/module_alias/main.swift deleted file mode 100644 index 81f6da727..000000000 --- a/examples/xplatform/module_alias/main.swift +++ /dev/null @@ -1,15 +0,0 @@ -// 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. - -print("Hello, world!") diff --git a/swift/BUILD b/swift/BUILD index 0239353f5..a3066e3de 100644 --- a/swift/BUILD +++ b/swift/BUILD @@ -218,23 +218,6 @@ bzl_library( ], ) -bzl_library( - name = "swift_module_alias", - srcs = ["swift_module_alias.bzl"], - deps = [ - ":module_name", - ":providers", - "//swift/internal:attrs", - "//swift/internal:compiling", - "//swift/internal:features", - "//swift/internal:linking", - "//swift/internal:output_groups", - "//swift/internal:toolchain_utils", - "//swift/internal:utils", - "@bazel_skylib//lib:dicts", - ], -) - bzl_library( name = "swift_module_mapping", srcs = ["swift_module_mapping.bzl"], @@ -310,7 +293,6 @@ bzl_library( ":swift_interop_info", ":swift_library", ":swift_library_group", - ":swift_module_alias", ":swift_module_mapping", ":swift_module_mapping_test", ":swift_package_configuration", diff --git a/swift/swift.bzl b/swift/swift.bzl index 0bb92206c..14bb7f81c 100644 --- a/swift/swift.bzl +++ b/swift/swift.bzl @@ -53,7 +53,6 @@ load(":swift_import.bzl", _swift_import = "swift_import") load(":swift_interop_hint.bzl", _swift_interop_hint = "swift_interop_hint") load(":swift_library.bzl", _swift_library = "swift_library") load(":swift_library_group.bzl", _swift_library_group = "swift_library_group") -load(":swift_module_alias.bzl", _swift_module_alias = "swift_module_alias") load( ":swift_package_configuration.bzl", _swift_package_configuration = "swift_package_configuration", @@ -84,7 +83,6 @@ swift_import = _swift_import swift_interop_hint = _swift_interop_hint swift_library = _swift_library swift_library_group = _swift_library_group -swift_module_alias = _swift_module_alias swift_package_configuration = _swift_package_configuration swift_test = _swift_test diff --git a/swift/swift_library_group.bzl b/swift/swift_library_group.bzl index 2d3d7b79f..7025f94ec 100644 --- a/swift/swift_library_group.bzl +++ b/swift/swift_library_group.bzl @@ -56,8 +56,8 @@ Groups Swift compatible libraries (e.g. `swift_library` and `objc_library`). The target can be used anywhere a `swift_library` can be used. It behaves similar to source-less `{cc,obj}_library` targets. -Unlike `swift_module_alias`, a new module isn't created for this target, you -need to import the grouped libraries directly. +A new module isn't created for this target, you need to import the grouped +libraries directly. """, implementation = _swift_library_group_impl, ) diff --git a/swift/swift_module_alias.bzl b/swift/swift_module_alias.bzl deleted file mode 100644 index 9e93e5ecc..000000000 --- a/swift/swift_module_alias.bzl +++ /dev/null @@ -1,178 +0,0 @@ -# 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. - -"""Implementation of the `swift_module_alias` rule.""" - -load("@bazel_skylib//lib:dicts.bzl", "dicts") -load("//swift/internal:attrs.bzl", "swift_toolchain_attrs") -load("//swift/internal:compiling.bzl", "compile") -load("//swift/internal:features.bzl", "configure_features") -load( - "//swift/internal:linking.bzl", - "create_linking_context_from_compilation_outputs", -) -load( - "//swift/internal:output_groups.bzl", - "supplemental_compilation_output_groups", -) -load( - "//swift/internal:toolchain_utils.bzl", - "get_swift_toolchain", - "use_swift_toolchain", -) -load("//swift/internal:utils.bzl", "compact", "get_providers") -load(":module_name.bzl", "derive_swift_module_name") -load(":providers.bzl", "SwiftInfo") - -def _swift_module_alias_impl(ctx): - deps = ctx.attr.deps - module_mapping = { - module.name: dep.label - for dep in deps - for module in dep[SwiftInfo].direct_modules - } - - module_name = ctx.attr.module_name - if not module_name: - module_name = derive_swift_module_name(ctx.label) - - # Generate a source file that imports each of the deps using `@_exported`. - reexport_src = ctx.actions.declare_file( - "{}_exports.swift".format(ctx.label.name), - ) - ctx.actions.write( - content = "\n".join([ - "@_exported import {}".format(module) - for module in module_mapping.keys() - ]), - output = reexport_src, - ) - - swift_toolchain = get_swift_toolchain(ctx) - feature_configuration = configure_features( - ctx = ctx, - requested_features = ctx.features, - swift_toolchain = swift_toolchain, - unsupported_features = ctx.disabled_features, - ) - - swift_infos = get_providers(deps, SwiftInfo) - - compile_result = compile( - actions = ctx.actions, - cc_infos = get_providers(ctx.attr.deps, CcInfo), - copts = ["-parse-as-library"], - feature_configuration = feature_configuration, - include_dev_srch_paths = ctx.attr.testonly, - module_name = module_name, - package_name = None, - srcs = [reexport_src], - swift_infos = swift_infos, - swift_toolchain = swift_toolchain, - target_name = ctx.label.name, - workspace_name = ctx.workspace_name, - ) - - module_context = compile_result.module_context - compilation_outputs = compile_result.compilation_outputs - supplemental_outputs = compile_result.supplemental_outputs - - linking_context, linking_output = ( - create_linking_context_from_compilation_outputs( - actions = ctx.actions, - compilation_outputs = compilation_outputs, - feature_configuration = feature_configuration, - include_dev_srch_paths = ctx.attr.testonly, - label = ctx.label, - linking_contexts = [ - dep[CcInfo].linking_context - for dep in deps - if CcInfo in dep - ], - module_context = module_context, - swift_toolchain = swift_toolchain, - ) - ) - - return [ - DefaultInfo( - files = depset(compact([ - module_context.swift.swiftdoc, - module_context.swift.swiftinterface, - module_context.swift.private_swiftinterface, - module_context.swift.swiftmodule, - linking_output.library_to_link.pic_static_library, - linking_output.library_to_link.static_library, - ])), - ), - OutputGroupInfo( - **supplemental_compilation_output_groups(supplemental_outputs) - ), - coverage_common.instrumented_files_info( - ctx, - dependency_attributes = ["deps"], - ), - CcInfo( - compilation_context = module_context.clang.compilation_context, - linking_context = linking_context, - ), - compile_result.swift_info, - ] - -swift_module_alias = rule( - attrs = dicts.add( - swift_toolchain_attrs(), - { - "deps": attr.label_list( - doc = """\ -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`). -""", - providers = [[SwiftInfo]], - ), - "module_name": attr.string( - doc = """\ -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. -""", - ), - }, - ), - doc = """\ -Creates a Swift module that re-exports other modules. - -This rule effectively creates an "alias" for one or more modules such that a -client can import the alias module and it will implicitly import those -dependencies. It should be used primarily as a way to migrate users when a -module name is being changed. An alias that depends on more than one module can -be used to split a large module into smaller, more targeted modules. - -Symbols in the original modules can be accessed through either the original -module name or the alias module name, so callers can be migrated separately -after moving the physical build target as needed. (An exception to this is -runtime type metadata, which only encodes the module name of the type where the -symbol is defined; it is not repeated by the alias module.) - -> Caution: This rule uses the undocumented `@_exported` feature to re-export the -> `deps` in the new module. You depend on undocumented features at your own -> risk, as they may change in a future version of Swift. -""", - fragments = ["cpp"], - implementation = _swift_module_alias_impl, - toolchains = use_swift_toolchain(), -) From 9787c73c4fdcd9ef6a57c4ba40231908beaf5d43 Mon Sep 17 00:00:00 2001 From: Brentley Jones Date: Tue, 22 Oct 2024 09:26:45 -0500 Subject: [PATCH 13/92] Update `doc/README.md` (#1428) Removes out of date/redundant information by pointing to the sub-docs. Signed-off-by: Brentley Jones --- doc/README.md | 27 +++++---------------------- 1 file changed, 5 insertions(+), 22 deletions(-) diff --git a/doc/README.md b/doc/README.md index 8bf19d2fa..f3cc3c768 100644 --- a/doc/README.md +++ b/doc/README.md @@ -1,23 +1,6 @@ -# Swift Build Definitions +# Documentation -## BUILD Rules - -* [swift_binary](rules.md#swift_binary) -* [swift_import](rules.md#swift_import) -* [swift_interop_hint](rules.md#swift_interop_hint) -* [swift_library](rules.md#swift_library) -* [swift_proto_library](rules.md#swift_proto_library) -* [swift_test](rules.md#swift_test) - -## Repository Setup - -* [swift_rules_dependencies](setup.md#swift_rules_dependencies) - -## Low-level Skylark API - -* [swift_common](api.md) module - -## Skylark Providers - -* [SwiftInfo](providers.md#SwiftInfo) -* [SwiftToolchainInfo](providers.md#SwiftToolchainInfo) +* [WORKSPACE setup](setup.md#swift_rules_dependencies) +* [Rules](rules.md) +* [Providers](providers.md) +* [API](api.md) From 2e6628d2ace4895cee1720cf2feb2e2fcbf9bd18 Mon Sep 17 00:00:00 2001 From: Thomas Van Lenten Date: Mon, 21 Aug 2023 06:06:40 -0700 Subject: [PATCH 14/92] Change `swift_library` `alwayslink` default to True This also changes the default for the `swift_common.create_linking_context_from_compilation_outputs` api. SwiftPM works by linking all the .o files, and how .swift sources map to .o files can be dependent on the compiler options, so this should end up in a state that seem users familiar with SwiftPM/Xcode will expect. Remove the private feature that was supporting this by honoring the objc flag. PiperOrigin-RevId: 558766347 (cherry picked from commit c58d81a5fedcee78997dd085185d5744ad7d9e26) Signed-off-by: Brentley Jones --- doc/api.md | 2 +- doc/rules.md | 4 ++-- swift/internal/attrs.bzl | 28 +++++++++++++++------- swift/internal/feature_names.bzl | 4 ---- swift/internal/features.bzl | 8 +------ swift/internal/linking.bzl | 22 ++++++----------- swift/toolchains/swift_toolchain.bzl | 1 - swift/toolchains/xcode_swift_toolchain.bzl | 3 +-- 8 files changed, 32 insertions(+), 40 deletions(-) diff --git a/doc/api.md b/doc/api.md index 9a5846d52..c002b9a24 100644 --- a/doc/api.md +++ b/doc/api.md @@ -248,7 +248,7 @@ command line parameters file, those actions will be created here. | :------------- | :------------- | :------------- | | actions | The context's `actions` object. | none | | additional_inputs | A `list` of `File`s containing any additional files that are referenced by `user_link_flags` and therefore need to be propagated up to the linker. | `[]` | -| alwayslink | If True, any binary that depends on the providers returned by this function will link in all of the library's object files, even if some contain no symbols referenced by the binary. | `False` | +| alwayslink | If `False`, any binary that depends on the providers returned by this function will link in all of the library's object files only if there are symbol references. See the discussion on `swift_library` `alwayslink` for why that behavior could result in undesired results. | `True` | | compilation_outputs | A `CcCompilationOutputs` value containing the object files to link. Typically, this is the second tuple element in the value returned by `compile`. | none | | feature_configuration | A feature configuration obtained from `configure_features`. | none | | is_test | Deprecated. This argument will be removed in the next major release. Use the `include_dev_srch_paths` attribute instead. Represents if the `testonly` value of the context. | `None` | diff --git a/doc/rules.md b/doc/rules.md index fdf8da713..f48af6a80 100644 --- a/doc/rules.md +++ b/doc/rules.md @@ -466,7 +466,7 @@ Compiles and links Swift code into a static library and Swift module. | srcs | A list of `.swift` source files that will be compiled into the library. | 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 | `[]` | | always_include_developer_search_paths | If `True`, the developer framework search paths will be added to the compilation command. This enables a Swift module to access `XCTest` without having to mark the target as `testonly = True`. | Boolean | optional | `False` | -| 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` | +| alwayslink | If `False`, any binary that depends (directly or indirectly) on this Swift module will only link in all the object files for the files listed in `srcs` when there is a direct symbol reference.

Swift protocol conformances don't create linker references. Likewise, if the Swift code has Objective-C classes/methods, their usage does not always result in linker references.

_"All the object files"_ for this module is also somewhat fuzzy. Unlike C, C++, and Objective-C, where each source file results in a `.o` file; for Swift the number of .o files depends on the compiler options (`-wmo`/`-whole-module-optimization`, `-num-threads`). That makes relying on linker reference more fragile, and any individual .swift file in `srcs` may/may not get picked up based on the linker references to other files that happen to get batched into a single `.o` by the compiler options used.

Swift Package Manager always passes the individual `.o` files to the linker instead of using intermediate static libraries, so it effectively is the same as `alwayslink = True`. | Boolean | optional | `True` | | 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 | `[]` | | 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 | `[]` | | 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 | `""` | @@ -716,7 +716,7 @@ swift_proto_library( | additional_compiler_deps | List of additional dependencies required by the generated Swift code at compile time, whose SwiftProtoInfo will be ignored.

Allowed kinds of dependencies are:

* `swift_library` (or anything propagating `SwiftInfo`)

* `cc_library` and `objc_library` (or anything propagating `CcInfo`) | List of labels | optional | `[]` | | additional_compiler_info | Dictionary of additional information passed to the compiler targets. See the documentation of the respective compiler rules for more information on which fields are accepted and how they are used. | Dictionary: String -> String | optional | `{}` | | always_include_developer_search_paths | If `True`, the developer framework search paths will be added to the compilation command. This enables a Swift module to access `XCTest` without having to mark the target as `testonly = True`. | Boolean | optional | `False` | -| 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` | +| alwayslink | If `False`, any binary that depends (directly or indirectly) on this Swift module will only link in all the object files for the files listed in `srcs` when there is a direct symbol reference.

Swift protocol conformances don't create linker references. Likewise, if the Swift code has Objective-C classes/methods, their usage does not always result in linker references.

_"All the object files"_ for this module is also somewhat fuzzy. Unlike C, C++, and Objective-C, where each source file results in a `.o` file; for Swift the number of .o files depends on the compiler options (`-wmo`/`-whole-module-optimization`, `-num-threads`). That makes relying on linker reference more fragile, and any individual .swift file in `srcs` may/may not get picked up based on the linker references to other files that happen to get batched into a single `.o` by the compiler options used.

Swift Package Manager always passes the individual `.o` files to the linker instead of using intermediate static libraries, so it effectively is the same as `alwayslink = True`. | Boolean | optional | `True` | | compilers | One or more `swift_proto_compiler` targets (or targets producing `SwiftProtoCompilerInfo`), from which the Swift protos will be generated. | List of labels | optional | `["@rules_swift//proto/compilers:swift_proto"]` | | 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 | `[]` | | 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 | `[]` | diff --git a/swift/internal/attrs.bzl b/swift/internal/attrs.bzl index 944a991a0..de08d6acf 100644 --- a/swift/internal/attrs.bzl +++ b/swift/internal/attrs.bzl @@ -297,15 +297,27 @@ and emit a `.swiftinterface` file as one of the compilation outputs. mandatory = False, ), "alwayslink": attr.bool( - default = False, + default = True, doc = """\ -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. +If `False`, any binary that depends (directly or indirectly) on this Swift module +will only link in all the object files for the files listed in `srcs` when there +is a direct symbol reference. + +Swift protocol conformances don't create linker references. Likewise, if the +Swift code has Objective-C classes/methods, their usage does not always result in +linker references. + +_"All the object files"_ for this module is also somewhat fuzzy. Unlike C, C++, +and Objective-C, where each source file results in a `.o` file; for Swift the +number of .o files depends on the compiler options +(`-wmo`/`-whole-module-optimization`, `-num-threads`). That makes relying on +linker reference more fragile, and any individual .swift file in `srcs` may/may +not get picked up based on the linker references to other files that happen to +get batched into a single `.o` by the compiler options used. + +Swift Package Manager always passes the individual `.o` files to the linker +instead of using intermediate static libraries, so it effectively is the same +as `alwayslink = True`. """, ), "generated_header_name": attr.string( diff --git a/swift/internal/feature_names.bzl b/swift/internal/feature_names.bzl index 2479f1485..2db90111c 100644 --- a/swift/internal/feature_names.bzl +++ b/swift/internal/feature_names.bzl @@ -342,10 +342,6 @@ SWIFT_FEATURE_LLD_GC_WORKAROUND = "swift.lld_gc_workaround" # objects if you know that isn't required. SWIFT_FEATURE_OBJC_LINK_FLAGS = "swift.objc_link_flag" -# A private feature that is set by the toolchain if the given toolchain wants -# all Swift compilations to always be linked. -SWIFT_FEATURE__FORCE_ALWAYSLINK_TRUE = "swift._force_alwayslink_true" - # If enabled, requests the `-enforce-exclusivity=checked` swiftc flag which # enables runtime checking of exclusive memory access on mutation. SWIFT_FEATURE_CHECKED_EXCLUSIVITY = "swift.checked_exclusivity" diff --git a/swift/internal/features.bzl b/swift/internal/features.bzl index 5e471c4fe..48e9acb53 100644 --- a/swift/internal/features.bzl +++ b/swift/internal/features.bzl @@ -39,7 +39,6 @@ load( "SWIFT_FEATURE_OPT_USES_WMO", "SWIFT_FEATURE_REMAP_XCODE_PATH", "SWIFT_FEATURE_USE_GLOBAL_MODULE_CACHE", - "SWIFT_FEATURE__FORCE_ALWAYSLINK_TRUE", "SWIFT_FEATURE__SUPPORTS_V6", ) load(":package_specs.bzl", "label_matches_package_specs") @@ -229,7 +228,7 @@ def is_feature_enabled(feature_configuration, feature_name): feature_name = feature_name, ) -def default_features_for_toolchain(ctx, target_triple): +def default_features_for_toolchain(target_triple): """Enables a common set of swift features based on build configuration. We have a common set of features we'd like to enable for both @@ -238,7 +237,6 @@ def default_features_for_toolchain(ctx, target_triple): what platform we're targetting (linux, macos, ios, etc.). Args: - ctx: Context of the swift toolchain rule building this list of features. target_triple: Target triple configured for our toolchain. Returns: @@ -271,13 +269,9 @@ def default_features_for_toolchain(ctx, target_triple): SWIFT_FEATURE_REMAP_XCODE_PATH, ]) - if getattr(ctx.fragments.objc, "alwayslink_by_default", False): - features.append(SWIFT_FEATURE__FORCE_ALWAYSLINK_TRUE) - # Linux specific features if target_triples.unversioned_os(target_triple) == "linux": features.extend([ - SWIFT_FEATURE__FORCE_ALWAYSLINK_TRUE, SWIFT_FEATURE_NO_GENERATED_MODULE_MAP, ]) diff --git a/swift/internal/linking.bzl b/swift/internal/linking.bzl index 77ed337b9..2467ba2b5 100644 --- a/swift/internal/linking.bzl +++ b/swift/internal/linking.bzl @@ -28,15 +28,11 @@ load( "ensure_swiftmodule_is_embedded", "should_embed_swiftmodule_for_debugging", ) -load( - ":developer_dirs.bzl", - "developer_dirs_linkopts", -) +load(":developer_dirs.bzl", "developer_dirs_linkopts") load( ":feature_names.bzl", "SWIFT_FEATURE_LLD_GC_WORKAROUND", "SWIFT_FEATURE_OBJC_LINK_FLAGS", - "SWIFT_FEATURE__FORCE_ALWAYSLINK_TRUE", ) load( ":features.bzl", @@ -272,7 +268,7 @@ def create_linking_context_from_compilation_outputs( *, actions, additional_inputs = [], - alwayslink = False, + alwayslink = True, compilation_outputs, feature_configuration, is_test = None, @@ -297,9 +293,11 @@ def create_linking_context_from_compilation_outputs( additional_inputs: A `list` of `File`s containing any additional files that are referenced by `user_link_flags` and therefore need to be propagated up to the linker. - alwayslink: If True, any binary that depends on the providers returned - by this function will link in all of the library's object files, - even if some contain no symbols referenced by the binary. + alwayslink: If `False`, any binary that depends on the providers + returned by this function will link in all of the library's object + files only if there are symbol references. See the discussion on + `swift_library` `alwayslink` for why that behavior could result + in undesired results. compilation_outputs: A `CcCompilationOutputs` value containing the object files to link. Typically, this is the second tuple element in the value returned by `compile`. @@ -361,12 +359,6 @@ def create_linking_context_from_compilation_outputs( if autolink_linking_context: extra_linking_contexts.append(autolink_linking_context) - if not alwayslink: - alwayslink = is_feature_enabled( - feature_configuration = feature_configuration, - feature_name = SWIFT_FEATURE__FORCE_ALWAYSLINK_TRUE, - ) - if is_feature_enabled( feature_configuration = feature_configuration, feature_name = SWIFT_FEATURE_LLD_GC_WORKAROUND, diff --git a/swift/toolchains/swift_toolchain.bzl b/swift/toolchains/swift_toolchain.bzl index 892fb37fa..23e87b59d 100644 --- a/swift/toolchains/swift_toolchain.bzl +++ b/swift/toolchains/swift_toolchain.bzl @@ -456,7 +456,6 @@ def _swift_toolchain_impl(ctx): features_from_swiftcopts(swiftcopts = swiftcopts) ) requested_features.extend(default_features_for_toolchain( - ctx = ctx, target_triple = target_triple, )) diff --git a/swift/toolchains/xcode_swift_toolchain.bzl b/swift/toolchains/xcode_swift_toolchain.bzl index caca31197..1cd8fee8d 100644 --- a/swift/toolchains/xcode_swift_toolchain.bzl +++ b/swift/toolchains/xcode_swift_toolchain.bzl @@ -181,7 +181,7 @@ def _swift_linkopts_cc_info( linkopts.extend([ "-L{}".format(swift_lib_dir), "-L/usr/lib/swift", - # TODO(b/112000244): These should get added by the C++ Starlark API, + # TODO(b/112000244): This 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 @@ -604,7 +604,6 @@ def _xcode_swift_toolchain_impl(ctx): ) + wmo_features_from_swiftcopts(swiftcopts = swiftcopts) requested_features.extend(ctx.features) requested_features.extend(default_features_for_toolchain( - ctx = ctx, target_triple = target_triple, )) From 8628c876c183a8aef3804d2a6a709095f60f6840 Mon Sep 17 00:00:00 2001 From: Brentley Jones Date: Tue, 22 Oct 2024 09:49:36 -0500 Subject: [PATCH 15/92] Add back API docs for function removed from `swift_common` (#1429) Missed in e76d714c98ad2b621bd571faeefb836d263fd460. --------- Signed-off-by: Brentley Jones --- doc/BUILD | 12 ++++-- doc/api.md | 109 ++++++++++++++++++++++++++++++++++++++++++++++++++-- doc/doc.bzl | 5 +++ 3 files changed, 120 insertions(+), 6 deletions(-) diff --git a/doc/BUILD b/doc/BUILD index 212073658..3b7cc27b2 100644 --- a/doc/BUILD +++ b/doc/BUILD @@ -6,6 +6,8 @@ load("@io_bazel_stardoc//stardoc:stardoc.bzl", "stardoc") _DOC_SRCS = { "api": [ + "create_swift_interop_info", + "derive_swift_module_name", "swift_common", ], "providers": [ @@ -45,9 +47,13 @@ write_file( "", "# 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.", + "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.", + "", + "Some API is exposed as free functions outside of the `swift_common`", + "module.", ], ) diff --git a/doc/api.md b/doc/api.md index c002b9a24..7d1aaa08f 100644 --- a/doc/api.md +++ b/doc/api.md @@ -1,9 +1,112 @@ # 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. +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. + +Some API is exposed as free functions outside of the `swift_common` +module. + + +## create_swift_interop_info + +
+create_swift_interop_info(exclude_headers, module_map, module_name, requested_features, suppressed,
+                          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 do the following: + +* Add `swift_clang_module_aspect` to any attribute that provides + dependencies of the code that needs to interop with Swift (typically + `deps`, but could be other attributes as well, such as attributes + providing additional support libraries). +* Have the rule implementation call `create_swift_interop_info`, passing + it only the list of `SwiftInfo` providers from its dependencies. This + tells `swift_clang_module_aspect` when it runs on *this* rule's target + to derive the module name from the target label and create a module map + using the headers from the compilation context of the `CcInfo` you + propagate. + +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** collect dependency providers automatically. This allows the +rule to not only add extra dependencies (such as support libraries from +implicit attributes) but also to exclude dependencies if necessary. + + +**PARAMETERS** + + +| Name | Description | Default Value | +| :------------- | :------------- | :------------- | +| exclude_headers | A `list` of `File`s representing headers that should be excluded from the module if the module map is generated. | `[]` | +| 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. | `[]` | +| suppressed | A `bool` indicating whether the module that the aspect would create for the target should instead be suppressed. | `False` | +| 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. + + + + +## derive_swift_module_name + +
+derive_swift_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 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.cc_feature_configuration diff --git a/doc/doc.bzl b/doc/doc.bzl index f3f57cf37..08f882203 100644 --- a/doc/doc.bzl +++ b/doc/doc.bzl @@ -81,6 +81,10 @@ load( "//swift:swift_interop_hint.bzl", _swift_interop_hint = "swift_interop_hint", ) +load( + "//swift:swift_interop_info.bzl", + _create_swift_interop_info = "create_swift_interop_info", +) load("//swift:swift_library.bzl", _swift_library = "swift_library") load( "//swift:swift_library_group.bzl", @@ -111,6 +115,7 @@ swift_proto_library = _swift_proto_library swift_proto_library_group = _swift_proto_library_group # swift symbols +create_swift_interop_info = _create_swift_interop_info derive_swift_module_name = _derive_swift_module_name swift_common = _swift_common SwiftInfo = _SwiftInfo From eb2a7ff8312a0b37837836314e372af357b4764a Mon Sep 17 00:00:00 2001 From: Googler Date: Wed, 23 Aug 2023 07:17:32 -0700 Subject: [PATCH 16/92] Remove `swift_common.toolchain_attrs` PiperOrigin-RevId: 559415879 (cherry picked from commit c7cf7c3c428e719b2d7ade197eb56a618cf09328) Cherry-pick notes: Other rulesets will need to fully migrate to using toolchains, and `swift_common.get_swift_toolchain` in particular, with this change. Signed-off-by: Brentley Jones --- doc/api.md | 46 -------------------- mixed_language/internal/library.bzl | 3 +- proto/BUILD | 1 - proto/swift_proto_library_group.bzl | 2 - swift/BUILD | 2 - swift/internal/BUILD | 1 - swift/internal/attrs.bzl | 43 ------------------ swift/internal/swift_symbol_graph_aspect.bzl | 2 - swift/swift_clang_module_aspect.bzl | 5 --- swift/swift_common.bzl | 2 - swift/swift_import.bzl | 2 - 11 files changed, 1 insertion(+), 108 deletions(-) diff --git a/doc/api.md b/doc/api.md index 7d1aaa08f..cd07bc3a7 100644 --- a/doc/api.md +++ b/doc/api.md @@ -486,52 +486,6 @@ A struct containing the precompiled module and optional indexstore directory, or `None` if the toolchain or target does not support precompiled modules. - - -## swift_common.toolchain_attrs - -
-swift_common.toolchain_attrs(toolchain_attr_name)
-
- -Returns an attribute dictionary for toolchain users. - -The returned dictionary contains a key with the name specified by the -argument `toolchain_attr_name` (which defaults to the value `"_toolchain"`), -the value of which is a BUILD API `attr.label` that references the default -Swift toolchain. Users who are authoring custom rules can add this -dictionary to the attributes of their own rule in order to depend on the -toolchain and access its `SwiftToolchainInfo` provider to pass it to other -`swift_common` functions. - -There is a hierarchy to the attribute sets offered by the `swift_common` -API: - -1. If you only need access to the toolchain for its tools and libraries but - are not doing any compilation, use `toolchain_attrs`. -2. If you need to invoke compilation actions but are not making the - resulting object files into a static or shared library, use - `compilation_attrs`. -3. If you want to provide a rule interface that is suitable as a drop-in - replacement for `swift_library`, use `library_rule_attrs`. - -Each of the attribute functions in the list above also contains the -attributes from the earlier items in the list. - - -**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. - - ## swift_common.use_toolchain diff --git a/mixed_language/internal/library.bzl b/mixed_language/internal/library.bzl index 2a59f6240..d0bb5b3ea 100644 --- a/mixed_language/internal/library.bzl +++ b/mixed_language/internal/library.bzl @@ -25,7 +25,7 @@ load( load("//swift:swift_clang_module_aspect.bzl", "swift_clang_module_aspect") # buildifier: disable=bzl-visibility -load("//swift/internal:attrs.bzl", "swift_deps_attr", "swift_toolchain_attrs") +load("//swift/internal:attrs.bzl", "swift_deps_attr") # buildifier: disable=bzl-visibility load( @@ -198,7 +198,6 @@ def _mixed_language_library_impl(ctx): mixed_language_library = rule( attrs = dicts.add( - swift_toolchain_attrs(), { "clang_target": attr.label( doc = """ diff --git a/proto/BUILD b/proto/BUILD index 17555d5dc..ee9d2647e 100644 --- a/proto/BUILD +++ b/proto/BUILD @@ -56,7 +56,6 @@ bzl_library( ":swift_proto_utils", "//swift:module_name", "//swift:providers", - "//swift:swift_common", "//swift/internal:toolchain_utils", "//swift/internal:utils", "@bazel_skylib//lib:dicts", diff --git a/proto/swift_proto_library_group.bzl b/proto/swift_proto_library_group.bzl index 4acd101f8..5c3452996 100644 --- a/proto/swift_proto_library_group.bzl +++ b/proto/swift_proto_library_group.bzl @@ -36,7 +36,6 @@ load( "SwiftProtoCompilerInfo", "SwiftProtoInfo", ) -load("//swift:swift_common.bzl", "swift_common") # buildifier: disable=bzl-visibility load("//swift/internal:toolchain_utils.bzl", "use_swift_toolchain") @@ -73,7 +72,6 @@ def _swift_proto_library_group_aspect_impl(target, aspect_ctx): _swift_proto_library_group_aspect = aspect( attr_aspects = ["deps"], attrs = dicts.add( - swift_common.toolchain_attrs(), { "_compiler": attr.label( default = Label("//proto:_swift_proto_compiler"), diff --git a/swift/BUILD b/swift/BUILD index a3066e3de..3b1a1c0a1 100644 --- a/swift/BUILD +++ b/swift/BUILD @@ -76,7 +76,6 @@ bzl_library( deps = [ ":module_name", ":providers", - "//swift/internal:attrs", "//swift/internal:compiling", "//swift/internal:feature_names", "//swift/internal:features", @@ -91,7 +90,6 @@ bzl_library( name = "swift_common", srcs = ["swift_common.bzl"], deps = [ - "//swift/internal:attrs", "//swift/internal:compiling", "//swift/internal:features", "//swift/internal:linking", diff --git a/swift/internal/BUILD b/swift/internal/BUILD index 0dc92fa70..2eb200941 100644 --- a/swift/internal/BUILD +++ b/swift/internal/BUILD @@ -228,7 +228,6 @@ bzl_library( srcs = ["swift_symbol_graph_aspect.bzl"], visibility = ["//swift:__subpackages__"], deps = [ - ":attrs", ":features", ":providers", ":symbol_graph_extracting", diff --git a/swift/internal/attrs.bzl b/swift/internal/attrs.bzl index de08d6acf..1b97c5fb1 100644 --- a/swift/internal/attrs.bzl +++ b/swift/internal/attrs.bzl @@ -102,7 +102,6 @@ def swift_compilation_attrs( additional_deps_aspects = additional_deps_aspects, additional_deps_providers = additional_deps_providers, ), - swift_toolchain_attrs(), { "copts": attr.string_list( doc = """\ @@ -369,48 +368,6 @@ potentially avoid a PLT relocation). Set to `False` to build a `.so` or `.dll`. }, ) -def swift_toolchain_attrs(toolchain_attr_name = "_toolchain"): - """Returns an attribute dictionary for toolchain users. - - The returned dictionary contains a key with the name specified by the - argument `toolchain_attr_name` (which defaults to the value `"_toolchain"`), - the value of which is a BUILD API `attr.label` that references the default - Swift toolchain. Users who are authoring custom rules can add this - dictionary to the attributes of their own rule in order to depend on the - toolchain and access its `SwiftToolchainInfo` provider to pass it to other - `swift_common` functions. - - There is a hierarchy to the attribute sets offered by the `swift_common` - API: - - 1. If you only need access to the toolchain for its tools and libraries but - are not doing any compilation, use `toolchain_attrs`. - 2. If you need to invoke compilation actions but are not making the - resulting object files into a static or shared library, use - `compilation_attrs`. - 3. If you want to provide a rule interface that is suitable as a drop-in - replacement for `swift_library`, use `library_rule_attrs`. - - Each of the attribute functions in the list above also contains the - attributes from the earlier items in the list. - - Args: - 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. - - Returns: - A new attribute dictionary that can be added to the attributes of a - custom build rule to provide access to the Swift toolchain. - """ - return { - toolchain_attr_name: attr.label( - default = Label("@build_bazel_rules_swift_local_config//:toolchain"), - ), - } - def swift_toolchain_driver_attrs(): """Returns attributes used to attach custom drivers to toolchains. diff --git a/swift/internal/swift_symbol_graph_aspect.bzl b/swift/internal/swift_symbol_graph_aspect.bzl index 90d8616a9..9a45a9b4f 100644 --- a/swift/internal/swift_symbol_graph_aspect.bzl +++ b/swift/internal/swift_symbol_graph_aspect.bzl @@ -24,7 +24,6 @@ file in the parent directory. load("@bazel_skylib//lib:dicts.bzl", "dicts") load("//swift:providers.bzl", "SwiftInfo", "SwiftSymbolGraphInfo") -load("//swift/internal:attrs.bzl", "swift_toolchain_attrs") load("//swift/internal:features.bzl", "configure_features") load("//swift/internal:symbol_graph_extracting.bzl", "extract_symbol_graph") load( @@ -148,7 +147,6 @@ def make_swift_symbol_graph_aspect( return aspect( attr_aspects = ["deps"], attrs = dicts.add( - swift_toolchain_attrs(), { # TODO: use `attr.bool` once https://github.com/bazelbuild/bazel/issues/22809 is resolved. "emit_extension_block_symbols": attr.string( diff --git a/swift/swift_clang_module_aspect.bzl b/swift/swift_clang_module_aspect.bzl index 0946afb94..1851a96c0 100644 --- a/swift/swift_clang_module_aspect.bzl +++ b/swift/swift_clang_module_aspect.bzl @@ -15,7 +15,6 @@ """Propagates unified `SwiftInfo` providers for C/Objective-C targets.""" load("@bazel_skylib//lib:sets.bzl", "sets") -load("//swift/internal:attrs.bzl", "swift_toolchain_attrs") load("//swift/internal:compiling.bzl", "precompile_clang_module") load( "//swift/internal:feature_names.bzl", @@ -561,7 +560,6 @@ def _swift_clang_module_aspect_impl(target, aspect_ctx): swift_toolchain = get_swift_toolchain( aspect_ctx, - attr = "_toolchain_for_aspect", ) feature_configuration = configure_features( ctx = aspect_ctx, @@ -595,9 +593,6 @@ def _swift_clang_module_aspect_impl(target, aspect_ctx): swift_clang_module_aspect = aspect( attr_aspects = _MULTIPLE_TARGET_ASPECT_ATTRS, - attrs = swift_toolchain_attrs( - toolchain_attr_name = "_toolchain_for_aspect", - ), doc = """\ Propagates unified `SwiftInfo` providers for targets that represent C/Objective-C modules. diff --git a/swift/swift_common.bzl b/swift/swift_common.bzl index aec38dd6b..c50eb37c7 100644 --- a/swift/swift_common.bzl +++ b/swift/swift_common.bzl @@ -20,7 +20,6 @@ example, `swift_proto_library` generates Swift source code from `.proto` files and then needs to compile them. This module provides that lower-level interface. """ -load("//swift/internal:attrs.bzl", "swift_toolchain_attrs") load( "//swift/internal:compiling.bzl", "compile", @@ -61,6 +60,5 @@ swift_common = struct( get_toolchain = get_swift_toolchain, is_enabled = is_feature_enabled, precompile_clang_module = precompile_clang_module, - toolchain_attrs = swift_toolchain_attrs, use_toolchain = use_swift_toolchain, ) diff --git a/swift/swift_import.bzl b/swift/swift_import.bzl index d2b66691b..48178162a 100644 --- a/swift/swift_import.bzl +++ b/swift/swift_import.bzl @@ -18,7 +18,6 @@ load("@bazel_skylib//lib:dicts.bzl", "dicts") load( "//swift/internal:attrs.bzl", "swift_common_rule_attrs", - "swift_toolchain_attrs", ) load("//swift/internal:compiling.bzl", "compile_module_interface") load( @@ -156,7 +155,6 @@ def _swift_import_impl(ctx): swift_import = rule( attrs = dicts.add( swift_common_rule_attrs(), - swift_toolchain_attrs(), { "archives": attr.label_list( allow_empty = True, From 6d8407e4c8e37b24f96749f49c5de0dbefce16da Mon Sep 17 00:00:00 2001 From: Nevena Kotlaja Date: Tue, 27 Feb 2024 12:08:33 -0800 Subject: [PATCH 17/92] Remove implicit dependencies of `_allowlist_function_transition` in third_party Rules are not required to have an implicit dependencies on the transition allowlist since Bazel knows where the file is. PiperOrigin-RevId: 610831569 (cherry picked from commit 589e42ce5c96030ee0970d3d59a6a2ce03362fde) Signed-off-by: Brentley Jones --- proto/swift_proto_library_group.bzl | 5 ----- swift/swift_compiler_plugin.bzl | 5 ----- test/fixtures/module_mapping/apply_mapping.bzl | 5 ----- 3 files changed, 15 deletions(-) diff --git a/proto/swift_proto_library_group.bzl b/proto/swift_proto_library_group.bzl index 5c3452996..89b4c85b4 100644 --- a/proto/swift_proto_library_group.bzl +++ b/proto/swift_proto_library_group.bzl @@ -133,11 +133,6 @@ def _swift_proto_library_group_impl(ctx): swift_proto_library_group = rule( attrs = { - "_allowlist_function_transition": attr.label( - default = Label( - "@bazel_tools//tools/allowlists/function_transition_allowlist", - ), - ), "compiler": attr.label( default = Label("//proto/compilers:swift_proto"), doc = """\ diff --git a/swift/swift_compiler_plugin.bzl b/swift/swift_compiler_plugin.bzl index 20a4bb530..3d6b1bb40 100644 --- a/swift/swift_compiler_plugin.bzl +++ b/swift/swift_compiler_plugin.bzl @@ -354,11 +354,6 @@ universal_swift_compiler_plugin = rule( mandatory = True, providers = [[SwiftBinaryInfo, SwiftCompilerPluginInfo]], ), - "_allowlist_function_transition": attr.label( - default = Label( - "@bazel_tools//tools/allowlists/function_transition_allowlist", - ), - ), # TODO(b/301253335): Enable AEGs and switch from `swift` exec_group to swift `toolchain` param. "_use_auto_exec_groups": attr.bool(default = False), }, diff --git a/test/fixtures/module_mapping/apply_mapping.bzl b/test/fixtures/module_mapping/apply_mapping.bzl index b7fc8fbd3..329b2b3d9 100644 --- a/test/fixtures/module_mapping/apply_mapping.bzl +++ b/test/fixtures/module_mapping/apply_mapping.bzl @@ -32,11 +32,6 @@ apply_mapping = rule( attrs = { "mapping": attr.label(), "target": attr.label(cfg = apply_mapping_transition), - "_allowlist_function_transition": attr.label( - default = Label( - "@bazel_tools//tools/allowlists/function_transition_allowlist", - ), - ), }, implementation = _apply_mapping_impl, ) From d4104d7c43de9252fea928d6e49e6345479ca32c Mon Sep 17 00:00:00 2001 From: Tony Allevato Date: Wed, 1 May 2024 11:40:30 -0700 Subject: [PATCH 18/92] Remove the `emit_swiftinterface` build setting This was only present to support Apple framework rules, but its design was flawed, so they no longer use it. Library evolution shouldn't be enabled for entire dependency subgraphs; authors of SDKs should explicitly enable it on their libraries with the `library_evolution` attribute. PiperOrigin-RevId: 629786303 (cherry picked from commit 1ee51c8cc980e8e0a9cd7f21e99c5e424fbc3afa) Cherry-pick notes: Applied the same change to the `emit_private_swiftinterface` build setting. Signed-off-by: Brentley Jones --- swift/BUILD | 15 +-------------- swift/internal/attrs.bzl | 6 ------ swift/swift_library.bzl | 14 +------------- 3 files changed, 2 insertions(+), 33 deletions(-) diff --git a/swift/BUILD b/swift/BUILD index 3b1a1c0a1..980fbf619 100644 --- a/swift/BUILD +++ b/swift/BUILD @@ -1,5 +1,5 @@ load("@bazel_skylib//:bzl_library.bzl", "bzl_library") -load("@bazel_skylib//rules:common_settings.bzl", "bool_flag", "bool_setting") +load("@bazel_skylib//rules:common_settings.bzl", "bool_flag") load( "//swift/internal:build_settings.bzl", "per_module_swiftcopt_flag", @@ -350,19 +350,6 @@ per_module_swiftcopt_flag( build_setting_default = "", ) -# Configuration setting for enabling the generation of swiftinterface files. -bool_setting( - name = "emit_swiftinterface", - build_setting_default = False, - visibility = ["//visibility:public"], -) - -# Configuration setting for enabling the generation of private swiftinterface files (e.g. those for `@_spi`). -bool_setting( - name = "emit_private_swiftinterface", - build_setting_default = False, -) - # NOTE: Enabling this flag will transition --proto_compiler to # //tools/protoc_wrapper:protoc for swift_grpc_library and swift_proto_library, # unless you set --proto_compiler manually. diff --git a/swift/internal/attrs.bzl b/swift/internal/attrs.bzl index 1b97c5fb1..680864f5a 100644 --- a/swift/internal/attrs.bzl +++ b/swift/internal/attrs.bzl @@ -186,12 +186,6 @@ def swift_config_attrs(): configuration settings. """ return { - "_config_emit_private_swiftinterface": attr.label( - default = Label("//swift:emit_private_swiftinterface"), - ), - "_config_emit_swiftinterface": attr.label( - default = Label("//swift:emit_swiftinterface"), - ), "_per_module_swiftcopt": attr.label( default = Label("//swift:per_module_swiftcopt"), ), diff --git a/swift/swift_library.bzl b/swift/swift_library.bzl index 8d45693c3..1a13abf2e 100644 --- a/swift/swift_library.bzl +++ b/swift/swift_library.bzl @@ -16,7 +16,6 @@ load("@bazel_skylib//lib:dicts.bzl", "dicts") load("@bazel_skylib//lib:sets.bzl", "sets") -load("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo") load( "//swift/internal:attrs.bzl", "swift_deps_attr", @@ -139,20 +138,9 @@ def _swift_library_impl(ctx): extra_features = [] - # TODO(b/239957001): Remove the global flag. - if ( - ctx.attr.library_evolution or - ctx.attr._config_emit_swiftinterface[BuildSettingInfo].value - ): + if ctx.attr.library_evolution: extra_features.append(SWIFT_FEATURE_ENABLE_LIBRARY_EVOLUTION) extra_features.append(SWIFT_FEATURE_EMIT_SWIFTINTERFACE) - - # TODO(b/239957001): Remove the global flag. - if ( - ctx.attr.library_evolution or - ctx.attr._config_emit_private_swiftinterface[BuildSettingInfo].value - ): - extra_features.append(SWIFT_FEATURE_ENABLE_LIBRARY_EVOLUTION) extra_features.append(SWIFT_FEATURE_EMIT_PRIVATE_SWIFTINTERFACE) module_name = ctx.attr.module_name From 8c644027f84cb029d429275b627a3d30ca93a92f Mon Sep 17 00:00:00 2001 From: Brentley Jones Date: Tue, 22 Oct 2024 13:01:58 -0500 Subject: [PATCH 19/92] Disable `swift.emit_swiftsourceinfo` by default (#1409) Signed-off-by: Brentley Jones --- swift/internal/features.bzl | 2 - test/split_derived_files_tests.bzl | 76 +++++++++++++++++------------- 2 files changed, 43 insertions(+), 35 deletions(-) diff --git a/swift/internal/features.bzl b/swift/internal/features.bzl index 48e9acb53..f9489def0 100644 --- a/swift/internal/features.bzl +++ b/swift/internal/features.bzl @@ -22,7 +22,6 @@ load( "SWIFT_FEATURE_COVERAGE", "SWIFT_FEATURE_COVERAGE_PREFIX_MAP", "SWIFT_FEATURE_DEBUG_PREFIX_MAP", - "SWIFT_FEATURE_DECLARE_SWIFTSOURCEINFO", "SWIFT_FEATURE_DISABLE_CLANG_SPI", "SWIFT_FEATURE_DISABLE_SYSTEM_INDEX", "SWIFT_FEATURE_EMIT_SWIFTDOC", @@ -249,7 +248,6 @@ def default_features_for_toolchain(target_triple): SWIFT_FEATURE_CHECKED_EXCLUSIVITY, SWIFT_FEATURE_COVERAGE_PREFIX_MAP, SWIFT_FEATURE_DEBUG_PREFIX_MAP, - SWIFT_FEATURE_DECLARE_SWIFTSOURCEINFO, SWIFT_FEATURE_DISABLE_CLANG_SPI, SWIFT_FEATURE_DISABLE_SYSTEM_INDEX, SWIFT_FEATURE_EMIT_SWIFTDOC, diff --git a/test/split_derived_files_tests.bzl b/test/split_derived_files_tests.bzl index e53c8b7a8..b53c7cbd6 100644 --- a/test/split_derived_files_tests.bzl +++ b/test/split_derived_files_tests.bzl @@ -8,40 +8,38 @@ load("//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( + +default_no_split_no_emit_swiftdoc_provider_test = make_provider_test_rule( config_settings = { "//command_line_option:features": [ - "swift.split_derived_files_generation", + "-swift.emit_swiftdoc", ], }, ) -split_swiftmodule_provider_test = make_provider_test_rule( + +default_no_split_emit_swiftsourceinfo_provider_test = make_provider_test_rule( config_settings = { "//command_line_option:features": [ - "swift.split_derived_files_generation", + "swift.emit_swiftsourceinfo", ], }, ) -split_swiftmodule_wmo_test = make_action_command_line_test_rule( + +split_swiftmodule_test = make_action_command_line_test_rule( config_settings = { - str(Label("//swift:copt")): [ - "-whole-module-optimization", - ], "//command_line_option:features": [ "swift.split_derived_files_generation", ], }, ) -split_swiftmodule_wmo_provider_test = make_provider_test_rule( +split_swiftmodule_provider_test = make_provider_test_rule( config_settings = { - str(Label("//swift:copt")): [ - "-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 = { str(Label("//swift:copt")): [ @@ -53,36 +51,48 @@ split_swiftmodule_skip_function_bodies_test = make_action_command_line_test_rule ], }, ) -default_no_split_no_emit_swiftdoc_test = make_provider_test_rule( + +split_emit_swiftsourceinfo_provider_test = make_provider_test_rule( config_settings = { "//command_line_option:features": [ - "-swift.emit_swiftdoc", + "swift.emit_swiftsourceinfo", + "swift.split_derived_files_generation", ], }, ) -default_no_split_no_emit_swiftsourceinfo_test = make_provider_test_rule( + +split_no_emit_swiftdoc_provider_test = make_provider_test_rule( config_settings = { "//command_line_option:features": [ - "-swift.emit_swiftsourceinfo", + "-swift.emit_swiftdoc", + "swift.split_derived_files_generation", ], }, ) -split_no_emit_swiftdoc_test = make_provider_test_rule( + +split_swiftmodule_bitcode_test = make_action_command_line_test_rule( config_settings = { "//command_line_option:features": [ - "-swift.emit_swiftdoc", "swift.split_derived_files_generation", + "swift.emit_bc", ], }, ) -split_no_emit_swiftsourceinfo_test = make_provider_test_rule( + +split_swiftmodule_copts_test = make_action_command_line_test_rule( config_settings = { + str(Label("//swift:copt")): [ + "-DHELLO", + ], + "//command_line_option:objccopt": [ + "-DWORLD=1", + ], "//command_line_option:features": [ - "-swift.emit_swiftsourceinfo", "swift.split_derived_files_generation", ], }, ) + split_swiftmodule_indexing_test = make_action_command_line_test_rule( config_settings = { "//command_line_option:features": [ @@ -91,21 +101,21 @@ split_swiftmodule_indexing_test = make_action_command_line_test_rule( ], }, ) -split_swiftmodule_bitcode_test = make_action_command_line_test_rule( + +split_swiftmodule_wmo_test = make_action_command_line_test_rule( config_settings = { + str(Label("//swift:copt")): [ + "-whole-module-optimization", + ], "//command_line_option:features": [ "swift.split_derived_files_generation", - "swift.emit_bc", ], }, ) -split_swiftmodule_copts_test = make_action_command_line_test_rule( +split_swiftmodule_wmo_provider_test = make_provider_test_rule( config_settings = { str(Label("//swift:copt")): [ - "-DHELLO", - ], - "//command_line_option:objccopt": [ - "-DWORLD=1", + "-whole-module-optimization", ], "//command_line_option:features": [ "swift.split_derived_files_generation", @@ -160,7 +170,7 @@ def split_derived_files_test_suite(name, tags = []): target_under_test = "//test/fixtures/debug_settings:simple", ) - default_no_split_provider_test( + default_no_split_emit_swiftsourceinfo_provider_test( name = "{}_default_no_split_provider_swiftsourceinfo".format(name), expected_files = [ "test_fixtures_debug_settings_simple.swiftsourceinfo", @@ -171,7 +181,7 @@ def split_derived_files_test_suite(name, tags = []): target_under_test = "//test/fixtures/debug_settings:simple", ) - default_no_split_no_emit_swiftdoc_test( + default_no_split_no_emit_swiftdoc_provider_test( name = "{}_default_no_split_provider_no_emit_swiftdoc".format(name), expected_files = [ "-test_fixtures_debug_settings_simple.swiftdoc", @@ -182,7 +192,7 @@ def split_derived_files_test_suite(name, tags = []): target_under_test = "//test/fixtures/debug_settings:simple", ) - default_no_split_no_emit_swiftsourceinfo_test( + default_no_split_provider_test( name = "{}_default_no_split_provider_no_emit_swiftsourceinfo".format(name), expected_files = [ "-test_fixtures_debug_settings_simple.swiftsourceinfo", @@ -204,7 +214,7 @@ def split_derived_files_test_suite(name, tags = []): target_under_test = "//test/fixtures/debug_settings:simple", ) - split_swiftmodule_provider_test( + split_emit_swiftsourceinfo_provider_test( name = "{}_split_provider_swiftsourceinfo".format(name), field = "direct_modules.swift.swiftsourceinfo", expected_files = [ @@ -215,7 +225,7 @@ def split_derived_files_test_suite(name, tags = []): target_under_test = "//test/fixtures/debug_settings:simple", ) - split_no_emit_swiftdoc_test( + split_no_emit_swiftdoc_provider_test( name = "{}_split_provider_no_emit_swiftdoc".format(name), field = "direct_modules.swift.swiftdoc", expected_files = [ @@ -226,7 +236,7 @@ def split_derived_files_test_suite(name, tags = []): target_under_test = "//test/fixtures/debug_settings:simple", ) - split_no_emit_swiftsourceinfo_test( + split_swiftmodule_provider_test( name = "{}_split_provider_no_emit_swiftsourceinfo".format(name), field = "direct_modules.swift.swiftsourceinfo", expected_files = [ From e41f3028d30dfb22fe4814324a4302c8cfb66ef8 Mon Sep 17 00:00:00 2001 From: Brentley Jones Date: Tue, 22 Oct 2024 13:55:26 -0500 Subject: [PATCH 20/92] Remove support for the `SWIFT_CUSTOM_TOOLCHAIN` define (#1432) Signed-off-by: Brentley Jones --- swift/toolchains/xcode_swift_toolchain.bzl | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/swift/toolchains/xcode_swift_toolchain.bzl b/swift/toolchains/xcode_swift_toolchain.bzl index 1cd8fee8d..489d76899 100644 --- a/swift/toolchains/xcode_swift_toolchain.bzl +++ b/swift/toolchains/xcode_swift_toolchain.bzl @@ -574,15 +574,12 @@ def _xcode_swift_toolchain_impl(ctx): # attribute, which supports remote builds. # # To use a "standard" custom toolchain built using the full Swift build - # script, use `--define=SWIFT_CUSTOM_TOOLCHAIN=` as shown below. + # script, set the `TOOLCHAINS` envirinment variable as shown below. swift_executable = get_swift_executable_for_toolchain(ctx) toolchain_root = ctx.var.get("SWIFT_USE_TOOLCHAIN_ROOT") - # TODO: Remove SWIFT_CUSTOM_TOOLCHAIN for the next major release - custom_toolchain = ctx.var.get("SWIFT_CUSTOM_TOOLCHAIN") or ctx.configuration.default_shell_env.get("TOOLCHAINS") + custom_toolchain = ctx.configuration.default_shell_env.get("TOOLCHAINS") custom_xcode_toolchain_root = None - if ctx.var.get("SWIFT_CUSTOM_TOOLCHAIN"): - print("WARNING: SWIFT_CUSTOM_TOOLCHAIN is deprecated. Use --action_env=TOOLCHAINS= instead.") # buildifier: disable=print if toolchain_root and custom_toolchain: fail("Do not use SWIFT_USE_TOOLCHAIN_ROOT and TOOLCHAINS" + "in the same build.") From a8d7635f1ce14de720cec94b759b525f04b7797e Mon Sep 17 00:00:00 2001 From: Brentley Jones Date: Tue, 22 Oct 2024 14:25:11 -0500 Subject: [PATCH 21/92] Warn instead of fail when `swiftc` not found on Linux (#1433) Signed-off-by: Brentley Jones --- swift/internal/swift_autoconfiguration.bzl | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/swift/internal/swift_autoconfiguration.bzl b/swift/internal/swift_autoconfiguration.bzl index ddfb5bd03..c120b6981 100644 --- a/swift/internal/swift_autoconfiguration.bzl +++ b/swift/internal/swift_autoconfiguration.bzl @@ -208,7 +208,18 @@ def _create_linux_toolchain(repository_ctx): """ path_to_swiftc = repository_ctx.which("swiftc") if not path_to_swiftc: - fail("No 'swiftc' executable found in $PATH") + print("""\ +No 'swiftc' executable found in $PATH. Not auto-generating a Linux Swift \ +toolchain. +""") # buildifier: disable=print + repository_ctx.file( + "BUILD", + """\ +# No 'swiftc' executable found in $PATH. Not auto-generating a Linux Swift \ +toolchain. +""", + ) + return root = path_to_swiftc.dirname.dirname feature_values = _compute_feature_values(repository_ctx, path_to_swiftc) @@ -223,7 +234,7 @@ def _create_linux_toolchain(repository_ctx): repository_ctx.file( "BUILD", - """ + """\ load( "@build_bazel_rules_swift//swift/toolchains:swift_toolchain.bzl", "swift_toolchain", From 04b2915c0ef1335953a3cdba3addd67bfadcffdf Mon Sep 17 00:00:00 2001 From: Brentley Jones Date: Tue, 22 Oct 2024 14:29:57 -0500 Subject: [PATCH 22/92] Prefer Swift toolchain clang over system clang (#1434) Signed-off-by: Brentley Jones --- .bazelci/presubmit.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.bazelci/presubmit.yml b/.bazelci/presubmit.yml index bdd1b662e..0ca4a71b5 100644 --- a/.bazelci/presubmit.yml +++ b/.bazelci/presubmit.yml @@ -16,7 +16,7 @@ x_defaults: CC: clang SWIFT_VERSION: "5.9.2" SWIFT_HOME: "$HOME/swift-$SWIFT_VERSION" - PATH: "$PATH:$SWIFT_HOME/usr/bin" + PATH: "$SWIFT_HOME/usr/bin:$PATH" linux_common: &linux_common <<: *linux_environment build_flags: &linux_flags From 22f5c3d07bcd64075750d60af844644b7c65eda7 Mon Sep 17 00:00:00 2001 From: Googler Date: Thu, 19 Sep 2024 12:40:27 -0700 Subject: [PATCH 23/92] =?UTF-8?q?Stop=20trying=20to=20extract=20symbol=20g?= =?UTF-8?q?raphs=20from=20actions=20that=20don=E2=80=99t=20compile=20Swift?= =?UTF-8?q?=20or=20Objective-C=20code?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PiperOrigin-RevId: 676522200 (cherry picked from commit ba27dcde25224f6ed5d3fa1ffefedcdeadab3613) Signed-off-by: Brentley Jones --- swift/internal/BUILD | 2 + swift/internal/swift_symbol_graph_aspect.bzl | 55 ++++++++++++-------- 2 files changed, 34 insertions(+), 23 deletions(-) diff --git a/swift/internal/BUILD b/swift/internal/BUILD index 2eb200941..68dd77e6f 100644 --- a/swift/internal/BUILD +++ b/swift/internal/BUILD @@ -233,6 +233,8 @@ bzl_library( ":symbol_graph_extracting", ":toolchain_utils", ":utils", + "//swift:providers", + "//swift:swift_clang_module_aspect", "@bazel_skylib//lib:dicts", ], ) diff --git a/swift/internal/swift_symbol_graph_aspect.bzl b/swift/internal/swift_symbol_graph_aspect.bzl index 9a45a9b4f..5cd1ce220 100644 --- a/swift/internal/swift_symbol_graph_aspect.bzl +++ b/swift/internal/swift_symbol_graph_aspect.bzl @@ -24,6 +24,7 @@ file in the parent directory. load("@bazel_skylib//lib:dicts.bzl", "dicts") load("//swift:providers.bzl", "SwiftInfo", "SwiftSymbolGraphInfo") +load("//swift:swift_clang_module_aspect.bzl", "swift_clang_module_aspect") load("//swift/internal:features.bzl", "configure_features") load("//swift/internal:symbol_graph_extracting.bzl", "extract_symbol_graph") load( @@ -54,30 +55,37 @@ def _swift_symbol_graph_aspect_impl(target, aspect_ctx): emit_extension_block_symbols = aspect_ctx.attr.emit_extension_block_symbols minimum_access_level = aspect_ctx.attr.minimum_access_level - for module in swift_info.direct_modules: - output_dir = aspect_ctx.actions.declare_directory( - "{}.symbolgraphs".format(target.label.name), - ) - extract_symbol_graph( - actions = aspect_ctx.actions, - compilation_contexts = [compilation_context], - emit_extension_block_symbols = emit_extension_block_symbols, - feature_configuration = feature_configuration, - include_dev_srch_paths = include_developer_search_paths( - aspect_ctx.rule.attr, - ), - minimum_access_level = minimum_access_level, - module_name = module.name, - output_dir = output_dir, - swift_infos = [swift_info], - swift_toolchain = swift_toolchain, - ) - symbol_graphs.append( - struct( + # Only extract the symbol graphs of modules when the target compiles Swift or ObjC code + compiles_code = [ + action + for action in target.actions + if action.mnemonic in ("SwiftCompile", "SwiftCompileModule", "SwiftPrecompileCModule") + ] + if compiles_code: + for module in swift_info.direct_modules: + output_dir = aspect_ctx.actions.declare_directory( + "{}.symbolgraphs".format(target.label.name), + ) + extract_symbol_graph( + actions = aspect_ctx.actions, + compilation_contexts = [compilation_context], + emit_extension_block_symbols = emit_extension_block_symbols, + feature_configuration = feature_configuration, + include_dev_srch_paths = include_developer_search_paths( + aspect_ctx.rule.attr, + ), + minimum_access_level = minimum_access_level, module_name = module.name, - symbol_graph_dir = output_dir, - ), - ) + output_dir = output_dir, + swift_infos = [swift_info], + swift_toolchain = swift_toolchain, + ) + symbol_graphs.append( + struct( + module_name = module.name, + symbol_graph_dir = output_dir, + ), + ) # TODO(b/204480390): We intentionally don't propagate symbol graphs from # private deps at this time, since the main use case for them is @@ -190,5 +198,6 @@ default value is {default_value}. fragments = ["cpp"], implementation = aspect_impl, provides = [SwiftSymbolGraphInfo], + requires = [swift_clang_module_aspect], toolchains = use_swift_toolchain(), ) From 28419bce7d48b73759ec46b4ad6fdda2fb0b2c1b Mon Sep 17 00:00:00 2001 From: Tony Allevato Date: Tue, 7 May 2024 07:10:40 -0700 Subject: [PATCH 24/92] Add `swift_overlay` rule PiperOrigin-RevId: 631412228 (cherry picked from commit 8e001a5431685310f34830f58acd0ac6f432b0d2 and 3fcc22be62fb8d173bd411bcf144a19a6d1ccc03) Signed-off-by: Brentley Jones --- doc/BUILD | 1 + doc/doc.bzl | 2 + doc/rules.md | 96 +++++++++ examples/xplatform/overlay/BUILD | 54 +++++ examples/xplatform/overlay/Orientation.swift | 20 ++ .../overlay/RetroLibraryOverlay.swift | 34 +++ examples/xplatform/overlay/RetroTest.swift | 41 ++++ examples/xplatform/overlay/retro_library.c | 24 +++ examples/xplatform/overlay/retro_library.h | 31 +++ swift/BUILD | 20 ++ swift/internal/BUILD | 16 +- swift/internal/binary_attrs.bzl | 105 ++++++++++ swift/internal/linking.bzl | 92 +-------- swift/internal/output_groups.bzl | 8 +- swift/internal/providers.bzl | 55 +++++ swift/providers.bzl | 15 ++ swift/swift_binary.bzl | 7 +- swift/swift_clang_module_aspect.bzl | 194 ++++++++++++++++-- swift/swift_compiler_plugin.bzl | 8 +- swift/swift_library.bzl | 6 +- swift/swift_overlay.bzl | 174 ++++++++++++++++ swift/swift_test.bzl | 2 +- 22 files changed, 894 insertions(+), 111 deletions(-) create mode 100644 examples/xplatform/overlay/BUILD create mode 100644 examples/xplatform/overlay/Orientation.swift create mode 100644 examples/xplatform/overlay/RetroLibraryOverlay.swift create mode 100644 examples/xplatform/overlay/RetroTest.swift create mode 100644 examples/xplatform/overlay/retro_library.c create mode 100644 examples/xplatform/overlay/retro_library.h create mode 100644 swift/internal/binary_attrs.bzl create mode 100644 swift/swift_overlay.bzl diff --git a/doc/BUILD b/doc/BUILD index 3b7cc27b2..2498ccec3 100644 --- a/doc/BUILD +++ b/doc/BUILD @@ -30,6 +30,7 @@ _DOC_SRCS = { "mixed_language_library", "swift_module_mapping", "swift_module_mapping_test", + "swift_overlay", "swift_package_configuration", "swift_test", "swift_proto_library", diff --git a/doc/doc.bzl b/doc/doc.bzl index 08f882203..90fb65af5 100644 --- a/doc/doc.bzl +++ b/doc/doc.bzl @@ -98,6 +98,7 @@ load( "//swift:swift_module_mapping_test.bzl", _swift_module_mapping_test = "swift_module_mapping_test", ) +load("//swift:swift_overlay.bzl", _swift_overlay = "swift_overlay") load( "//swift:swift_package_configuration.bzl", _swift_package_configuration = "swift_package_configuration", @@ -133,5 +134,6 @@ swift_library_group = _swift_library_group mixed_language_library = _mixed_language_library swift_module_mapping = _swift_module_mapping swift_module_mapping_test = _swift_module_mapping_test +swift_overlay = _swift_overlay swift_package_configuration = _swift_package_configuration swift_test = _swift_test diff --git a/doc/rules.md b/doc/rules.md index f48af6a80..a23cb2a3f 100644 --- a/doc/rules.md +++ b/doc/rules.md @@ -27,6 +27,7 @@ On this page: * [mixed_language_library](#mixed_language_library) * [swift_module_mapping](#swift_module_mapping) * [swift_module_mapping_test](#swift_module_mapping_test) + * [swift_overlay](#swift_overlay) * [swift_package_configuration](#swift_package_configuration) * [swift_test](#swift_test) * [swift_proto_library](#swift_proto_library) @@ -604,6 +605,101 @@ remaining modules collected are not present in the `aliases` of the | mapping | The label of a `swift_module_mapping` target against which the transitive closure of `deps` will be validated. | Label | required | | + + +## swift_overlay + +
+swift_overlay(name, deps, srcs, always_include_developer_search_paths, alwayslink, copts, defines,
+              library_evolution, linkopts, linkstatic, package_name, plugins, private_deps,
+              swiftc_inputs)
+
+ +A Swift overlay that sits on top of a C/Objective-C library, allowing an author +of a C/Objective-C library to create additional Swift-specific APIs that are +automatically available when a Swift target depends on their C/Objective-C +library. + +The Swift overlay will only be compiled when other Swift targets depend on the +original library that uses the overlay; non-Swift clients depending on the +original library will not cause the Swift overlay code to be built or linked. +This is done to retain optimium build performance and binary size for non-Swift +clients. For this reason, `swift_overlay` is **not** a general purpose mechanism +for creating mixed-language modules; `swift_overlay` does not support generation +of an Objective-C header. + +The `swift_overlay` rule does not perform any compilation of its own. Instead, +it must be placed in the `aspect_hints` attribute of another rule such as +`objc_library` or `cc_library`. For example, + +```build +objc_library( + name = "MyModule", + srcs = ["MyModule.m"], + hdrs = ["MyModule.h"], + aspect_hints = [":MyModule_overlay"], + deps = [...], +) + +swift_overlay( + name = "MyModule_overlay", + srcs = ["MyModule.swift"], + deps = [...], +) +``` + +When some other Swift target, such as a `swift_library`, depends on `MyModule`, +the Swift code in `MyModule_overlay` will be compiled into the same module. +Therefore, when that library imports `MyModule`, it will see the APIs from the +`objc_library` and the `swift_overlay` as a single combined module. + +When writing a Swift overlay, the Swift code must do a re-exporting import of +its own module in order to access the C/Objective-C APIs; they are not available +automatically. Continuing the example above, any Swift sources that want to use +or extend the API from the C/Objective-C side of the module would need to write +the following: + +```swift +@_exported import MyModule +``` + +The `swift_overlay` rule supports all the same attributes as `swift_library`, +except for the following: + +* `module_name` is not supported because the overlay inherits the same module + name as the target it is attached to. +* `generates_header` and `generated_header_name` are not supported because it + is assumed that the overlay is pure Swift code that does not export any APIs + that would be of interest to C/Objective-C clients. + +Aside from its module name and its underlying C/Objective-C module dependency, +`swift_overlay` does not inherit anything else from its associated target. If +the `swift_overlay` imports any modules other than its C/Objective-C side, the +overlay target must explicitly depend on them as well. This means that an +overlay can have a different set of dependencies than the underlying module, if +desired. + +**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.

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_library` (or anything propagating `SwiftInfo`)

* `cc_library` and `objc_library` (or anything propagating `CcInfo`) | List of labels | optional | `[]` | +| srcs | A list of `.swift` source files that will be compiled into the library. | List of labels | required | | +| always_include_developer_search_paths | If `True`, the developer framework search paths will be added to the compilation command. This enables a Swift module to access `XCTest` without having to mark the target as `testonly = True`. | Boolean | optional | `False` | +| alwayslink | If `False`, any binary that depends (directly or indirectly) on this Swift module will only link in all the object files for the files listed in `srcs` when there is a direct symbol reference.

Swift protocol conformances don't create linker references. Likewise, if the Swift code has Objective-C classes/methods, their usage does not always result in linker references.

_"All the object files"_ for this module is also somewhat fuzzy. Unlike C, C++, and Objective-C, where each source file results in a `.o` file; for Swift the number of .o files depends on the compiler options (`-wmo`/`-whole-module-optimization`, `-num-threads`). That makes relying on linker reference more fragile, and any individual .swift file in `srcs` may/may not get picked up based on the linker references to other files that happen to get batched into a single `.o` by the compiler options used.

Swift Package Manager always passes the individual `.o` files to the linker instead of using intermediate static libraries, so it effectively is the same as `alwayslink = True`. | Boolean | optional | `True` | +| 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 | `[]` | +| 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 | `[]` | +| library_evolution | Indicates whether the Swift code should be compiled with library evolution mode enabled.

This attribute should be used to compile a module that will be distributed as part of a client-facing (non-implementation-only) module in a library or framework that will be distributed for use outside of the Bazel build graph. Setting this to true will compile the module with the `-library-evolution` flag and emit a `.swiftinterface` file as one of the compilation outputs. | 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 | `[]` | +| linkstatic | If True, the Swift module will be built for static linking. This will make all interfaces internal to the module that is being linked against and will inform the consuming module that the objects will be locally available (which may potentially avoid a PLT relocation). Set to `False` to build a `.so` or `.dll`. | Boolean | optional | `True` | +| package_name | The semantic package of the Swift target being built. Targets with the same package_name can access APIs using the 'package' access control modifier in Swift 5.9+. | String | optional | `""` | +| plugins | A list of `swift_compiler_plugin` targets that should be loaded by the compiler when compiling this module and any modules that directly depend on it. | List of labels | 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_library` (or anything propagating `SwiftInfo`)

* `cc_library` and `objc_library` (or anything propagating `CcInfo`) | List of labels | optional | `[]` | +| swiftc_inputs | Additional files that are referenced using `$(location ...)` in attributes that support location expansion. | List of labels | optional | `[]` | + + ## swift_package_configuration diff --git a/examples/xplatform/overlay/BUILD b/examples/xplatform/overlay/BUILD new file mode 100644 index 000000000..220f419d3 --- /dev/null +++ b/examples/xplatform/overlay/BUILD @@ -0,0 +1,54 @@ +load( + "//swift:swift_interop_hint.bzl", + "swift_interop_hint", +) +load( + "//swift:swift_library.bzl", + "swift_library", +) +load( + "//swift:swift_overlay.bzl", + "swift_overlay", +) +load( + "//swift:swift_test.bzl", + "swift_test", +) + +licenses(["notice"]) + +cc_library( + name = "retro_library", + srcs = ["retro_library.c"], + hdrs = ["retro_library.h"], + aspect_hints = [ + ":retro_library_swift_hint", + ":retro_library_swift_overlay", + ], +) + +swift_interop_hint( + name = "retro_library_swift_hint", + module_name = "RetroLibrary", +) + +swift_library( + name = "orientation", + srcs = ["Orientation.swift"], + module_name = "OrientationModule", +) + +swift_overlay( + name = "retro_library_swift_overlay", + srcs = ["RetroLibraryOverlay.swift"], + deps = [":orientation"], +) + +swift_test( + name = "retro_test", + srcs = ["RetroTest.swift"], + deps = [ + ":orientation", + ":retro_library", + ], +) diff --git a/examples/xplatform/overlay/Orientation.swift b/examples/xplatform/overlay/Orientation.swift new file mode 100644 index 000000000..d96da4e67 --- /dev/null +++ b/examples/xplatform/overlay/Orientation.swift @@ -0,0 +1,20 @@ +// Copyright 2024 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. + +/// The orientation of a 2D shape. +public enum Orientation { + case unoriented + case horizontal + case vertical +} diff --git a/examples/xplatform/overlay/RetroLibraryOverlay.swift b/examples/xplatform/overlay/RetroLibraryOverlay.swift new file mode 100644 index 000000000..88589eca9 --- /dev/null +++ b/examples/xplatform/overlay/RetroLibraryOverlay.swift @@ -0,0 +1,34 @@ +// Copyright 2024 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 OrientationModule +@_exported import RetroLibrary + +extension RetroRect { + public var orientation: Orientation { + if width > height { + return .horizontal + } + if height > width { + return .vertical + } + return .unoriented + } +} + +extension RetroRect: CustomStringConvertible { + public var description: String { + return "" + } +} diff --git a/examples/xplatform/overlay/RetroTest.swift b/examples/xplatform/overlay/RetroTest.swift new file mode 100644 index 000000000..334a8a63c --- /dev/null +++ b/examples/xplatform/overlay/RetroTest.swift @@ -0,0 +1,41 @@ +// Copyright 2024 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 OrientationModule +import RetroLibrary +import XCTest + +final class RetroTest: XCTestCase { + var rect: RetroRect! + + override func setUp() { + rect = RetroRect(x: 1, y: 2, width: 3, height: 4) + } + + func testRenamedAPI() { + rect.print() + } + + func testRefinedAPI() { + XCTAssertEqual(rect.area, 12.0, accuracy: 0.001) + } + + func testDeclarationAddedInOverlay() { + XCTAssertEqual(rect.description, "") + } + + func testDeclarationUsingTypeOnlyImportedByOverlay() { + XCTAssertEqual(rect.orientation, .vertical) + } +} diff --git a/examples/xplatform/overlay/retro_library.c b/examples/xplatform/overlay/retro_library.c new file mode 100644 index 000000000..d8fc4ec25 --- /dev/null +++ b/examples/xplatform/overlay/retro_library.c @@ -0,0 +1,24 @@ +// Copyright 2024 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 "examples/xplatform/overlay/retro_library.h" + +#include + +float RetroRectArea(RetroRect r) { return r.width * r.height; } + +void RetroRectPrint(RetroRect r) { + printf("\n", r.x, r.y, r.width, + r.height); +} diff --git a/examples/xplatform/overlay/retro_library.h b/examples/xplatform/overlay/retro_library.h new file mode 100644 index 000000000..329b496e4 --- /dev/null +++ b/examples/xplatform/overlay/retro_library.h @@ -0,0 +1,31 @@ +// Copyright 2024 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 THIRD_PARTY_BAZEL_RULES_RULES_SWIFT_EXAMPLES_XPLATFORM_OVERLAY_RETRO_LIBRARY_H_ +#define THIRD_PARTY_BAZEL_RULES_RULES_SWIFT_EXAMPLES_XPLATFORM_OVERLAY_RETRO_LIBRARY_H_ + +typedef struct _RetroRect { + float x; + float y; + float width; + float height; +} RetroRect; + +float RetroRectArea(RetroRect r) + __attribute__((swift_name("getter:RetroRect.area(self:)"))); + +void RetroRectPrint(RetroRect r) + __attribute__((swift_name("RetroRect.print(self:)"))); + +#endif // THIRD_PARTY_BAZEL_RULES_RULES_SWIFT_EXAMPLES_XPLATFORM_OVERLAY_RETRO_LIBRARY_H_ diff --git a/swift/BUILD b/swift/BUILD index 980fbf619..0a5d1f09d 100644 --- a/swift/BUILD +++ b/swift/BUILD @@ -59,6 +59,7 @@ bzl_library( deps = [ ":module_name", ":providers", + "//swift/internal:binary_attrs", "//swift/internal:compiling", "//swift/internal:feature_names", "//swift/internal:linking", @@ -79,7 +80,10 @@ bzl_library( "//swift/internal:compiling", "//swift/internal:feature_names", "//swift/internal:features", + "//swift/internal:linking", "//swift/internal:module_maps", + "//swift/internal:output_groups", + "//swift/internal:providers", "//swift/internal:swift_interop_info", "//swift/internal:toolchain_utils", "//swift/internal:utils", @@ -104,6 +108,7 @@ bzl_library( deps = [ ":module_name", ":providers", + "//swift/internal:binary_attrs", "//swift/internal:compiling", "//swift/internal:linking", "//swift/internal:output_groups", @@ -233,6 +238,19 @@ bzl_library( ], ) +bzl_library( + name = "swift_overlay", + srcs = ["swift_overlay.bzl"], + deps = [ + ":providers", + ":swift_clang_module_aspect", + "//swift/internal:attrs", + "//swift/internal:feature_names", + "//swift/internal:providers", + "//swift/internal:utils", + ], +) + bzl_library( name = "swift_package_configuration", srcs = ["swift_package_configuration.bzl"], @@ -258,6 +276,7 @@ bzl_library( deps = [ ":module_name", ":providers", + "//swift/internal:binary_attrs", "//swift/internal:compiling", "//swift/internal:env_expansion", "//swift/internal:feature_names", @@ -293,6 +312,7 @@ bzl_library( ":swift_library_group", ":swift_module_mapping", ":swift_module_mapping_test", + ":swift_overlay", ":swift_package_configuration", ":swift_symbol_graph_aspect", ":swift_test", diff --git a/swift/internal/BUILD b/swift/internal/BUILD index 68dd77e6f..3d2374b60 100644 --- a/swift/internal/BUILD +++ b/swift/internal/BUILD @@ -62,6 +62,17 @@ bzl_library( ], ) +bzl_library( + name = "binary_attrs", + srcs = ["binary_attrs.bzl"], + visibility = ["//swift:__subpackages__"], + deps = [ + ":attrs", + "//swift:swift_clang_module_aspect", + "@bazel_skylib//lib:dicts", + ], +) + bzl_library( name = "build_settings", srcs = ["build_settings.bzl"], @@ -162,15 +173,12 @@ bzl_library( deps = [ ":action_names", ":actions", - ":attrs", ":autolinking", ":debugging", ":developer_dirs", ":feature_names", ":features", - "//swift:swift_clang_module_aspect", - "@bazel_skylib//lib:dicts", - "@bazel_tools//tools/build_defs/cc:action_names.bzl", + "//swift:providers", ], ) diff --git a/swift/internal/binary_attrs.bzl b/swift/internal/binary_attrs.bzl new file mode 100644 index 000000000..425ae804f --- /dev/null +++ b/swift/internal/binary_attrs.bzl @@ -0,0 +1,105 @@ +# Copyright 2024 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. + +"""Common attributes used by rules that define Swift binary targets.""" + +load("@bazel_skylib//lib:dicts.bzl", "dicts") +load( + "//swift:swift_clang_module_aspect.bzl", + "swift_clang_module_aspect", +) +load(":attrs.bzl", "swift_compilation_attrs") + +_MALLOC_DOCSTRING = """\ +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. +""" + +def binary_rule_attrs( + *, + additional_deps_aspects = [], + additional_deps_providers = [], + stamp_default): + """Returns attributes common to both `swift_binary` and `swift_test`. + + Args: + additional_deps_aspects: A list of additional aspects that should be + applied to the `deps` attribute of the rule. + additional_deps_providers: A list of lists representing additional + providers that should be allowed by the `deps` attribute of the + rule. + stamp_default: The default value of the `stamp` attribute. + + Returns: + A `dict` of attributes for a binary or test rule. + """ + return dicts.add( + swift_compilation_attrs( + additional_deps_aspects = [ + swift_clang_module_aspect, + ] + additional_deps_aspects, + additional_deps_providers = additional_deps_providers, + requires_srcs = False, + ), + { + "linkopts": attr.string_list( + doc = """\ +Additional linker options that should be passed to `clang`. These strings are +subject to `$(location ...)` expansion. +""", + mandatory = False, + ), + "malloc": attr.label( + default = Label("@bazel_tools//tools/cpp:malloc"), + doc = _MALLOC_DOCSTRING, + mandatory = False, + providers = [[CcInfo]], + ), + "stamp": attr.int( + default = stamp_default, + doc = """\ +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. +""", + mandatory = False, + ), + # Do not add references; temporary attribute for C++ toolchain + # Starlark migration. + "_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]], + ), + }, + ) diff --git a/swift/internal/linking.bzl b/swift/internal/linking.bzl index 2467ba2b5..a63f4d51e 100644 --- a/swift/internal/linking.bzl +++ b/swift/internal/linking.bzl @@ -14,14 +14,9 @@ """Implementation of linking logic for Swift.""" -load("@bazel_skylib//lib:dicts.bzl", "dicts") -load( - "//swift:swift_clang_module_aspect.bzl", - "swift_clang_module_aspect", -) +load("//swift:providers.bzl", "SwiftOverlayInfo") load(":action_names.bzl", "SWIFT_ACTION_AUTOLINK_EXTRACT") load(":actions.bzl", "is_action_enabled") -load(":attrs.bzl", "swift_compilation_attrs") load(":autolinking.bzl", "register_autolink_extract_action") load( ":debugging.bzl", @@ -41,87 +36,6 @@ load( "is_feature_enabled", ) -def binary_rule_attrs( - *, - additional_deps_aspects = [], - additional_deps_providers = [], - stamp_default): - """Returns attributes common to both `swift_binary` and `swift_test`. - - Args: - additional_deps_aspects: A list of additional aspects that should be - applied to the `deps` attribute of the rule. - additional_deps_providers: A list of lists representing additional - providers that should be allowed by the `deps` attribute of the - rule. - stamp_default: The default value of the `stamp` attribute. - - Returns: - A `dict` of attributes for a binary or test rule. - """ - return dicts.add( - swift_compilation_attrs( - additional_deps_aspects = [ - swift_clang_module_aspect, - ] + additional_deps_aspects, - additional_deps_providers = additional_deps_providers, - requires_srcs = False, - ), - { - "linkopts": attr.string_list( - doc = """\ -Additional linker options that should be passed to `clang`. These strings are -subject to `$(location ...)` expansion. -""", - mandatory = False, - ), - "malloc": attr.label( - default = Label("@bazel_tools//tools/cpp:malloc"), - doc = """\ -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. -""", - mandatory = False, - providers = [[CcInfo]], - ), - "stamp": attr.int( - default = stamp_default, - doc = """\ -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. -""", - mandatory = False, - ), - # Do not add references; temporary attribute for C++ toolchain - # Starlark migration. - "_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]], - ), - }, - ) - def configure_features_for_binary( *, ctx, @@ -503,6 +417,10 @@ def register_link_binary_action( dep[CcInfo].linking_context for dep in deps if CcInfo in dep + ] + [ + dep[SwiftOverlayInfo].linking_context + for dep in deps + if SwiftOverlayInfo in dep ] + additional_linking_contexts for module_context in module_contexts: diff --git a/swift/internal/output_groups.bzl b/swift/internal/output_groups.bzl index 136d0f3bf..6f23f17e8 100644 --- a/swift/internal/output_groups.bzl +++ b/swift/internal/output_groups.bzl @@ -19,12 +19,16 @@ visibility([ "//swift/...", ]) -def supplemental_compilation_output_groups(*supplemental_outputs): +def supplemental_compilation_output_groups( + *supplemental_outputs, + additional_indexstore_files = []): """Computes output groups from supplemental compilation outputs. Args: *supplemental_outputs: Zero or more supplemental outputs `struct`s returned from calls to `compile`. + additional_indexstore_files: An optional `list` of additional indexstore + artifacts to be included in the `indexstore` output group. Returns: A dictionary whose keys are output group names and whose values are @@ -33,7 +37,7 @@ def supplemental_compilation_output_groups(*supplemental_outputs): """ ast_files = [] const_values_files = [] - indexstore_files = [] + indexstore_files = list(additional_indexstore_files) macro_expansions_files = [] for outputs in supplemental_outputs: diff --git a/swift/internal/providers.bzl b/swift/internal/providers.bzl index d5f951e4f..19b128a81 100644 --- a/swift/internal/providers.bzl +++ b/swift/internal/providers.bzl @@ -57,3 +57,58 @@ compiling it and/or any modules that depend on it. """, }, ) + +SwiftOverlayCompileInfo = provider( + doc = """\ +Propagated by the `swift_overlay` rule to represent information needed to +compile a Swift overlay with its paired C/Objective-C module. +""", + fields = { + "label": "The label of the `swift_overlay` target.", + "srcs": "The source files to compile in the overlay.", + "additional_inputs": "Additional inputs to the compiler.", + "copts": """\ +List of strings. Swift compiler flags to pass when compiling the overlay. +""", + "defines": """\ +List of strings. Compiler conditions to set when compiling the overlay. +""", + "disabled_features": """\ +List of strings. Features that should be disabled when compiling the overlay. +""", + "enabled_features": """\ +List of strings. Features that should be enabled when compiling the overlay. +""", + "include_dev_srch_paths": """\ +Bool. Whether to add the developer framework search paths when compiling the +overlay. +""", + "library_evolution": """\ +Bool. Whether to compile the overlay with library evolution enabled. +""", + "linkopts": """\ +List of strings. Linker flags to propagate when the overlay is used as a +dependency. +""", + "plugins": """\ +A list of `SwiftCompilerPluginInfo` providers of the overlay's plug-ins. +""", + "private_deps": """\ +A `struct` containing the following fields: + +* `cc_infos`: A list of `CcInfo` providers from the overlay's `private_deps`. +* `swift_infos`: A list of `SwiftInfo` providers from the overlay's + `private_deps`. +""", + "alwayslink": """\ +Bool. Whether the overlay should always be included in the final binary's +linkage. +""", + "deps": """\ +A `struct` containing the following fields: + +* `cc_infos`: A list of `CcInfo` providers from the overlay's `deps`. +* `swift_infos`: A list of `SwiftInfo` providers from the overlay's `deps`. +""", + }, +) diff --git a/swift/providers.bzl b/swift/providers.bzl index 18f818006..4fb0f18d4 100644 --- a/swift/providers.bzl +++ b/swift/providers.bzl @@ -145,6 +145,21 @@ this provider and all of its dependencies. init = _swift_info_init, ) +SwiftOverlayInfo = provider( + doc = """\ +Contains additional artifacts from the Swift overlay for a C/Objective-C module +that also need to be propagated to clients of the module for it to work +properly. +""", + fields = { + "linking_context": """\ +`CcLinkingContext`. A linking context that contain object files, linker flags, +and additional linker inputs for Swift code that was compiled as an overlay for +a C/Objective-C target. +""", + }, +) + SwiftPackageConfigurationInfo = provider( doc = """\ Describes a compiler configuration that is applied by default to targets in a diff --git a/swift/swift_binary.bzl b/swift/swift_binary.bzl index 775cd8fcd..effd85006 100644 --- a/swift/swift_binary.bzl +++ b/swift/swift_binary.bzl @@ -16,6 +16,7 @@ load("@bazel_skylib//lib:dicts.bzl", "dicts") load("@bazel_skylib//lib:paths.bzl", "paths") +load("//swift/internal:binary_attrs.bzl", "binary_rule_attrs") load("//swift/internal:compiling.bzl", "compile") load( "//swift/internal:feature_names.bzl", @@ -24,7 +25,6 @@ load( load("//swift/internal:features.bzl", "is_feature_enabled") load( "//swift/internal:linking.bzl", - "binary_rule_attrs", "configure_features_for_binary", "create_linking_context_from_compilation_outputs", "malloc_linking_context", @@ -51,6 +51,7 @@ load( ":providers.bzl", "SwiftBinaryInfo", "SwiftInfo", + "SwiftOverlayInfo", "create_swift_module_context", ) @@ -233,6 +234,10 @@ def _swift_binary_impl(ctx): dep[CcInfo].linking_context for dep in ctx.attr.deps if CcInfo in dep + ] + [ + dep[SwiftOverlayInfo].linking_context + for dep in ctx.attr.deps + if SwiftOverlayInfo in dep ], module_context = compile_result.module_context, swift_toolchain = swift_toolchain, diff --git a/swift/swift_clang_module_aspect.bzl b/swift/swift_clang_module_aspect.bzl index 1851a96c0..52b8a2486 100644 --- a/swift/swift_clang_module_aspect.bzl +++ b/swift/swift_clang_module_aspect.bzl @@ -15,7 +15,7 @@ """Propagates unified `SwiftInfo` providers for C/Objective-C targets.""" load("@bazel_skylib//lib:sets.bzl", "sets") -load("//swift/internal:compiling.bzl", "precompile_clang_module") +load("//swift/internal:compiling.bzl", "compile", "precompile_clang_module") load( "//swift/internal:feature_names.bzl", "SWIFT_FEATURE_MODULE_MAP_HOME_IS_CWD", @@ -26,7 +26,16 @@ load( "configure_features", "is_feature_enabled", ) +load( + "//swift/internal:linking.bzl", + "create_linking_context_from_compilation_outputs", +) load("//swift/internal:module_maps.bzl", "write_module_map") +load( + "//swift/internal:output_groups.bzl", + "supplemental_compilation_output_groups", +) +load("//swift/internal:providers.bzl", "SwiftOverlayCompileInfo") load( "//swift/internal:swift_interop_info.bzl", "SwiftInteropInfo", @@ -38,6 +47,7 @@ load( ) load( "//swift/internal:utils.bzl", + "compact", "compilation_context_for_explicit_module_compilation", ) load(":module_name.bzl", "derive_swift_module_name") @@ -45,6 +55,7 @@ load( ":providers.bzl", "SwiftClangModuleAspectInfo", "SwiftInfo", + "SwiftOverlayInfo", "create_clang_module_inputs", "create_swift_module_context", ) @@ -391,6 +402,8 @@ def _handle_module( ) ) + output_groups = {} + pcm_outputs = precompile_clang_module( actions = aspect_ctx.actions, cc_compilation_context = compilation_context_to_compile, @@ -402,33 +415,151 @@ def _handle_module( target_name = target.label.name, ) precompiled_module = getattr(pcm_outputs, "pcm_file", None) - indexstore_directory = getattr(pcm_outputs, "indexstore_directory", None) + pcm_indexstore = getattr(pcm_outputs, "indexstore_directory", None) + + clang_module_context = create_clang_module_inputs( + compilation_context = compilation_context, + module_map = module_map_file, + precompiled_module = precompiled_module, + strict_includes = strict_includes, + ) + + # If we have a `swift_overlay` in the aspect hints of this target, compile + # it and propagate the Swift module information as part of the same + # `SwiftInfo` provider so that downstream Swift clients get both halves of + # the module. + overlay_swift_module = None + overlay_direct_deps = [] + overlay_linking_context = None + overlay_info = _find_swift_overlay_compile_info(aspect_ctx) + if overlay_info: + overlay_direct_deps = overlay_info.deps.swift_infos + swift_infos_for_overlay = direct_swift_infos + overlay_direct_deps + [ + SwiftInfo( + modules = [ + create_swift_module_context( + name = module_name, + clang = clang_module_context, + ), + ], + direct_swift_infos = direct_swift_infos + overlay_direct_deps, + swift_infos = swift_infos, + ), + ] + overlay_compile_result, overlay_linking_context = _compile_swift_overlay( + aspect_ctx = aspect_ctx, + cc_info = CcInfo( + compilation_context = compilation_context_to_compile, + ), + module_name = module_name, + overlay_info = overlay_info, + swift_infos = swift_infos_for_overlay, + swift_toolchain = swift_toolchain, + ) + overlay_swift_info = overlay_compile_result.swift_info + overlay_swift_module = overlay_swift_info.direct_modules[0].swift + output_groups = supplemental_compilation_output_groups( + overlay_compile_result.supplemental_outputs, + additional_indexstore_files = compact([pcm_indexstore]), + ) + elif pcm_indexstore: + output_groups = {"indexstore": depset([pcm_indexstore])} providers = [ SwiftInfo( modules = [ create_swift_module_context( name = module_name, - clang = create_clang_module_inputs( - compilation_context = compilation_context, - module_map = module_map_file, - precompiled_module = precompiled_module, - strict_includes = strict_includes, - ), + clang = clang_module_context, + swift = overlay_swift_module, ), ], - direct_swift_infos = direct_swift_infos, + direct_swift_infos = direct_swift_infos + overlay_direct_deps, swift_infos = swift_infos, ), ] + if overlay_linking_context: + providers.append(SwiftOverlayInfo( + linking_context = overlay_linking_context, + )) - if indexstore_directory: - providers.append( - OutputGroupInfo(indexstore = depset([indexstore_directory])), - ) + if output_groups: + providers.append(OutputGroupInfo(**output_groups)) return providers +def _compile_swift_overlay( + *, + aspect_ctx, + cc_info, + module_name, + overlay_info, + swift_infos, + swift_toolchain): + """Compiles Swift code to be used as an overlay for a C/Objective-C module. + + Args: + aspect_ctx: The aspect context. + cc_info: The `CcInfo` that represents the + C/Objective-C slice of this module. + module_name: The module name of that C/Objective-C module that this + overlay shares. + overlay_info: The `SwiftOverlayCompileInfo` provider from the target + providing the overlay's compilation information. + swift_infos: A list of `SwiftInfo` providers that represent the + dependencies of both the original C/Objective-C target and the + Swift overlay. + swift_toolchain: The Swift toolchain to use when compiling. + + Returns: + The compilation result, as returned by `swift_common.compile`. + """ + + # We need to create a new feature configuration here because we want to use + # the features that apply to the overlay target, not to the target that uses + # the overlay. + feature_configuration = configure_features( + ctx = aspect_ctx, + requested_features = overlay_info.enabled_features, + swift_toolchain = swift_toolchain, + unsupported_features = overlay_info.disabled_features, + ) + compile_result = compile( + actions = aspect_ctx.actions, + additional_inputs = overlay_info.additional_inputs, + cc_infos = overlay_info.deps.cc_infos + [cc_info], + copts = overlay_info.copts + ["-parse-as-library"], + defines = overlay_info.defines, + feature_configuration = feature_configuration, + include_dev_srch_paths = overlay_info.include_dev_srch_paths, + module_name = module_name, + package_name = None, + plugins = overlay_info.plugins, + srcs = overlay_info.srcs, + swift_infos = swift_infos, + swift_toolchain = swift_toolchain, + target_name = overlay_info.label.name, + workspace_name = aspect_ctx.workspace_name, + ) + linking_context, _ = ( + create_linking_context_from_compilation_outputs( + actions = aspect_ctx.actions, + additional_inputs = overlay_info.additional_inputs, + alwayslink = overlay_info.alwayslink, + compilation_outputs = compile_result.compilation_outputs, + feature_configuration = feature_configuration, + label = overlay_info.label, + linking_contexts = [ + cc_info.linking_context + for cc_info in overlay_info.deps.cc_infos + overlay_info.private_deps.cc_infos + ], + module_context = compile_result.module_context, + swift_toolchain = swift_toolchain, + user_link_flags = overlay_info.linkopts, + ) + ) + return compile_result, linking_context + def _collect_swift_infos_from_deps(aspect_ctx): """Collect `SwiftInfo` providers from dependencies. @@ -528,6 +659,43 @@ def _find_swift_interop_info(target, aspect_ctx): return interop_target[SwiftInteropInfo], default_direct_swift_infos, default_swift_infos return None, default_direct_swift_infos, default_swift_infos +def _find_swift_overlay_compile_info(aspect_ctx): + """Returns the `SwiftOverlayCompileInfo` from an aspect hint, if present. + + It is an error if `aspect_hints` contains two or more targets that propagate + `SwiftOverlayCompileInfo`. + + Args: + aspect_ctx: The aspect's context. + + Returns: + The `SwiftOverlayCompileInfo` if found, or `None`. + """ + overlay_target = None + + for hint in aspect_ctx.rule.attr.aspect_hints: + if SwiftOverlayCompileInfo in hint: + if (hint.label.package != aspect_ctx.label.package or + hint.label.repo_name != aspect_ctx.label.repo_name): + fail(("The 'swift_overlay' '{overlay}' is not in the same " + + "BUILD package as the target '{attached}' that it is " + + "attached to. They must be in the same package.").format( + overlay = hint.label, + attached = aspect_ctx.label, + )) + if overlay_target: + fail(("Conflicting Swift overlay info from aspect hints " + + "'{hint1}' and '{hint2}'. Only one is " + + "allowed.").format( + hint1 = str(overlay_target.label), + hint2 = str(hint.label), + )) + overlay_target = hint + + if overlay_target: + return overlay_target[SwiftOverlayCompileInfo] + return None + def _swift_clang_module_aspect_impl(target, aspect_ctx): providers = [SwiftClangModuleAspectInfo()] diff --git a/swift/swift_compiler_plugin.bzl b/swift/swift_compiler_plugin.bzl index 3d6b1bb40..d68fbc60b 100644 --- a/swift/swift_compiler_plugin.bzl +++ b/swift/swift_compiler_plugin.bzl @@ -21,6 +21,7 @@ load( "@build_bazel_apple_support//lib:transitions.bzl", "macos_universal_transition", ) +load("//swift/internal:binary_attrs.bzl", "binary_rule_attrs") load("//swift/internal:compiling.bzl", "compile") load( "//swift/internal:feature_names.bzl", @@ -29,7 +30,6 @@ load( load("//swift/internal:features.bzl", "is_feature_enabled") load( "//swift/internal:linking.bzl", - "binary_rule_attrs", "configure_features_for_binary", "create_linking_context_from_compilation_outputs", "malloc_linking_context", @@ -51,7 +51,7 @@ load( "get_providers", ) load(":module_name.bzl", "derive_swift_module_name") -load(":providers.bzl", "SwiftBinaryInfo", "SwiftInfo") +load(":providers.bzl", "SwiftBinaryInfo", "SwiftInfo", "SwiftOverlayInfo") def _swift_compiler_plugin_impl(ctx): swift_toolchain = get_swift_toolchain(ctx) @@ -173,6 +173,10 @@ def _swift_compiler_plugin_impl(ctx): dep[CcInfo].linking_context for dep in deps if CcInfo in dep + ] + [ + dep[SwiftOverlayInfo].linking_context + for dep in deps + if SwiftOverlayInfo in dep ], module_context = module_context, swift_toolchain = swift_toolchain, diff --git a/swift/swift_library.bzl b/swift/swift_library.bzl index 1a13abf2e..e198421e4 100644 --- a/swift/swift_library.bzl +++ b/swift/swift_library.bzl @@ -57,7 +57,7 @@ load( "include_developer_search_paths", ) load(":module_name.bzl", "derive_swift_module_name") -load(":providers.bzl", "SwiftInfo") +load(":providers.bzl", "SwiftInfo", "SwiftOverlayInfo") load(":swift_clang_module_aspect.bzl", "swift_clang_module_aspect") def _maybe_parse_as_library_copts(srcs): @@ -211,6 +211,10 @@ def _swift_library_impl(ctx): dep[CcInfo].linking_context for dep in deps + private_deps if CcInfo in dep + ] + [ + dep[SwiftOverlayInfo].linking_context + for dep in deps + private_deps + if SwiftOverlayInfo in dep ], module_context = module_context, swift_toolchain = swift_toolchain, diff --git a/swift/swift_overlay.bzl b/swift/swift_overlay.bzl new file mode 100644 index 000000000..f0e99d67e --- /dev/null +++ b/swift/swift_overlay.bzl @@ -0,0 +1,174 @@ +# Copyright 2024 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. + +"""Implementation of the `swift_overlay` rule.""" + +load( + "//swift/internal:attrs.bzl", + "swift_deps_attr", + "swift_library_rule_attrs", +) +load( + "//swift/internal:feature_names.bzl", + "SWIFT_FEATURE_EMIT_PRIVATE_SWIFTINTERFACE", + "SWIFT_FEATURE_EMIT_SWIFTINTERFACE", + "SWIFT_FEATURE_ENABLE_LIBRARY_EVOLUTION", +) +load( + "//swift/internal:providers.bzl", + "SwiftCompilerPluginInfo", + "SwiftOverlayCompileInfo", +) +load( + "//swift/internal:utils.bzl", + "get_providers", + "include_developer_search_paths", +) +load(":providers.bzl", "SwiftInfo") +load(":swift_clang_module_aspect.bzl", "swift_clang_module_aspect") + +def _swift_overlay_impl(ctx): + deps = ctx.attr.deps + private_deps = ctx.attr.private_deps + + features = list(ctx.features) + if ctx.attr.library_evolution: + features.append(SWIFT_FEATURE_ENABLE_LIBRARY_EVOLUTION) + features.append(SWIFT_FEATURE_EMIT_SWIFTINTERFACE) + features.append(SWIFT_FEATURE_EMIT_PRIVATE_SWIFTINTERFACE) + + include_dev_srch_paths = include_developer_search_paths(ctx.attr) + + return [SwiftOverlayCompileInfo( + label = ctx.label, + srcs = ctx.files.srcs, + additional_inputs = ctx.files.swiftc_inputs, + copts = ctx.attr.copts, + defines = ctx.attr.defines, + disabled_features = ctx.disabled_features, + enabled_features = ctx.features, + include_dev_srch_paths = include_dev_srch_paths, + library_evolution = ctx.attr.library_evolution, + linkopts = ctx.attr.linkopts, + plugins = get_providers(ctx.attr.plugins, SwiftCompilerPluginInfo), + private_deps = struct( + cc_infos = get_providers(private_deps, CcInfo), + swift_infos = get_providers(private_deps, SwiftInfo), + ), + alwayslink = ctx.attr.alwayslink, + deps = struct( + cc_infos = get_providers(deps, CcInfo), + swift_infos = get_providers(deps, SwiftInfo), + ), + )] + +def _swift_overlay_attrs(): + """Returns the attribute dictionary for the `swift_overlay` rule.""" + attrs = swift_library_rule_attrs(additional_deps_aspects = [ + swift_clang_module_aspect, + ]) + attrs["private_deps"] = swift_deps_attr( + 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 +dependent for linking, but artifacts/flags required for compilation (such as +.swiftmodule files, C headers, and search paths) will not be propagated. +""", + ) + + # `swift_overlay` must not provide its own module name because it will be + # taken from the target to which it is applied as an aspect hint. Likewise, + # it cannot generate a header because we assume that this is a pure Swift + # overlay that does not export any APIs that would be of interest to + # C/Objective-C clients. + attrs.pop("module_name") + attrs.pop("generated_header_name") + attrs.pop("generates_header") + + # TODO: b/65410357 - More work is needed to support runfiles. + attrs.pop("data") + + return attrs + +swift_overlay = rule( + attrs = _swift_overlay_attrs(), + doc = """\ +A Swift overlay that sits on top of a C/Objective-C library, allowing an author +of a C/Objective-C library to create additional Swift-specific APIs that are +automatically available when a Swift target depends on their C/Objective-C +library. + +The Swift overlay will only be compiled when other Swift targets depend on the +original library that uses the overlay; non-Swift clients depending on the +original library will not cause the Swift overlay code to be built or linked. +This is done to retain optimium build performance and binary size for non-Swift +clients. For this reason, `swift_overlay` is **not** a general purpose mechanism +for creating mixed-language modules; `swift_overlay` does not support generation +of an Objective-C header. + +The `swift_overlay` rule does not perform any compilation of its own. Instead, +it must be placed in the `aspect_hints` attribute of another rule such as +`objc_library` or `cc_library`. For example, + +```build +objc_library( + name = "MyModule", + srcs = ["MyModule.m"], + hdrs = ["MyModule.h"], + aspect_hints = [":MyModule_overlay"], + deps = [...], +) + +swift_overlay( + name = "MyModule_overlay", + srcs = ["MyModule.swift"], + deps = [...], +) +``` + +When some other Swift target, such as a `swift_library`, depends on `MyModule`, +the Swift code in `MyModule_overlay` will be compiled into the same module. +Therefore, when that library imports `MyModule`, it will see the APIs from the +`objc_library` and the `swift_overlay` as a single combined module. + +When writing a Swift overlay, the Swift code must do a re-exporting import of +its own module in order to access the C/Objective-C APIs; they are not available +automatically. Continuing the example above, any Swift sources that want to use +or extend the API from the C/Objective-C side of the module would need to write +the following: + +```swift +@_exported import MyModule +``` + +The `swift_overlay` rule supports all the same attributes as `swift_library`, +except for the following: + +* `module_name` is not supported because the overlay inherits the same module + name as the target it is attached to. +* `generates_header` and `generated_header_name` are not supported because it + is assumed that the overlay is pure Swift code that does not export any APIs + that would be of interest to C/Objective-C clients. + +Aside from its module name and its underlying C/Objective-C module dependency, +`swift_overlay` does not inherit anything else from its associated target. If +the `swift_overlay` imports any modules other than its C/Objective-C side, the +overlay target must explicitly depend on them as well. This means that an +overlay can have a different set of dependencies than the underlying module, if +desired. +""", + fragments = ["cpp"], + implementation = _swift_overlay_impl, +) diff --git a/swift/swift_test.bzl b/swift/swift_test.bzl index bfd901954..262c18676 100644 --- a/swift/swift_test.bzl +++ b/swift/swift_test.bzl @@ -16,6 +16,7 @@ load("@bazel_skylib//lib:dicts.bzl", "dicts") load("@bazel_skylib//lib:paths.bzl", "paths") +load("//swift/internal:binary_attrs.bzl", "binary_rule_attrs") load("//swift/internal:compiling.bzl", "compile") load("//swift/internal:env_expansion.bzl", "expanded_env") load( @@ -25,7 +26,6 @@ load( load("//swift/internal:features.bzl", "is_feature_enabled") load( "//swift/internal:linking.bzl", - "binary_rule_attrs", "configure_features_for_binary", "malloc_linking_context", "register_link_binary_action", From 2b6aef2897981d08702364fc2536fe02a16d8d2f Mon Sep 17 00:00:00 2001 From: Tony Allevato Date: Wed, 21 Aug 2024 07:37:37 -0700 Subject: [PATCH 25/92] Allow `swift_cross_import_overlay` to specify a disambiguating explicit module name for the declaring and/or bystanding module This can be used if either of those targets exports multiple modules. PiperOrigin-RevId: 665873940 (cherry picked from commit 8563c97fde773118244ebd987ce44637a4a884ac) Signed-off-by: Brentley Jones --- doc/rules.md | 5 ++++- swift/swift_cross_import_overlay.bzl | 27 +++++++++++++++++++++++---- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/doc/rules.md b/doc/rules.md index a23cb2a3f..81fed3821 100644 --- a/doc/rules.md +++ b/doc/rules.md @@ -194,7 +194,8 @@ using `swift_compiler_plugin`. ## swift_cross_import_overlay
-swift_cross_import_overlay(name, deps, bystanding_module, declaring_module)
+swift_cross_import_overlay(name, deps, bystanding_module, bystanding_module_name, declaring_module,
+                           declaring_module_name)
 
Declares a cross-import overlay that will be automatically added as a dependency @@ -227,7 +228,9 @@ the future, this rule is not recommended for other widespread use. | name | A unique name for this target. | Name | required | | | deps | A non-empty list of targets representing modules that should be passed as dependencies when a target depends on both `declaring_module` and `bystanding_module`. | List of labels | required | | | bystanding_module | A label for the target representing the second of the two modules (the other being `declaring_module`) that must be imported for the cross-import overlay modules to be imported. It is completely passive in the cross-import process, having no definition with or other association to either the declaring module or the cross-import modules. | Label | required | | +| bystanding_module_name | The name of the bystanding module from the target specified by the `bystanding_module` attribute. This is inferred if `bystanding_module` only exports a single direct module; this name must be specified if `bystanding_module` exports more than one. | String | optional | `""` | | declaring_module | A label for the target representing the first of the two modules (the other being `bystanding_module`) that must be imported for the cross-import overlay modules to be imported. This is the module that contains the `.swiftcrossimport` overlay definition that connects it to the bystander and to the overlay modules. | Label | required | | +| declaring_module_name | The name of the declaring module from the target specified by the `declaring_module` attribute. This is inferred if `declaring_module` only exports a single direct module; this name must be specified if `declaring_module` exports more than one. | String | optional | `""` | diff --git a/swift/swift_cross_import_overlay.bzl b/swift/swift_cross_import_overlay.bzl index 0c598a9a4..a2d110b79 100644 --- a/swift/swift_cross_import_overlay.bzl +++ b/swift/swift_cross_import_overlay.bzl @@ -19,16 +19,17 @@ load(":providers.bzl", "SwiftInfo") def _get_sole_module_name(swift_info, attr): if len(swift_info.direct_modules) != 1: - fail(("The target specified by '{}' must define exactly one Swift " + - "and/or Clang module.").format(attr)) + fail(("The target specified by '{attr}' must define exactly one Swift " + + "and/or Clang module, or you must specify '{attr}_name' to" + + "disambiguate them.").format(attr = attr)) return swift_info.direct_modules[0].name def _swift_cross_import_overlay_impl(ctx): - bystanding_module = _get_sole_module_name( + bystanding_module = ctx.attr.bystanding_module_name or _get_sole_module_name( ctx.attr.bystanding_module[SwiftInfo], "bystanding_module", ) - declaring_module = _get_sole_module_name( + declaring_module = ctx.attr.declaring_module_name or _get_sole_module_name( ctx.attr.declaring_module[SwiftInfo], "declaring_module", ) @@ -53,6 +54,15 @@ the cross-import modules. mandatory = True, providers = [[SwiftInfo]], ), + "bystanding_module_name": attr.string( + doc = """\ +The name of the bystanding module from the target specified by the +`bystanding_module` attribute. This is inferred if `bystanding_module` only +exports a single direct module; this name must be specified if +`bystanding_module` exports more than one. +""", + mandatory = False, + ), "declaring_module": attr.label( doc = """\ A label for the target representing the first of the two modules (the other @@ -63,6 +73,15 @@ overlay definition that connects it to the bystander and to the overlay modules. mandatory = True, providers = [[SwiftInfo]], ), + "declaring_module_name": attr.string( + doc = """\ +The name of the declaring module from the target specified by the +`declaring_module` attribute. This is inferred if `declaring_module` only +exports a single direct module; this name must be specified if +`declaring_module` exports more than one. +""", + mandatory = False, + ), "deps": attr.label_list( allow_empty = False, doc = """\ From 68b58f7a44026fcaa174de0f8dc678d753aadeb6 Mon Sep 17 00:00:00 2001 From: Tony Allevato Date: Wed, 2 Oct 2024 08:02:26 -0700 Subject: [PATCH 26/92] Remove the `swift.no_generated_module_map` feature Nobody is using it anymore; furthermore, it's dangerous now that we use explicit modules by default. PiperOrigin-RevId: 681454702 (cherry picked from commit bcf0703b72b8fc51aad2b3d204b0562cbd14155b) Signed-off-by: Brentley Jones --- swift/internal/BUILD | 1 - swift/internal/compiling.bzl | 11 ++--------- swift/internal/feature_names.bzl | 5 ----- swift/internal/features.bzl | 8 -------- 4 files changed, 2 insertions(+), 23 deletions(-) diff --git a/swift/internal/BUILD b/swift/internal/BUILD index 3d2374b60..21bffb5b4 100644 --- a/swift/internal/BUILD +++ b/swift/internal/BUILD @@ -156,7 +156,6 @@ bzl_library( deps = [ ":feature_names", ":package_specs", - ":target_triples", "@bazel_skylib//lib:collections", "@bazel_skylib//lib:new_sets", "@bazel_skylib//rules:common_settings", diff --git a/swift/internal/compiling.bzl b/swift/internal/compiling.bzl index 58c114943..4dc55c0fc 100644 --- a/swift/internal/compiling.bzl +++ b/swift/internal/compiling.bzl @@ -46,7 +46,6 @@ load( "SWIFT_FEATURE_HEADERS_ALWAYS_ACTION_INPUTS", "SWIFT_FEATURE_INDEX_WHILE_BUILDING", "SWIFT_FEATURE_MODULAR_INDEXING", - "SWIFT_FEATURE_NO_GENERATED_MODULE_MAP", "SWIFT_FEATURE_OPT", "SWIFT_FEATURE_OPT_USES_WMO", "SWIFT_FEATURE_PROPAGATE_GENERATED_MODULE_MAP", @@ -692,10 +691,7 @@ to use swift_common.compile(include_dev_srch_paths = ...) instead.\ # 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, - ): + if generated_header_name: compilation_context_to_compile = ( compilation_context_for_explicit_module_compilation( compilation_contexts = [ @@ -1234,10 +1230,7 @@ def _declare_compile_outputs( # trap door lets them escape the module redefinition error, with the # caveat that certain import scenarios could lead to incorrect behavior # because a header can be imported textually instead of modularly. - if generated_header and not is_feature_enabled( - feature_configuration = feature_configuration, - feature_name = SWIFT_FEATURE_NO_GENERATED_MODULE_MAP, - ): + if generated_header: # Collect the names of Clang modules that the module being built # directly depends on. dependent_module_names = sets.make() diff --git a/swift/internal/feature_names.bzl b/swift/internal/feature_names.bzl index 2db90111c..21ae31fde 100644 --- a/swift/internal/feature_names.bzl +++ b/swift/internal/feature_names.bzl @@ -148,11 +148,6 @@ SWIFT_FEATURE_MODULE_MAP_NO_PRIVATE_HEADERS = ( # link to any version of the ASAN runtime library. SWIFT_FEATURE_NO_ASAN_VERSION_CHECK = "swift.no_asan_version_check" -# If enabled, the compilation action for a library target will not generate a -# 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, the parent directory of the generated module map is added to # `CcInfo.compilation_context.includes`. This allows `objc_library` to import # the Swift module. If you swap this feature between enabled and disabled, and diff --git a/swift/internal/features.bzl b/swift/internal/features.bzl index f9489def0..e40909b28 100644 --- a/swift/internal/features.bzl +++ b/swift/internal/features.bzl @@ -33,7 +33,6 @@ load( "SWIFT_FEATURE_FILE_PREFIX_MAP", "SWIFT_FEATURE_FULL_DEBUG_INFO", "SWIFT_FEATURE_INTERNALIZE_AT_LINK", - "SWIFT_FEATURE_NO_GENERATED_MODULE_MAP", "SWIFT_FEATURE_OBJC_LINK_FLAGS", "SWIFT_FEATURE_OPT_USES_WMO", "SWIFT_FEATURE_REMAP_XCODE_PATH", @@ -41,7 +40,6 @@ load( "SWIFT_FEATURE__SUPPORTS_V6", ) load(":package_specs.bzl", "label_matches_package_specs") -load(":target_triples.bzl", "target_triples") def are_all_features_enabled(feature_configuration, feature_names): """Returns `True` if all features are enabled in the feature configuration. @@ -267,12 +265,6 @@ def default_features_for_toolchain(target_triple): SWIFT_FEATURE_REMAP_XCODE_PATH, ]) - # Linux specific features - if target_triples.unversioned_os(target_triple) == "linux": - features.extend([ - SWIFT_FEATURE_NO_GENERATED_MODULE_MAP, - ]) - return features def upcoming_and_experimental_features(feature_configuration): From 6b868c92ec6ee4e6fa24f5317336166b4cea93e6 Mon Sep 17 00:00:00 2001 From: Tony Allevato Date: Wed, 31 Jul 2024 05:32:11 -0700 Subject: [PATCH 27/92] Put the `swift_overlay`'s deps `SwiftInfo`s into the *transitive* `SwiftInfo`s of the propagated `SwiftInfo` instead of the direct `SwiftInfo`s (which causes them to be merged with the overlay module itself) This ensures that the provider relationships are correct; without this, it appears like the overlay's deps are being provided by the overlay target directly. PiperOrigin-RevId: 657971095 (cherry picked from commit 4633a1fe5d52580685fdf32f98d92556b8d04d58) Signed-off-by: Brentley Jones --- swift/swift_clang_module_aspect.bzl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/swift/swift_clang_module_aspect.bzl b/swift/swift_clang_module_aspect.bzl index 52b8a2486..2ced52121 100644 --- a/swift/swift_clang_module_aspect.bzl +++ b/swift/swift_clang_module_aspect.bzl @@ -442,8 +442,8 @@ def _handle_module( clang = clang_module_context, ), ], - direct_swift_infos = direct_swift_infos + overlay_direct_deps, - swift_infos = swift_infos, + direct_swift_infos = direct_swift_infos, + swift_infos = swift_infos + overlay_direct_deps, ), ] overlay_compile_result, overlay_linking_context = _compile_swift_overlay( @@ -474,8 +474,8 @@ def _handle_module( swift = overlay_swift_module, ), ], - direct_swift_infos = direct_swift_infos + overlay_direct_deps, - swift_infos = swift_infos, + direct_swift_infos = direct_swift_infos, + swift_infos = swift_infos + overlay_direct_deps, ), ] if overlay_linking_context: From 930bce3206242cf3adcd23d42e6d84c71f6ad84b Mon Sep 17 00:00:00 2001 From: Tony Allevato Date: Wed, 12 Jun 2024 06:28:59 -0700 Subject: [PATCH 28/92] Don't drop compilation contexts for `private_deps` when compiling the target that imports them This was rarely caught in practice because it only affects implicit module compilation and only matters when a dependency has includes that depend on custom header search paths provided by one of their C dependencies. PiperOrigin-RevId: 642596524 (cherry picked from commit 8600c56a7991b9779b0e0761c8be037455a317b4) Signed-off-by: Brentley Jones --- doc/api.md | 6 ++++-- swift/internal/compiling.bzl | 9 ++++++++- swift/swift_clang_module_aspect.bzl | 1 + swift/swift_library.bzl | 1 + 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/doc/api.md b/doc/api.md index cd07bc3a7..0052eebf3 100644 --- a/doc/api.md +++ b/doc/api.md @@ -138,8 +138,9 @@ A C++ `FeatureConfiguration` value (see
 swift_common.compile(actions, additional_inputs, cc_infos, copts, defines, exec_group,
                      extra_swift_infos, feature_configuration, generated_header_name, is_test,
-                     include_dev_srch_paths, module_name, package_name, plugins, private_swift_infos,
-                     srcs, swift_infos, swift_toolchain, target_name, workspace_name)
+                     include_dev_srch_paths, module_name, package_name, plugins, private_cc_infos,
+                     private_swift_infos, srcs, swift_infos, swift_toolchain, target_name,
+                     workspace_name)
 
Compiles a Swift module. @@ -163,6 +164,7 @@ Compiles a Swift module. | module_name | The name of the Swift module being compiled. This must be present and valid; use `derive_swift_module_name` to generate a default from the target's label if needed. | none | | package_name | The semantic package of the name of the Swift module being compiled. | none | | plugins | A list of `SwiftCompilerPluginInfo` providers that represent plugins that should be loaded by the compiler. | `[]` | +| private_cc_infos | A list of `CcInfos`s that represent private (non-propagated) C/Objective-C requirements of the target being compiled, such as Swift-compatible preprocessor defines, header search paths, and so forth. These are typically retrieved from a target's `private_deps`. | `[]` | | private_swift_infos | A list of `SwiftInfo` providers from private (implementation-only) dependencies of the target being compiled. The modules defined by these providers are used as dependencies of the Swift module being compiled but not of the Clang module for the generated header. | `[]` | | srcs | The Swift source files to compile. | none | | swift_infos | A list of `SwiftInfo` providers from non-private dependencies of the target being compiled. The modules defined by these providers are used as dependencies of both the Swift module being compiled and the Clang module for the generated header. | none | diff --git a/swift/internal/compiling.bzl b/swift/internal/compiling.bzl index 4dc55c0fc..d5ea3e1f1 100644 --- a/swift/internal/compiling.bzl +++ b/swift/internal/compiling.bzl @@ -314,6 +314,7 @@ def compile( module_name, package_name, plugins = [], + private_cc_infos = [], private_swift_infos = [], srcs, swift_infos, @@ -359,6 +360,11 @@ def compile( being compiled. plugins: A list of `SwiftCompilerPluginInfo` providers that represent plugins that should be loaded by the compiler. + private_cc_infos: A list of `CcInfos`s that represent private + (non-propagated) C/Objective-C requirements of the target being + compiled, such as Swift-compatible preprocessor defines, header + search paths, and so forth. These are typically retrieved from a + target's `private_deps`. private_swift_infos: A list of `SwiftInfo` providers from private (implementation-only) dependencies of the target being compiled. The modules defined by these providers are used as dependencies of the @@ -533,7 +539,8 @@ def compile( for cc_info in cc_infos ] merged_cc_info = cc_common.merge_cc_infos( - cc_infos = cc_infos + swift_toolchain.implicit_deps_providers.cc_infos, + cc_infos = cc_infos + private_cc_infos + + swift_toolchain.implicit_deps_providers.cc_infos, ) transitive_swiftmodules = [] diff --git a/swift/swift_clang_module_aspect.bzl b/swift/swift_clang_module_aspect.bzl index 2ced52121..da99aac89 100644 --- a/swift/swift_clang_module_aspect.bzl +++ b/swift/swift_clang_module_aspect.bzl @@ -535,6 +535,7 @@ def _compile_swift_overlay( module_name = module_name, package_name = None, plugins = overlay_info.plugins, + private_cc_infos = overlay_info.private_deps.cc_infos, srcs = overlay_info.srcs, swift_infos = swift_infos, swift_toolchain = swift_toolchain, diff --git a/swift/swift_library.bzl b/swift/swift_library.bzl index e198421e4..73656c091 100644 --- a/swift/swift_library.bzl +++ b/swift/swift_library.bzl @@ -186,6 +186,7 @@ def _swift_library_impl(ctx): module_name = module_name, package_name = ctx.attr.package_name, plugins = get_providers(ctx.attr.plugins, SwiftCompilerPluginInfo), + private_cc_infos = get_providers(ctx.attr.private_deps, CcInfo), private_swift_infos = private_swift_infos, srcs = srcs, swift_infos = swift_infos, From 9af31ccfeb6640112098b58a37dab874accdd4a3 Mon Sep 17 00:00:00 2001 From: Tony Allevato Date: Tue, 13 Feb 2024 12:06:20 -0800 Subject: [PATCH 29/92] Support sharding and regex-based `--test_filter` in `swift_test` `swift_test` targets can now shard their execution using Bazel's standard `shard_count` test attribute. When Bazel runs a sharded test, it spawns _N_ separate actions in parallel where each shard runs a distinct subset of the tests. On both Apple and Linux, this is achieved by looking at the shard count and filtering out a subset of tests. This is straightforward on Linux where we generate the test runner. Making this work with Apple's `xctest` bundle runner would not have been feasible, so we've abandoned that approach and now treat `swift_test` like any other binary rule: we generate a plain executable (wrapped as an `.xctest` "bundle" on macOS, but not a Mach-O bundle type binary) and use `XCTestSuite.default` to access the dynamically discovered tests. From that object, we can recurse down the test suite/test case hierarchy and select the shard's subset to run. This means we also can no longer use `xctest`'s test filtering, so this change reimplements it manually. This turns out to be an improvement, because we can now use regular expressions to perform the filtering instead of being limited to XCTest's less flexible syntax. Tests will be represented with names of the form `ClassName/MethodName`, and `--test_filter` can be a regular expression that matches those. PiperOrigin-RevId: 606703344 (cherry picked from commit 80cdcccb9ccc205e0e1b68ec863d652d452533c4, 1b1f348bd0e4d37f975a7f92fc135d460ee56a6e, 51b1d1c0a155d6902d7503dfa0f4fcb8cde2bc3c, and bf9019414b00dd0c4e100bf80e8ffc41f6c16ee5) Signed-off-by: Brentley Jones --- doc/providers.md | 2 +- doc/rules.md | 45 +-- examples/xplatform/test_filter/BUILD | 4 +- swift/internal/linking.bzl | 9 +- swift/providers.bzl | 11 +- swift/swift_test.bzl | 308 +++++++----------- swift/toolchains/swift_toolchain.bzl | 4 +- swift/toolchains/xcode_swift_toolchain.bzl | 62 +++- test/rules/swift_shell_test.bzl | 15 +- test/xctest_runner_tests.bzl | 2 +- tools/test_discoverer/BUILD | 4 +- tools/test_discoverer/ObjcTestPrinter.swift | 135 ++++++++ ...ter.swift => SymbolGraphTestPrinter.swift} | 99 ++++-- tools/test_discoverer/TestDiscoverer.swift | 56 +++- tools/test_discoverer/TestPrinterCommon.swift | 90 +++++ tools/xctest_runner/BUILD | 17 - tools/xctest_runner/xctest_runner.sh.template | 103 ------ 17 files changed, 571 insertions(+), 395 deletions(-) create mode 100644 tools/test_discoverer/ObjcTestPrinter.swift rename tools/test_discoverer/{TestPrinter.swift => SymbolGraphTestPrinter.swift} (65%) create mode 100644 tools/test_discoverer/TestPrinterCommon.swift delete mode 100644 tools/xctest_runner/BUILD delete mode 100644 tools/xctest_runner/xctest_runner.sh.template diff --git a/doc/providers.md b/doc/providers.md index e32836f6d..b905e6783 100644 --- a/doc/providers.md +++ b/doc/providers.md @@ -137,7 +137,7 @@ that use the toolchain. | requested_features | `List` of `string`s. 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. | | 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). | -| test_configuration | `Struct` containing the following 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.

* `uses_xctest_bundles`: A Boolean value indicating whether test targets should emit `.xctest` bundles that are launched with the `xctest` tool.

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. | +| test_configuration | `Struct` containing the following fields:

* `binary_name`: A template string used to compute the name of the output binary for `swift_test` rules. Any occurrences of the string `"{name}"` will be substituted by the name of the target.

* `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.

* `objc_test_discovery`: A Boolean value indicating whether test targets should discover tests dynamically using the Objective-C runtime.

* `test_linking_contexts`: A list of `CcLinkingContext`s that provide additional flags to use when linking test binaries.

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 `string`s. 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). | diff --git a/doc/rules.md b/doc/rules.md index 81fed3821..071896457 100644 --- a/doc/rules.md +++ b/doc/rules.md @@ -946,34 +946,39 @@ test discovery: See the documentation of the `discover_tests` attribute for more information about how this behavior affects the rule's outputs. -``` -### Xcode Integration +### Test Bundles -If integrating with Xcode, the relative paths in test binaries can prevent the -Issue navigator from working for test failures. To work around this, you can -have the paths made absolute via swizzling by enabling the -`"apple.swizzle_absolute_xcttestsourcelocation"` feature. You'll also need to -set the `BUILD_WORKSPACE_DIRECTORY` environment variable in your scheme to the -root of your workspace (i.e. `$(SRCROOT)`). +The `swift_test` rule always produces a standard executable binary. This is true +even when targeting macOS, where the typical practice is to use a Mach-O bundle +binary. However, when targeting macOS, the executable binary is still generated +inside a bundle-like directory structure: `{name}.xctest/Contents/MacOS/{name}`. +This allows tests to still work if they contain logic that looks for the path to +their bundle. ### Test Filtering `swift_test` supports Bazel's `--test_filter` flag on all platforms (i.e., Apple -and Linux), which can be used to run only a subset of tests. The expected filter -format is the same as Xcode's `xctest` tool: +and Linux), which can be used to run only a subset of tests. The test filter can +be a test name of the form `ClassName/MethodName` or a regular expression that +matches names of that form. -* `ModuleName`: Run only the test classes/methods in module `ModuleName`. -* `ModuleName.ClassName`: Run only the test methods in class - `ModuleName.ClassName`. -* `ModuleName.ClassName/testMethodName`: Run only the method `testMethodName` - in class `ModuleName.ClassName`. +For example, -Multiple such filters can be separated by commas. For example: +* `--test_filter='ArrayTests/testAppend'` would only run the test method + `testAppend` in the `ArrayTests` class. -```shell -bazel test --test_filter=AModule,BModule.SomeTests,BModule.OtherTests/testX //my/package/... -``` +* `--test_filter='ArrayTests/test(App.*|Ins.*)'` would run all test methods + starting with `testApp` or `testIns` in the `ArrayTests` class. + +### Xcode Integration + +If integrating with Xcode, the relative paths in test binaries can prevent the +Issue navigator from working for test failures. To work around this, you can +have the paths made absolute via swizzling by enabling the +`"apple.swizzle_absolute_xcttestsourcelocation"` feature. You'll also need to +set the `BUILD_WORKSPACE_DIRECTORY` environment variable in your scheme to the +root of your workspace (i.e. `$(SRCROOT)`). **ATTRIBUTES** @@ -986,7 +991,7 @@ bazel test --test_filter=AModule,BModule.SomeTests,BModule.OtherTests/testX //my | 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 | `[]` | | 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 | `[]` | | 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 | `[]` | -| discover_tests | Determines whether or not tests are automatically discovered in the binary. The default value is `True`.

If tests are discovered, then you should not provide your own `main` entry point in the `swift_test` binary; the test runtime provides the entry point for you. If you set this attribute to `False`, then you are responsible for providing your own `main`. This allows you to write tests that use a framework other than Apple's `XCTest`. The only requirement of such a test is that it terminate with a zero exit code for success or a non-zero exit code for failure.

Additionally, on Apple platforms, test discovery is handled by the Objective-C runtime and the output of a `swift_test` rule is an `.xctest` bundle that is invoked using the `xctest` tool in Xcode. If this attribute is used to disable test discovery, then the output of the `swift_test` rule will instead be a standard executable binary that is invoked directly. | Boolean | optional | `True` | +| discover_tests | Determines whether or not tests are automatically discovered in the binary. The default value is `True`.

Tests are discovered in a platform-specific manner. On Apple platforms, they are found using the XCTest framework's `XCTestSuite.default` accessor, which uses the Objective-C runtime to dynamically discover tests. On non-Apple platforms, discovery uses symbol graphs generated from dependencies to find classes and methods written in XCTest's style.

If tests are discovered, then you should not provide your own `main` entry point in the `swift_test` binary; the test runtime provides the entry point for you. If you set this attribute to `False`, then you are responsible for providing your own `main`. This allows you to write tests that use a framework other than Apple's `XCTest`. The only requirement of such a test is that it terminate with a zero exit code for success or a non-zero exit code for failure. | Boolean | optional | `True` | | env | Dictionary of environment variables that should be set during the test execution. | Dictionary: String -> String | 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"` | diff --git a/examples/xplatform/test_filter/BUILD b/examples/xplatform/test_filter/BUILD index c251aa05a..b956227cc 100644 --- a/examples/xplatform/test_filter/BUILD +++ b/examples/xplatform/test_filter/BUILD @@ -27,7 +27,7 @@ swift_test( swift_test( name = "test_filter__feature__target_class", env = { - "TESTBRIDGE_TEST_ONLY": "test_filter.PassTests", + "TESTBRIDGE_TEST_ONLY": "PassTests", }, module_name = "test_filter__feature__target_class", deps = [":test_filter_lib"], @@ -37,7 +37,7 @@ swift_test( swift_test( name = "test_filter__feature__target_class_method", env = { - "TESTBRIDGE_TEST_ONLY": "test_filter.PassFailTests/test_pass", + "TESTBRIDGE_TEST_ONLY": "PassFailTests/test_pass", }, module_name = "test_filter__feature__target_class_method", deps = [":test_filter_lib"], diff --git a/swift/internal/linking.bzl b/swift/internal/linking.bzl index a63f4d51e..84195912f 100644 --- a/swift/internal/linking.bzl +++ b/swift/internal/linking.bzl @@ -365,8 +365,8 @@ def register_link_binary_action( deps, feature_configuration, label, - name, module_contexts = [], + name = None, output_type, stamp, swift_toolchain, @@ -394,8 +394,8 @@ def register_link_binary_action( compilation of the sources in the binary target, which are embedded in the binary for debugging if this is a debug build. This list may be empty if the target had no sources of its own. - name: The name of the target being linked, which is used to derive the - output artifact. + name: If provided, the name of the output file to generate. If not + provided, the name of `label` will be used. output_type: A string indicating the output type; "executable" or "dynamic_library". stamp: A tri-state value (-1, 0, or 1) that specifies whether link @@ -434,6 +434,9 @@ def register_link_binary_action( if debugging_linking_context: linking_contexts.append(debugging_linking_context) + if not name: + name = label.name + autolink_linking_context = _create_autolink_linking_context( actions = actions, compilation_outputs = compilation_outputs, diff --git a/swift/providers.bzl b/swift/providers.bzl index 4fb0f18d4..e4548db37 100644 --- a/swift/providers.bzl +++ b/swift/providers.bzl @@ -412,14 +412,21 @@ compiles). "test_configuration": """\ `Struct` containing the following fields: +* `binary_name`: A template string used to compute the name of the output + binary for `swift_test` rules. Any occurrences of the string `"{name}"` will + be substituted by the name of the target. + * `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. -* `uses_xctest_bundles`: A Boolean value indicating whether test targets - should emit `.xctest` bundles that are launched with the `xctest` tool. +* `objc_test_discovery`: A Boolean value indicating whether test targets + should discover tests dynamically using the Objective-C runtime. + +* `test_linking_contexts`: A list of `CcLinkingContext`s that provide + additional flags to use when linking test binaries. This is used, for example, with Xcode-based toolchains to ensure that the `xctest` helper and coverage tools are found in the correct developer diff --git a/swift/swift_test.bzl b/swift/swift_test.bzl index 262c18676..ad2a13b19 100644 --- a/swift/swift_test.bzl +++ b/swift/swift_test.bzl @@ -88,77 +88,12 @@ def _maybe_parse_as_library_copts(srcs): srcs[0].basename != "main.swift" return ["-parse-as-library"] if use_parse_as_library else [] -def _create_xctest_bundle(name, actions, binary): - """Creates an `.xctest` bundle that contains the given binary. - - Args: - name: The name of the target being built, which will be used as the - basename of the bundle (followed by the .xctest bundle extension). - actions: The context's actions object. - binary: The binary that will be copied into the test bundle. - - Returns: - A `File` (tree artifact) representing the `.xctest` bundle. - """ - xctest_bundle = actions.declare_directory("{}.xctest".format(name)) - - args = actions.args() - args.add(xctest_bundle.path) - args.add(binary) - - # When XCTest loads this bundle, it will create an instance of this class - # which will register the observer that writes the XML output. - plist = '{ NSPrincipalClass = "BazelXMLTestObserverRegistration"; }' - - actions.run_shell( - arguments = [args], - command = ( - 'mkdir -p "$1/Contents/MacOS" && ' + - 'cp "$2" "$1/Contents/MacOS" && ' + - 'echo \'{}\' > "$1/Contents/Info.plist"'.format(plist) - ), - inputs = [binary], - mnemonic = "SwiftCreateTestBundle", - outputs = [xctest_bundle], - progress_message = "Creating test bundle for {}".format(name), - ) - - return xctest_bundle - -def _create_xctest_runner(name, actions, bundle, xctest_runner_template): - """Creates a script that will launch `xctest` with the given test bundle. - - Args: - name: The name of the target being built, which will be used as the - basename of the test runner script. - actions: The context's actions object. - bundle: The `File` representing the `.xctest` bundle that should be - executed. - xctest_runner_template: The `File` that will be used as a template to - generate the test runner shell script. - - Returns: - A `File` representing the shell script that will launch the test bundle - with the `xctest` tool. - """ - xctest_runner = actions.declare_file("{}.test-runner.sh".format(name)) - - actions.expand_template( - is_executable = True, - output = xctest_runner, - template = xctest_runner_template, - substitutions = { - "%bundle%": bundle.short_path, - }, - ) - - return xctest_runner - def _generate_test_discovery_srcs( *, actions, deps, name, + objc_test_discovery, owner_module_name, owner_symbol_graph_dir = None, test_discoverer): @@ -174,6 +109,8 @@ def _generate_test_discovery_srcs( deps: The list of direct dependencies of the test target. name: The name of the target being built, which will be used to derive the basename of the directory containing the generated files. + objc_test_discovery: If `True`, the runner should use Objective-C-based + XCTest discovery instead of symbol graphs. owner_module_name: The name of the owner module (the target being built). owner_symbol_graph_dir: A directory-type `File` containing the extracted @@ -190,54 +127,57 @@ def _generate_test_discovery_srcs( modules_to_scan = [] args = actions.args() - if owner_symbol_graph_dir: - inputs.append(owner_symbol_graph_dir) - modules_to_scan.append(owner_module_name) - - for dep in deps: - if SwiftSymbolGraphInfo not in dep: - continue - - symbol_graph_info = dep[SwiftSymbolGraphInfo] - - # Only include the direct symbol graphs if the owner didn't have any - # sources. - if not owner_symbol_graph_dir: - modules_to_scan.extend([ - symbol_graph.module_name - for symbol_graph in symbol_graph_info.direct_symbol_graphs - ]) - - # Always include the transitive symbol graphs; if a library depends on a - # support class that inherits from `XCTestCase`, we need to be able to - # detect that. - for symbol_graph in ( - symbol_graph_info.transitive_symbol_graphs.to_list() - ): - inputs.append(symbol_graph.symbol_graph_dir) - - if not modules_to_scan: - fail("Failed to find any modules to inspect for tests.") - - # For each direct dependency/module that we have a symbol graph for (i.e., - # every testonly dependency), declare a `.swift` source file where the - # discovery tool will generate an extension that lists the test entries for - # the classes/methods found in that module. - for module_name in modules_to_scan: - output_file = actions.declare_file( - "{target}_test_discovery_srcs/{module}.entries.swift".format( - module = module_name, - target = name, - ), - ) - outputs.append(output_file) - args.add( - "--module-output", - "{module}={path}".format( - module = module_name, - path = output_file.path, - ), - ) + if objc_test_discovery: + args.add("--objc-test-discovery") + else: + if owner_symbol_graph_dir: + inputs.append(owner_symbol_graph_dir) + modules_to_scan.append(owner_module_name) + + for dep in deps: + if SwiftSymbolGraphInfo not in dep: + continue + + symbol_graph_info = dep[SwiftSymbolGraphInfo] + + # Only include the direct symbol graphs if the owner didn't have any + # sources. + if not owner_symbol_graph_dir: + modules_to_scan.extend([ + symbol_graph.module_name + for symbol_graph in symbol_graph_info.direct_symbol_graphs + ]) + + # Always include the transitive symbol graphs; if a library depends + # on a support class that inherits from `XCTestCase`, we need to be + # able to detect that. + for symbol_graph in ( + symbol_graph_info.transitive_symbol_graphs.to_list() + ): + inputs.append(symbol_graph.symbol_graph_dir) + + if not modules_to_scan: + fail("Failed to find any modules to inspect for tests.") + + # For each direct dependency/module that we have a symbol graph for + # (i.e., every testonly dependency), declare a `.swift` source file + # where the discovery tool will generate an extension that lists the + # test entries for the classes/methods found in that module. + for module_name in modules_to_scan: + output_file = actions.declare_file( + "{target}_test_discovery_srcs/{module}.entries.swift".format( + module = module_name, + target = name, + ), + ) + outputs.append(output_file) + args.add( + "--module-output", + "{module}={path}".format( + module = module_name, + path = output_file.path, + ), + ) # Also declare a single `main.swift` file where the discovery tool will # generate the main runner. @@ -340,12 +280,7 @@ def _swift_test_impl(ctx): ) discover_tests = ctx.attr.discover_tests - uses_xctest_bundles = swift_toolchain.test_configuration.uses_xctest_bundles - is_bundled = discover_tests and uses_xctest_bundles - - # If we need to run the test in an .xctest bundle, the binary must have - # Mach-O type `MH_BUNDLE` instead of `MH_EXECUTE`. - extra_linkopts = ["-Wl,-bundle"] if is_bundled else [] + objc_test_discovery = swift_toolchain.test_configuration.objc_test_discovery # `is_feature_enabled` isn't used, as it requires the prefix of the feature # to start with `swift.` @@ -375,7 +310,9 @@ def _swift_test_impl(ctx): deps_cc_infos = [] deps_compilation_contexts = [] deps_swift_infos = [] - additional_linking_contexts = [] + additional_linking_contexts = list( + swift_toolchain.test_configuration.test_linking_contexts, + ) for dep in deps: if CcInfo in dep: deps_cc_infos.append(dep[CcInfo]) @@ -383,10 +320,10 @@ def _swift_test_impl(ctx): if SwiftInfo in dep: deps_swift_infos.append(dep[SwiftInfo]) if SwiftBinaryInfo in dep: - plugin_info = dep[SwiftBinaryInfo] - deps_swift_infos.append(plugin_info.swift_info) + binary_info = dep[SwiftBinaryInfo] + deps_swift_infos.append(binary_info.swift_info) additional_linking_contexts.append( - plugin_info.cc_info.linking_context, + binary_info.cc_info.linking_context, ) additional_linking_contexts.append(malloc_linking_context(ctx)) @@ -439,10 +376,11 @@ def _swift_test_impl(ctx): swift_infos_including_owner = [compile_result.swift_info] - # If we're going to do test discovery below, extract the symbol graph of - # the module that we just compiled so that we can discover any tests in - # the `srcs` of this target (instead of just in the direct `deps`). - if not is_bundled: + # If we're going to do symbol-graph-based test discovery below, extract + # the symbol graph of the module that we just compiled so that we can + # discover any tests in the `srcs` of this target (instead of just in + # the direct `deps`). + if not objc_test_discovery: owner_symbol_graph_dir = ctx.actions.declare_directory( "{}.symbolgraphs".format(ctx.label.name), ) @@ -461,13 +399,13 @@ def _swift_test_impl(ctx): compilation_outputs = cc_common.create_compilation_outputs() swift_infos_including_owner = deps_swift_infos - # If requested, discover tests using symbol graphs and generate a runner for - # them. - if discover_tests and not uses_xctest_bundles: + # If requested, discover tests and generate a runner for them. + if discover_tests: discovery_srcs = _generate_test_discovery_srcs( actions = ctx.actions, deps = ctx.attr.deps, name = ctx.label.name, + objc_test_discovery = objc_test_discovery, owner_module_name = module_name, owner_symbol_graph_dir = owner_symbol_graph_dir, test_discoverer = ctx.executable._test_discoverer, @@ -500,10 +438,6 @@ def _swift_test_impl(ctx): discovery_compile_result.supplemental_outputs, ) - # If we need to run the test in an .xctest bundle, the binary must have - # Mach-O type `MH_BUNDLE` instead of `MH_EXECUTE`. - extra_linkopts = ["-Wl,-bundle"] if is_bundled else [] - # Apply the optional debugging outputs extension if the toolchain defines # one. debug_outputs_provider = swift_toolchain.debug_outputs_provider @@ -519,10 +453,17 @@ def _swift_test_impl(ctx): feature_configuration = feature_configuration, feature_name = SWIFT_FEATURE_ADD_TARGET_NAME_TO_OUTPUT, ): - name = paths.join(ctx.label.name, ctx.label.name) + bundle_name = paths.join(ctx.label.name, ctx.label.name) else: - name = ctx.label.name - + bundle_name = ctx.label.name + + binary_name = swift_toolchain.test_configuration.binary_name.replace( + "{bundle_name}", + bundle_name, + ).replace( + "{name}", + ctx.label.name, + ) linking_outputs = register_link_binary_action( actions = ctx.actions, additional_inputs = ctx.files.swiftc_inputs, @@ -533,7 +474,7 @@ def _swift_test_impl(ctx): feature_configuration = feature_configuration, label = ctx.label, module_contexts = module_contexts, - name = name, + name = binary_name, output_type = "executable", stamp = ctx.attr.stamp, swift_toolchain = swift_toolchain, @@ -541,31 +482,10 @@ def _swift_test_impl(ctx): ctx, ctx.attr.linkopts, ctx.attr.swiftc_inputs, - ) + extra_linkopts + ctx.fragments.cpp.linkopts, + ) + ctx.fragments.cpp.linkopts, variables_extension = variables_extension, ) - # If the tests are to be bundled, create the bundle and the test runner - # script that launches it via `xctest`. Otherwise, just use the binary - # itself as the executable to launch. - if is_bundled: - xctest_bundle = _create_xctest_bundle( - name = ctx.label.name, - actions = ctx.actions, - binary = linking_outputs.executable, - ) - xctest_runner = _create_xctest_runner( - name = ctx.label.name, - actions = ctx.actions, - bundle = xctest_bundle, - xctest_runner_template = ctx.file._xctest_runner_template, - ) - additional_test_outputs = [xctest_bundle] - executable = xctest_runner - else: - additional_test_outputs = [] - executable = linking_outputs.executable - test_environment = dicts.add( swift_toolchain.test_configuration.env, {"TEST_BINARIES_FOR_LLVM_COV": linking_outputs.executable.short_path}, @@ -574,15 +494,14 @@ def _swift_test_impl(ctx): return [ DefaultInfo( - executable = executable, + executable = linking_outputs.executable, files = depset( - [executable] + additional_test_outputs + - additional_debug_outputs, + [linking_outputs.executable] + additional_debug_outputs, ), runfiles = ctx.runfiles( collect_data = True, collect_default = True, - files = ctx.files.data + additional_test_outputs, + files = ctx.files.data, transitive_files = ctx.attr._apple_coverage_support.files, ), ), @@ -626,18 +545,18 @@ swift_test = rule( Determines whether or not tests are automatically discovered in the binary. The default value is `True`. +Tests are discovered in a platform-specific manner. On Apple platforms, they are +found using the XCTest framework's `XCTestSuite.default` accessor, which uses +the Objective-C runtime to dynamically discover tests. On non-Apple platforms, +discovery uses symbol graphs generated from dependencies to find classes and +methods written in XCTest's style. + If tests are discovered, then you should not provide your own `main` entry point in the `swift_test` binary; the test runtime provides the entry point for you. If you set this attribute to `False`, then you are responsible for providing your own `main`. This allows you to write tests that use a framework other than Apple's `XCTest`. The only requirement of such a test is that it terminate with a zero exit code for success or a non-zero exit code for failure. - -Additionally, on Apple platforms, test discovery is handled by the Objective-C -runtime and the output of a `swift_test` rule is an `.xctest` bundle that is -invoked using the `xctest` tool in Xcode. If this attribute is used to disable -test discovery, then the output of the `swift_test` rule will instead be a -standard executable binary that is invoked directly. """, mandatory = False, ), @@ -667,10 +586,6 @@ standard executable binary that is invoked directly. Label("//tools/test_observer"), ], ), - "_xctest_runner_template": attr.label( - allow_single_file = True, - default = Label("//tools/xctest_runner:xctest_runner_template"), - ), # TODO(b/301253335): Enable AEGs and switch from `swift` exec_group to swift `toolchain` param. "_use_auto_exec_groups": attr.bool(default = False), }, @@ -704,34 +619,39 @@ test discovery: See the documentation of the `discover_tests` attribute for more information about how this behavior affects the rule's outputs. -``` -### Xcode Integration +### Test Bundles -If integrating with Xcode, the relative paths in test binaries can prevent the -Issue navigator from working for test failures. To work around this, you can -have the paths made absolute via swizzling by enabling the -`"apple.swizzle_absolute_xcttestsourcelocation"` feature. You'll also need to -set the `BUILD_WORKSPACE_DIRECTORY` environment variable in your scheme to the -root of your workspace (i.e. `$(SRCROOT)`). +The `swift_test` rule always produces a standard executable binary. This is true +even when targeting macOS, where the typical practice is to use a Mach-O bundle +binary. However, when targeting macOS, the executable binary is still generated +inside a bundle-like directory structure: `{name}.xctest/Contents/MacOS/{name}`. +This allows tests to still work if they contain logic that looks for the path to +their bundle. ### Test Filtering `swift_test` supports Bazel's `--test_filter` flag on all platforms (i.e., Apple -and Linux), which can be used to run only a subset of tests. The expected filter -format is the same as Xcode's `xctest` tool: +and Linux), which can be used to run only a subset of tests. The test filter can +be a test name of the form `ClassName/MethodName` or a regular expression that +matches names of that form. + +For example, -* `ModuleName`: Run only the test classes/methods in module `ModuleName`. -* `ModuleName.ClassName`: Run only the test methods in class - `ModuleName.ClassName`. -* `ModuleName.ClassName/testMethodName`: Run only the method `testMethodName` - in class `ModuleName.ClassName`. +* `--test_filter='ArrayTests/testAppend'` would only run the test method + `testAppend` in the `ArrayTests` class. -Multiple such filters can be separated by commas. For example: +* `--test_filter='ArrayTests/test(App.*|Ins.*)'` would run all test methods + starting with `testApp` or `testIns` in the `ArrayTests` class. -```shell -bazel test --test_filter=AModule,BModule.SomeTests,BModule.OtherTests/testX //my/package/... -``` +### Xcode Integration + +If integrating with Xcode, the relative paths in test binaries can prevent the +Issue navigator from working for test failures. To work around this, you can +have the paths made absolute via swizzling by enabling the +`"apple.swizzle_absolute_xcttestsourcelocation"` feature. You'll also need to +set the `BUILD_WORKSPACE_DIRECTORY` environment variable in your scheme to the +root of your workspace (i.e. `$(SRCROOT)`). """, exec_groups = { # Define an execution group for `SwiftTestDiscovery` actions that does diff --git a/swift/toolchains/swift_toolchain.bzl b/swift/toolchains/swift_toolchain.bzl index 23e87b59d..e783d4faf 100644 --- a/swift/toolchains/swift_toolchain.bzl +++ b/swift/toolchains/swift_toolchain.bzl @@ -550,9 +550,11 @@ def _swift_toolchain_impl(ctx): swift_worker = ctx.attr._worker[DefaultInfo].files_to_run, const_protocols_to_gather = ctx.file.const_protocols_to_gather, test_configuration = struct( + binary_name = "{name}", env = env, execution_requirements = {}, - uses_xctest_bundles = False, + objc_test_discovery = False, + test_linking_contexts = [], ), tool_configs = all_tool_configs, unsupported_features = ctx.disabled_features + [ diff --git a/swift/toolchains/xcode_swift_toolchain.bzl b/swift/toolchains/xcode_swift_toolchain.bzl index 489d76899..e05a90358 100644 --- a/swift/toolchains/xcode_swift_toolchain.bzl +++ b/swift/toolchains/xcode_swift_toolchain.bzl @@ -45,6 +45,7 @@ load( "SWIFT_ACTION_SYMBOL_GRAPH_EXTRACT", ) load("//swift/internal:attrs.bzl", "swift_toolchain_driver_attrs") +load("//swift/internal:developer_dirs.bzl", "swift_developer_lib_dir") load( "//swift/internal:feature_names.bzl", "SWIFT_FEATURE_COVERAGE", @@ -71,6 +72,7 @@ load("//swift/internal:target_triples.bzl", "target_triples") load( "//swift/internal:utils.bzl", "collect_implicit_deps_providers", + "compact", "get_swift_executable_for_toolchain", ) load("//swift/internal:wmo.bzl", "wmo_features_from_swiftcopts") @@ -201,6 +203,56 @@ def _swift_linkopts_cc_info( ), ) +def _test_linking_context(apple_toolchain, target_triple, toolchain_label): + """Returns a `CcLinkingContext` containing linker flags for test binaries. + + Args: + apple_toolchain: The `apple_common.apple_toolchain()` object. + target_triple: The target triple `struct`. + toolchain_label: The label of the Swift toolchain that will act as the + owner of the linker input propagating the flags. + + Returns: + A `CcLinkingContext` that will provide linker flags to `swift_test` + binaries. + """ + platform_developer_framework_dir = _platform_developer_framework_dir( + apple_toolchain, + target_triple, + ) + + # Weakly link to XCTest. It's possible that machine that links the test + # binary will have Xcode installed at a different path than the machine that + # runs the binary. To handle this, the binary `dlopen`s XCTest at startup + # using the path Bazel passes in the test action's environment. + linkopts = [ + "-Wl,-weak_framework,XCTest", + "-Wl,-weak-lXCTestSwiftSupport", + ] + + if platform_developer_framework_dir: + linkopts.extend([ + "-Wl,-rpath,{}".format(path) + for path in compact([ + swift_developer_lib_dir([ + struct( + developer_path_label = "platform", + path = platform_developer_framework_dir, + ), + ]), + platform_developer_framework_dir, + ]) + ]) + + return cc_common.create_linking_context( + linker_inputs = depset([ + cc_common.create_linker_input( + owner = toolchain_label, + user_link_flags = depset(linkopts), + ), + ]), + ) + def _make_resource_directory_configurator(developer_dir): """Configures compiler flags about the toolchain's resource directory. @@ -563,6 +615,12 @@ def _xcode_swift_toolchain_impl(ctx): else: swiftcopts.extend(ctx.attr._copts[BuildSettingInfo].value) + test_linking_context = _test_linking_context( + apple_toolchain = apple_toolchain, + target_triple = target_triple, + toolchain_label = ctx.label, + ) + # `--define=SWIFT_USE_TOOLCHAIN_ROOT=` is a rapid development feature # that lets you build *just* a custom `swift` driver (and `swiftc` # symlink), rather than a full toolchain, and point compilation actions at @@ -711,9 +769,11 @@ def _xcode_swift_toolchain_impl(ctx): swift_worker = ctx.attr._worker[DefaultInfo].files_to_run, const_protocols_to_gather = ctx.file.const_protocols_to_gather, test_configuration = struct( + binary_name = "{bundle_name}.xctest/Contents/MacOS/{name}", env = env, execution_requirements = execution_requirements, - uses_xctest_bundles = True, + objc_test_discovery = True, + test_linking_contexts = [test_linking_context], ), tool_configs = all_tool_configs, unsupported_features = ctx.disabled_features + [ diff --git a/test/rules/swift_shell_test.bzl b/test/rules/swift_shell_test.bzl index 5926b6245..65b1285bd 100644 --- a/test/rules/swift_shell_test.bzl +++ b/test/rules/swift_shell_test.bzl @@ -16,8 +16,16 @@ load("@bazel_skylib//lib:shell.bzl", "shell") +# buildifier: disable=bzl-visibility +load( + "//swift/internal:toolchain_utils.bzl", + "get_swift_toolchain", + "use_swift_toolchain", +) + def _swift_shell_test_impl(ctx): """Implementation of the swift_shell_test rule.""" + swift_toolchain = get_swift_toolchain(ctx) test_executable = ctx.attr.target_under_test.files_to_run.executable @@ -47,10 +55,13 @@ def _swift_shell_test_impl(ctx): runfiles = runfiles, ), ctx.attr.target_under_test[OutputGroupInfo], + testing.ExecutionInfo( + swift_toolchain.test_configuration.execution_requirements, + ), + testing.TestEnvironment(swift_toolchain.test_configuration.env), ] swift_shell_test = rule( - implementation = _swift_shell_test_impl, attrs = { "expected_return_code": attr.int( doc = "The expected return code from the target under test", @@ -76,5 +87,7 @@ swift_shell_test = rule( default = Label("//test/rules:swift_shell_runner.sh.template"), ), }, + implementation = _swift_shell_test_impl, test = True, + toolchains = use_swift_toolchain(), ) diff --git a/test/xctest_runner_tests.bzl b/test/xctest_runner_tests.bzl index 91dc0b628..ccd8c6c86 100644 --- a/test/xctest_runner_tests.bzl +++ b/test/xctest_runner_tests.bzl @@ -42,7 +42,7 @@ def xctest_runner_test_suite(name, tags = []): expected_return_code = 1, expected_logs = [ "Executed 0 tests, with 0 failures", - "error: no tests were executed", + "ERROR: No tests were executed", ], tags = all_tags, target_under_test = "//test/fixtures/xctest_runner:EmptyUnitTests", diff --git a/tools/test_discoverer/BUILD b/tools/test_discoverer/BUILD index a90bd2ecc..309635ecf 100644 --- a/tools/test_discoverer/BUILD +++ b/tools/test_discoverer/BUILD @@ -7,10 +7,12 @@ swift_binary( name = "test_discoverer", srcs = [ "DiscoveredTests.swift", + "ObjcTestPrinter.swift", "SymbolCollector.swift", + "SymbolGraphTestPrinter.swift", "SymbolKitExtensions.swift", "TestDiscoverer.swift", - "TestPrinter.swift", + "TestPrinterCommon.swift", ], visibility = ["//visibility:public"], deps = [ diff --git a/tools/test_discoverer/ObjcTestPrinter.swift b/tools/test_discoverer/ObjcTestPrinter.swift new file mode 100644 index 000000000..4236c021a --- /dev/null +++ b/tools/test_discoverer/ObjcTestPrinter.swift @@ -0,0 +1,135 @@ +// Copyright 2024 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 + +/// Prints a test runner as Swift source code to be compiled in order to run the tests. +/// +/// To support Bazel's test sharding protocol and better test filtering, this runner is not based +/// on an XCTest bundle but instead is an executable that queries the XCTest framework directly for +/// the tests to run. +struct ObjcTestPrinter { + /// Prints the main test runner to a Swift source file. + func printTestRunner(toFileAt url: URL) { + var contents = """ + import BazelTestObservation + import Foundation + import XCTest + + @main + \(availabilityAttribute) + struct Runner { + static func main() { + loadXCTest() + if let xmlObserver = BazelXMLTestObserver.default { + XCTestObservationCenter.shared.addTestObserver(xmlObserver) + } + do { + var testCollector = try ShardingFilteringTestCollector() + let shardedSuite = testCollector.shard(XCTestSuite.default) + shardedSuite.run() + if let testRun = shardedSuite.testRun { + if testRun.testCaseCount == 0 { + print("ERROR: No tests were executed") + exit(1) + } + exit(testRun.totalFailureCount == 0 ? EXIT_SUCCESS : EXIT_FAILURE) + } + } catch { + print("ERROR: \\(error); exiting.") + exit(1) + } + } + } + + private func loadXCTest() { + // We weakly linked to XCTest.framework and the Swift support dylib because the machine + // that links the test binary might not be the same that runs it, and they might have Xcode + // installed at different paths. Find the path that Bazel says they're installed at on + // *this* machine and load them. + guard let sdkRoot = ProcessInfo.processInfo.environment["SDKROOT"] else { + print("ERROR: Bazel must set the SDKROOT in order to find XCTest") + exit(1) + } + let sdkRootURL = URL(fileURLWithPath: sdkRoot) + let platformDeveloperPath = sdkRootURL // .../Developer/SDKs/MacOSX.sdk + .deletingLastPathComponent() // .../Developer/SDKs + .deletingLastPathComponent() // .../Developer + let xcTestPath = platformDeveloperPath + .appendingPathComponent("Library/Frameworks/XCTest.framework/XCTest") + .path + guard dlopen(xcTestPath, RTLD_NOW) != nil else { + print("ERROR: dlopen(\\"\\(xcTestPath)\\") failed") + exit(1) + } + let xcTestSwiftSupportPath = platformDeveloperPath + .appendingPathComponent("usr/lib/libXCTestSwiftSupport.dylib") + .path + guard dlopen(xcTestSwiftSupportPath, RTLD_NOW) != nil else { + print("ERROR: dlopen(\\"\\(xcTestSwiftSupportPath)\\") failed") + exit(1) + } + } + + """ + contents += createShardingFilteringTestCollector() + contents += """ + extension ShardingFilteringTestCollector { + mutating func shard(_ suite: XCTestSuite) -> XCTestSuite { + guard shardCount != 0 || filter != nil else { + // If we're not sharding or filtering, just return the original suite. + return suite + } + + // Create an empty suite with the same name. We'll recurse through the original suite, + // retaining any nested suite structures but only adding 1 of every `shard_count` tests + // we encounter. + let shardedSuite = XCTestSuite(name: suite.name) + for test in suite.tests { + switch test { + case let childSuite as XCTestSuite: + shardedSuite.addTest(shard(childSuite)) + default: + guard isIncludedByFilter(nameForFiltering(of: test)) else { + break + } + if isIncludedInShard() { + shardedSuite.addTest(test) + } + seenTestCount += 1 + } + } + return shardedSuite + } + + private func nameForFiltering(of test: XCTest) -> String { + let name = test.name + guard name.hasPrefix("-[") && name.hasSuffix("]") else { + return name + } + + let trimmedName = name.dropFirst(2).dropLast() + guard let spaceIndex = trimmedName.lastIndex(of: " ") else { + return String(trimmedName) + } + + return "\\(trimmedName[.. String { /// Prints discovered test entries and a test runner as Swift source code to be compiled in order to /// run the tests. -struct TestPrinter { +struct SymbolGraphTestPrinter { /// The discovered tests whose entries and runner should be printed as Swift source code. let discoveredTests: DiscoveredTests @@ -109,21 +99,19 @@ struct TestPrinter { contents += """ \(availabilityAttribute) - func \(allTestsIdentifier(for: discoveredModule))() -> [XCTestCaseEntry] { - return [ + func collect\(allTestsIdentifier(for: discoveredModule))(into collector: inout ShardingFilteringTestCollector) { """ for className in sortedClassNames { let testClass = discoveredModule.classes[className]! contents += """ - testCase(\(className).\(allTestsIdentifier(for: testClass))), + collector.addTests("\(className)", \(className).\(allTestsIdentifier(for: testClass))) """ } contents += """ - ] } """ @@ -151,38 +139,81 @@ struct TestPrinter { \(availabilityAttribute) struct Runner { static func main() { - var tests = [XCTestCaseEntry]() + if let xmlObserver = BazelXMLTestObserver.default { + XCTestObservationCenter.shared.addTestObserver(xmlObserver) + } + do { + var testCollector = try ShardingFilteringTestCollector() """ for moduleName in discoveredTests.modules.keys.sorted() { let module = discoveredTests.modules[moduleName]! contents += """ - tests += \(allTestsIdentifier(for: module))() + collect\(allTestsIdentifier(for: module))(into: &testCollector) """ } - // Bazel's `--test_filter` flag is passed to the test binary via the `TESTBRIDGE_TEST_ONLY` - // environment variable. Below, we read that value if it is present and pass it to `XCTMain`, - // where it behaves the same as the `-XCTest` flag value in Xcode's `xctest` tool. - // - // Note that `XCTMain` treats `arguments` as an actual command line, meaning that `arguments[0]` - // is expected to be the binary's path. So we only do the test filter logic if we've already - // been passed no other arguments. This lets the test binary still support other XCTest flags - // like `--list-tests` or `--dump-tests-json` if the user wishes to use those directly. + // We don't pass the test filter as an argument because we've already filtered the tests in the + // collector; this lets us do better filtering (i.e., regexes) than XCTest itself allows. contents += """ - if let xmlObserver = BazelXMLTestObserver.default { - XCTestObservationCenter.shared.addTestObserver(xmlObserver) + XCTMain(testCollector.testsToRun) + } catch { + print("ERROR: \\(error); exiting.") + exit(1) + } + } + } + + """ + + contents += createShardingFilteringTestCollector( + extraProperties: "private(set) var testsToRun: [XCTestCaseEntry] = []\n") + contents += """ + extension ShardingFilteringTestCollector { + mutating func addTests( + _ suiteName: String, + _ tests: [(String, (T) -> () -> Void)] + ) { + guard shardCount != 0 || filter != nil else { + // If we're not sharding or filtering, just add all the tests. + testsToRun.append(testCase(tests)) + return + } + var shardTests: [(String, (T) -> () -> Void)] = [] + for test in tests { + guard isIncludedByFilter("\\(suiteName)/\\(test.0)") else { + continue + } + if isIncludedInShard() { + shardTests.append(test) + } + seenTestCount += 1 } + testsToRun.append(testCase(shardTests)) + } - var arguments = CommandLine.arguments - if arguments.count == 1, - let testFilter = ProcessInfo.processInfo.environment["TESTBRIDGE_TEST_ONLY"] - { - arguments.append(testFilter) + mutating func addTests( + _ suiteName: String, + _ tests: [(String, (T) -> () throws -> Void)] + ) { + guard shardCount != 0 || filter != nil else { + // If we're not sharding or filtering, just add all the tests. + testsToRun.append(testCase(tests)) + return + } + var shardTests: [(String, (T) -> () throws -> Void)] = [] + for test in tests { + guard isIncludedByFilter("\\(suiteName)/\\(test.0)") else { + continue + } + if isIncludedInShard() { + shardTests.append(test) + } + seenTestCount += 1 } - XCTMain(tests, arguments: arguments) + testsToRun.append(testCase(shardTests)) } } diff --git a/tools/test_discoverer/TestDiscoverer.swift b/tools/test_discoverer/TestDiscoverer.swift index 7263753f5..ee81ce912 100644 --- a/tools/test_discoverer/TestDiscoverer.swift +++ b/tools/test_discoverer/TestDiscoverer.swift @@ -43,6 +43,12 @@ struct TestDiscoverer: ParsableCommand { @Option(help: "The path to the '.swift' file where the main test runner should be generated.") var mainOutput: String + @Flag(help: """ + If true, tests are discovered by asking the Objective-C runtime instead of scanning symbol \ + graphs. + """) + var objcTestDiscovery: Bool = false + @Option( help: .init( """ @@ -54,11 +60,28 @@ struct TestDiscoverer: ParsableCommand { var moduleOutput: [ModuleOutput] = [] func validate() throws { - guard !moduleOutput.isEmpty else { - throw ValidationError("At least one '--module-output' must be provided.") - } - guard !symbolGraphDirectories.isEmpty else { - throw ValidationError("At least one symbol graph directory must be provided.") + if objcTestDiscovery { + guard moduleOutput.isEmpty else { + throw ValidationError( + "'--module-output' cannot be provided if '--objc-test-discovery' is passed.") + } + guard symbolGraphDirectories.isEmpty else { + throw ValidationError( + "No symbol graph directories can be provided if '--objc-test-discovery' is passed.") + } + } else { + guard !moduleOutput.isEmpty else { + throw ValidationError(""" + At least one '--module-output' must be provided if '--objc-test-discovery' is not \ + passed. + """) + } + guard !symbolGraphDirectories.isEmpty else { + throw ValidationError(""" + At least one symbol graph directory must be provided if '--objc-test-discovery' is not \ + passed. + """) + } } } @@ -80,15 +103,20 @@ struct TestDiscoverer: ParsableCommand { } } - // For each module, print the list of test entries that were discovered in a source file that - // extends that module. - let testPrinter = TestPrinter(discoveredTests: collector.discoveredTests()) - for output in moduleOutput { - testPrinter.printTestEntries(forModule: output.moduleName, toFileAt: output.outputURL) - } - - // Print the runner source file, which implements the `@main` type that executes the tests. let mainFileURL = URL(fileURLWithPath: mainOutput) - testPrinter.printTestRunner(toFileAt: mainFileURL) + if objcTestDiscovery { + // Print the runner source file, which implements the `@main` type that executes the tests. + let testPrinter = ObjcTestPrinter() + testPrinter.printTestRunner(toFileAt: mainFileURL) + } else { + // For each module, print the list of test entries that were discovered in a source file that + // extends that module. + let testPrinter = SymbolGraphTestPrinter(discoveredTests: collector.discoveredTests()) + for output in moduleOutput { + testPrinter.printTestEntries(forModule: output.moduleName, toFileAt: output.outputURL) + } + // Print the runner source file, which implements the `@main` type that executes the tests. + testPrinter.printTestRunner(toFileAt: mainFileURL) + } } } diff --git a/tools/test_discoverer/TestPrinterCommon.swift b/tools/test_discoverer/TestPrinterCommon.swift new file mode 100644 index 000000000..3b76b79cf --- /dev/null +++ b/tools/test_discoverer/TestPrinterCommon.swift @@ -0,0 +1,90 @@ +// Copyright 2024 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 + +let availabilityAttribute = """ + @available(*, deprecated, message: "Not actually deprecated. Marked as deprecated to allow \ + inclusion of deprecated tests (which test deprecated functionality) without warnings.") + """ + +/// Creates a text file with the given contents at a file URL. +func createTextFile(at url: URL, contents: String) { + FileManager.default.createFile(atPath: url.path, contents: contents.data(using: .utf8)!) +} + +/// The parts of the `ShardingAwareTestCollector` type that are common to both the Objective-C and +/// symbol-graph-based implementations. Those generators then should add extensions to provide +/// runner-specific logic. +func createShardingFilteringTestCollector(extraProperties: String = "") -> String { + return """ + struct ShardingFilteringTestCollector { + struct Error: Swift.Error, CustomStringConvertible { + var message: String + var description: String { message } + } + + private var shardCount: Int + private var shardIndex: Int + private var seenTestCount: Int + private var filter: Regex? + \(extraProperties) + + init() throws { + // Bazel requires us to write out an empty file at this path to tell it that we support + // sharding. + if let statusPath = ProcessInfo.processInfo.environment["TEST_SHARD_STATUS_FILE"] { + guard FileManager.default.createFile(atPath: statusPath, contents: nil, attributes: nil) + else { + throw Error(message: "Could not create TEST_SHARD_STATUS_FILE (\\(statusPath))") + } + } + + self.shardCount = + ProcessInfo.processInfo.environment["TEST_TOTAL_SHARDS"].flatMap { Int($0, radix: 10) } ?? 0 + self.shardIndex = + ProcessInfo.processInfo.environment["TEST_SHARD_INDEX"].flatMap { Int($0, radix: 10) } ?? 0 + self.seenTestCount = 0 + + guard (shardCount == 0 && shardIndex == 0) || (shardCount > 0 && shardIndex < shardCount) + else { + throw Error( + message: "Invalid shard count (\\(shardCount)) and shard index (\\(shardIndex))") + } + if let filterString = ProcessInfo.processInfo.environment["TESTBRIDGE_TEST_ONLY"] { + guard let maybeFilter = try? Regex(filterString) else { + throw Error(message: "Could not parse '--test_filter' string as a regular expression") + } + filter = maybeFilter + } else { + filter = nil + } + } + + private func isIncludedByFilter(_ testName: String) -> Bool { + guard let filter = self.filter else { return true } + do { + return try filter.firstMatch(in: testName) != nil + } catch { + return false + } + } + + private func isIncludedInShard() -> Bool { + return shardCount == 0 || seenTestCount % shardCount == shardIndex + } + } + + """ +} diff --git a/tools/xctest_runner/BUILD b/tools/xctest_runner/BUILD deleted file mode 100644 index a080c97b1..000000000 --- a/tools/xctest_runner/BUILD +++ /dev/null @@ -1,17 +0,0 @@ -licenses(["notice"]) - -filegroup( - name = "xctest_runner_template", - srcs = ["xctest_runner.sh.template"], - visibility = ["//visibility:public"], -) - -# Consumed by Bazel integration tests. -filegroup( - name = "for_bazel_tests", - testonly = 1, - srcs = glob(["**"]), - visibility = [ - "//tools:__pkg__", - ], -) diff --git a/tools/xctest_runner/xctest_runner.sh.template b/tools/xctest_runner/xctest_runner.sh.template deleted file mode 100644 index a198ae982..000000000 --- a/tools/xctest_runner/xctest_runner.sh.template +++ /dev/null @@ -1,103 +0,0 @@ -#!/usr/bin/env bash -# -# 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. - -set -euo pipefail - -# This test script is used as the executable output of a `swift_test` rule when -# building macOS targets (unless the target has `discover_tests = False`) -# because the test binary is an `MH_BUNDLE` that needs to be loaded dynamically -# and runtime reflection is used to locate the test methods. - -tmp_dir=$(mktemp -d) -trap 'rm -rf "$tmp_dir"' EXIT -readonly profraw="$tmp_dir/coverage.profraw" -readonly testlog="$tmp_dir/test.log" - -# some/path/Foo.xctest -> some/path/Foo.xctest/Contents/MacOS/Foo -bundle_path="%bundle%" -basename="${bundle_path##*/}" -executable_path="$bundle_path/Contents/MacOS/${basename%.xctest}" - -sanitizer_dyld_env="" -if output=$(otool -L "$executable_path" | grep @rpath/libclang_rt | xargs | cut -d " " -f 1); then - rpath=$(otool -l "$executable_path" | grep -A2 LC_RPATH | grep "^\s*path" | grep usr/lib/clang | head -1 | cut -d " " -f 11) - if [[ -z "$rpath" ]]; then - echo "Sanitizer libraries are required but rpath could not be inferred, please file an issue" >&2 - exit 1 - fi - - for lib in $output; do - if [[ -n "$sanitizer_dyld_env" ]]; then - sanitizer_dyld_env="$sanitizer_dyld_env:" - fi - sanitizer_dyld_env="${sanitizer_dyld_env}$rpath/${lib#@rpath/}" - done -fi - -if [[ -n "${DYLD_INSERT_LIBRARIES:-}" ]]; then - if [[ -n "$sanitizer_dyld_env" ]]; then - sanitizer_dyld_env="$sanitizer_dyld_env:" - fi - sanitizer_dyld_env="$sanitizer_dyld_env$DYLD_INSERT_LIBRARIES" -fi - -exit_status=0 -xctest=$(xcrun -f xctest) -test_filter=${TESTBRIDGE_TEST_ONLY:-All} -DYLD_INSERT_LIBRARIES=$sanitizer_dyld_env LLVM_PROFILE_FILE="$profraw" $xctest -XCTest "$test_filter" "$bundle_path" \ - 2>&1 | tee -i "$testlog" \ - || exit_status=$? - -if [[ "$exit_status" -ne 0 ]]; then - exit "$exit_status" -fi - -# Fail when bundle executes nothing -test_target_execution_count=$(grep -e "Executed [[:digit:]]\{1,\} tests*," "$testlog" | tail -n1) -if echo "$test_target_execution_count" | grep -q -e "Executed 0 tests, with 0 failures"; then - echo "error: no tests were executed, is the test bundle empty?" >&2 - exit 1 -fi - -if [[ "${COVERAGE:-}" -ne 1 ]]; then - # Normal tests run without coverage - exit 0 -fi - -readonly profdata="$tmp_dir/coverage.profdata" -xcrun llvm-profdata merge "$profraw" --output "$profdata" - -readonly error_file="$tmp_dir/llvm-cov-error.txt" -llvm_cov_status=0 -xcrun llvm-cov \ - export \ - -format lcov \ - -instr-profile "$profdata" \ - -ignore-filename-regex='.*external/.+' \ - -path-equivalence="$ROOT",. \ - "$executable_path" \ - @"$COVERAGE_MANIFEST" \ - > "$COVERAGE_OUTPUT_FILE" \ - 2> "$error_file" \ - || llvm_cov_status=$? - -# Error ourselves if lcov outputs warnings, such as if we misconfigure -# something and the file path of one of the covered files doesn't exist -if [[ -s "$error_file" || "$llvm_cov_status" -ne 0 ]]; then - echo "error: while exporting coverage report" >&2 - cat "$error_file" >&2 - exit 1 -fi From 629111150934310c039e4bbff13f7b7903e21b72 Mon Sep 17 00:00:00 2001 From: Tony Allevato Date: Mon, 19 Aug 2024 08:01:14 -0700 Subject: [PATCH 30/92] Update docs to say that a source file should only be in a single `swift_*` target, and a `swift_overlay` should only be referenced by a single other target's `aspect_hints` PiperOrigin-RevId: 664809810 (cherry picked from commit c593bc4df2f25656d2e8acf911b1fbeb42a54f81) Signed-off-by: Brentley Jones --- doc/rules.md | 18 ++++++++++++------ swift/internal/attrs.bzl | 6 ++++++ swift/swift_overlay.bzl | 6 ++++++ 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/doc/rules.md b/doc/rules.md index 071896457..bee0d38ce 100644 --- a/doc/rules.md +++ b/doc/rules.md @@ -66,7 +66,7 @@ please use one of the platform-specific application rules in | :------------- | :------------- | :------------- | :------------- | :------------- | | 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.

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_library` (or anything propagating `SwiftInfo`)

* `cc_library` and `objc_library` (or anything propagating `CcInfo`) | List of labels | optional | `[]` | -| srcs | A list of `.swift` source files that will be compiled into the library. | List of labels | optional | `[]` | +| srcs | A list of `.swift` source files that will be compiled into the library.

Except in very rare circumstances, a Swift source file should only appear in a single `swift_*` target. Adding the same source file to multiple `swift_*` targets can lead to binary bloat and/or symbol collisions. If specific sources need to be shared by multiple targets, consider factoring them out into their own `swift_library` instead. | List of labels | 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 | `[]` | | 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 | `[]` | | 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 | `[]` | @@ -154,7 +154,7 @@ swift_library( | :------------- | :------------- | :------------- | :------------- | :------------- | | 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.

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_library` (or anything propagating `SwiftInfo`)

* `cc_library` and `objc_library` (or anything propagating `CcInfo`) | List of labels | optional | `[]` | -| srcs | A list of `.swift` source files that will be compiled into the library. | List of labels | optional | `[]` | +| srcs | A list of `.swift` source files that will be compiled into the library.

Except in very rare circumstances, a Swift source file should only appear in a single `swift_*` target. Adding the same source file to multiple `swift_*` targets can lead to binary bloat and/or symbol collisions. If specific sources need to be shared by multiple targets, consider factoring them out into their own `swift_library` instead. | List of labels | 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 | `[]` | | 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 | `[]` | | 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 | `[]` | @@ -467,7 +467,7 @@ Compiles and links Swift code into a static library and Swift module. | :------------- | :------------- | :------------- | :------------- | :------------- | | 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.

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_library` (or anything propagating `SwiftInfo`)

* `cc_library` and `objc_library` (or anything propagating `CcInfo`) | List of labels | optional | `[]` | -| srcs | A list of `.swift` source files that will be compiled into the library. | List of labels | required | | +| srcs | A list of `.swift` source files that will be compiled into the library.

Except in very rare circumstances, a Swift source file should only appear in a single `swift_*` target. Adding the same source file to multiple `swift_*` targets can lead to binary bloat and/or symbol collisions. If specific sources need to be shared by multiple targets, consider factoring them out into their own `swift_library` instead. | 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 | `[]` | | always_include_developer_search_paths | If `True`, the developer framework search paths will be added to the compilation command. This enables a Swift module to access `XCTest` without having to mark the target as `testonly = True`. | Boolean | optional | `False` | | alwayslink | If `False`, any binary that depends (directly or indirectly) on this Swift module will only link in all the object files for the files listed in `srcs` when there is a direct symbol reference.

Swift protocol conformances don't create linker references. Likewise, if the Swift code has Objective-C classes/methods, their usage does not always result in linker references.

_"All the object files"_ for this module is also somewhat fuzzy. Unlike C, C++, and Objective-C, where each source file results in a `.o` file; for Swift the number of .o files depends on the compiler options (`-wmo`/`-whole-module-optimization`, `-num-threads`). That makes relying on linker reference more fragile, and any individual .swift file in `srcs` may/may not get picked up based on the linker references to other files that happen to get batched into a single `.o` by the compiler options used.

Swift Package Manager always passes the individual `.o` files to the linker instead of using intermediate static libraries, so it effectively is the same as `alwayslink = True`. | Boolean | optional | `True` | @@ -682,6 +682,12 @@ overlay target must explicitly depend on them as well. This means that an overlay can have a different set of dependencies than the underlying module, if desired. +There is a tight coupling between a Swift overlay and the C/Objective-C module +to which it is being applied, so a specific `swift_overlay` target should only +be referenced by the `aspect_hints` of a single `objc_library` or `cc_library` +target. Referencing a `swift_overlay` from multiple targets' `aspect_hints` is +almost always an anti-pattern. + **ATTRIBUTES** @@ -689,7 +695,7 @@ desired. | :------------- | :------------- | :------------- | :------------- | :------------- | | 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.

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_library` (or anything propagating `SwiftInfo`)

* `cc_library` and `objc_library` (or anything propagating `CcInfo`) | List of labels | optional | `[]` | -| srcs | A list of `.swift` source files that will be compiled into the library. | List of labels | required | | +| srcs | A list of `.swift` source files that will be compiled into the library.

Except in very rare circumstances, a Swift source file should only appear in a single `swift_*` target. Adding the same source file to multiple `swift_*` targets can lead to binary bloat and/or symbol collisions. If specific sources need to be shared by multiple targets, consider factoring them out into their own `swift_library` instead. | List of labels | required | | | always_include_developer_search_paths | If `True`, the developer framework search paths will be added to the compilation command. This enables a Swift module to access `XCTest` without having to mark the target as `testonly = True`. | Boolean | optional | `False` | | alwayslink | If `False`, any binary that depends (directly or indirectly) on this Swift module will only link in all the object files for the files listed in `srcs` when there is a direct symbol reference.

Swift protocol conformances don't create linker references. Likewise, if the Swift code has Objective-C classes/methods, their usage does not always result in linker references.

_"All the object files"_ for this module is also somewhat fuzzy. Unlike C, C++, and Objective-C, where each source file results in a `.o` file; for Swift the number of .o files depends on the compiler options (`-wmo`/`-whole-module-optimization`, `-num-threads`). That makes relying on linker reference more fragile, and any individual .swift file in `srcs` may/may not get picked up based on the linker references to other files that happen to get batched into a single `.o` by the compiler options used.

Swift Package Manager always passes the individual `.o` files to the linker instead of using intermediate static libraries, so it effectively is the same as `alwayslink = True`. | Boolean | optional | `True` | | 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 | `[]` | @@ -810,7 +816,7 @@ swift_proto_library( | :------------- | :------------- | :------------- | :------------- | :------------- | | 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.

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_library` (or anything propagating `SwiftInfo`)

* `cc_library` and `objc_library` (or anything propagating `CcInfo`) | List of labels | optional | `[]` | -| srcs | A list of `.swift` source files that will be compiled into the library. | List of labels | optional | `[]` | +| srcs | A list of `.swift` source files that will be compiled into the library.

Except in very rare circumstances, a Swift source file should only appear in a single `swift_*` target. Adding the same source file to multiple `swift_*` targets can lead to binary bloat and/or symbol collisions. If specific sources need to be shared by multiple targets, consider factoring them out into their own `swift_library` instead. | List of labels | 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 | `[]` | | additional_compiler_deps | List of additional dependencies required by the generated Swift code at compile time, whose SwiftProtoInfo will be ignored.

Allowed kinds of dependencies are:

* `swift_library` (or anything propagating `SwiftInfo`)

* `cc_library` and `objc_library` (or anything propagating `CcInfo`) | List of labels | optional | `[]` | | additional_compiler_info | Dictionary of additional information passed to the compiler targets. See the documentation of the respective compiler rules for more information on which fields are accepted and how they are used. | Dictionary: String -> String | optional | `{}` | @@ -987,7 +993,7 @@ root of your workspace (i.e. `$(SRCROOT)`). | :------------- | :------------- | :------------- | :------------- | :------------- | | 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.

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_library` (or anything propagating `SwiftInfo`)

* `cc_library` and `objc_library` (or anything propagating `CcInfo`) | List of labels | optional | `[]` | -| srcs | A list of `.swift` source files that will be compiled into the library. | List of labels | optional | `[]` | +| srcs | A list of `.swift` source files that will be compiled into the library.

Except in very rare circumstances, a Swift source file should only appear in a single `swift_*` target. Adding the same source file to multiple `swift_*` targets can lead to binary bloat and/or symbol collisions. If specific sources need to be shared by multiple targets, consider factoring them out into their own `swift_library` instead. | List of labels | 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 | `[]` | | 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 | `[]` | | 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 | `[]` | diff --git a/swift/internal/attrs.bzl b/swift/internal/attrs.bzl index 680864f5a..053083e81 100644 --- a/swift/internal/attrs.bzl +++ b/swift/internal/attrs.bzl @@ -153,6 +153,12 @@ when compiling this module and any modules that directly depend on it. allow_files = ["swift"], doc = """\ A list of `.swift` source files that will be compiled into the library. + +Except in very rare circumstances, a Swift source file should only appear in a +single `swift_*` target. Adding the same source file to multiple `swift_*` +targets can lead to binary bloat and/or symbol collisions. If specific sources +need to be shared by multiple targets, consider factoring them out into their +own `swift_library` instead. """, flags = ["DIRECT_COMPILE_TIME_INPUT"], mandatory = requires_srcs, diff --git a/swift/swift_overlay.bzl b/swift/swift_overlay.bzl index f0e99d67e..c855b11c4 100644 --- a/swift/swift_overlay.bzl +++ b/swift/swift_overlay.bzl @@ -168,6 +168,12 @@ the `swift_overlay` imports any modules other than its C/Objective-C side, the overlay target must explicitly depend on them as well. This means that an overlay can have a different set of dependencies than the underlying module, if desired. + +There is a tight coupling between a Swift overlay and the C/Objective-C module +to which it is being applied, so a specific `swift_overlay` target should only +be referenced by the `aspect_hints` of a single `objc_library` or `cc_library` +target. Referencing a `swift_overlay` from multiple targets' `aspect_hints` is +almost always an anti-pattern. """, fragments = ["cpp"], implementation = _swift_overlay_impl, From 6d75c15d4b4afb8d3283d15e3e2dcc8364594d32 Mon Sep 17 00:00:00 2001 From: Brentley Jones Date: Fri, 25 Oct 2024 08:07:00 -0500 Subject: [PATCH 31/92] Remove unnecessary `SwiftBinaryInfo` required provider from `plugins` attribute (#1445) I incorrectly added this as part of the cherry-pick in 5f6ac77099eda1838c36c7e3c7b4e88309caad1d. --------- Signed-off-by: Brentley Jones --- swift/internal/attrs.bzl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/swift/internal/attrs.bzl b/swift/internal/attrs.bzl index 053083e81..46b56a191 100644 --- a/swift/internal/attrs.bzl +++ b/swift/internal/attrs.bzl @@ -15,7 +15,7 @@ """Common attributes used by multiple Swift build rules.""" load("@bazel_skylib//lib:dicts.bzl", "dicts") -load("//swift:providers.bzl", "SwiftBinaryInfo", "SwiftInfo") +load("//swift:providers.bzl", "SwiftInfo") load(":providers.bzl", "SwiftCompilerPluginInfo") def swift_common_rule_attrs( @@ -146,7 +146,7 @@ Swift 5.9+. A list of `swift_compiler_plugin` targets that should be loaded by the compiler when compiling this module and any modules that directly depend on it. """, - providers = [[SwiftBinaryInfo, SwiftCompilerPluginInfo]], + providers = [SwiftCompilerPluginInfo], ), "srcs": attr.label_list( allow_empty = not requires_srcs, From eb60754365ab3cf9ad2328bfd63803608188da31 Mon Sep 17 00:00:00 2001 From: Brentley Jones Date: Fri, 25 Oct 2024 09:20:57 -0500 Subject: [PATCH 32/92] Remove redundant code in `swift_import` (#1446) Signed-off-by: Brentley Jones --- swift/swift_import.bzl | 8 -------- 1 file changed, 8 deletions(-) diff --git a/swift/swift_import.bzl b/swift/swift_import.bzl index 48178162a..02e5e798a 100644 --- a/swift/swift_import.bzl +++ b/swift/swift_import.bzl @@ -52,14 +52,6 @@ def _swift_import_impl(ctx): swiftinterface = ctx.file.swiftinterface swiftmodule = ctx.file.swiftmodule - swift_toolchain = get_swift_toolchain(ctx) - feature_configuration = configure_features( - ctx = ctx, - swift_toolchain = swift_toolchain, - requested_features = ctx.features, - unsupported_features = ctx.disabled_features, - ) - if not (swiftinterface or swiftmodule): fail("One of 'swiftinterface' or 'swiftmodule' must be " + "specified.") From 433e5af19e8182421c73c1980b3b3431644f8011 Mon Sep 17 00:00:00 2001 From: Brentley Jones Date: Wed, 30 Oct 2024 15:27:57 -0500 Subject: [PATCH 33/92] Add `data` attribute to `mixed_language_library` (#1450) --- doc/rules.md | 11 ++++---- mixed_language/internal/library.bzl | 31 ++++++++++++++++++----- mixed_language/mixed_language_library.bzl | 8 ++++++ 3 files changed, 38 insertions(+), 12 deletions(-) diff --git a/doc/rules.md b/doc/rules.md index bee0d38ce..ef85bc3e9 100644 --- a/doc/rules.md +++ b/doc/rules.md @@ -1064,11 +1064,11 @@ swift_library( ## mixed_language_library
-mixed_language_library(name, alwayslink, clang_copts, clang_defines, clang_srcs, enable_modules,
-                       hdrs, includes, linkopts, module_map, module_name, non_arc_srcs, private_deps,
-                       sdk_dylibs, sdk_frameworks, swift_copts, swift_defines, swift_srcs,
-                       swiftc_inputs, textual_hdrs, umbrella_header, weak_sdk_frameworks, deps,
-                       kwargs)
+mixed_language_library(name, alwayslink, clang_copts, clang_defines, clang_srcs, data,
+                       enable_modules, hdrs, includes, linkopts, module_map, module_name,
+                       non_arc_srcs, private_deps, sdk_dylibs, sdk_frameworks, swift_copts,
+                       swift_defines, swift_srcs, swiftc_inputs, textual_hdrs, umbrella_header,
+                       weak_sdk_frameworks, deps, kwargs)
 
Creates a mixed language library from a Clang and Swift library target pair. @@ -1087,6 +1087,7 @@ Once that is the case, this macro will be deprecated. | clang_copts | The compiler flags for the clang library. These will only be used for the clang library. If you want them to affect the swift library as well, you need to pass them with `-Xcc` in `swift_copts`. | `[]` | | clang_defines | Extra clang `-D` flags to pass to the compiler. They should be in the form `KEY=VALUE` or simply `KEY` and are passed not only to the compiler for this target (as `clang_copts` are) but also to all dependers of this target. Subject to "Make variable" substitution and Bourne shell tokenization. | `[]` | | clang_srcs | The list of C, C++, Objective-C, or Objective-C++ sources for the clang library. | none | +| 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. | `[]` | | enable_modules | Enables clang module support (via `-fmodules`). Setting this to `True` will allow you to `@import` system headers and other targets: `@import UIKit;` `@import path_to_package_target;`. | `False` | | hdrs | The list of C, C++, Objective-C, or Objective-C++ header files published by this library to be included by sources in dependent rules. This can't include `umbrella_header`. | `[]` | | includes | List of `#include`/`#import` search paths to add to this target and all depending targets. | `[]` | diff --git a/mixed_language/internal/library.bzl b/mixed_language/internal/library.bzl index d0bb5b3ea..76d18167b 100644 --- a/mixed_language/internal/library.bzl +++ b/mixed_language/internal/library.bzl @@ -181,13 +181,20 @@ def _mixed_language_library_impl(ctx): ) return [ - DefaultInfo(files = depset( - outputs, - transitive = [ - swift_target[DefaultInfo].files, - clang_target[DefaultInfo].files, - ], - )), + DefaultInfo( + files = depset( + outputs, + transitive = [ + swift_target[DefaultInfo].files, + clang_target[DefaultInfo].files, + ], + ), + runfiles = ctx.runfiles( + collect_data = True, + collect_default = True, + files = ctx.files.data, + ), + ), cc_info, coverage_common.instrumented_files_info( ctx, @@ -206,6 +213,16 @@ The non-Swift portion of the mixed language module. mandatory = True, providers = [CcInfo], ), + "data": attr.label_list( + allow_files = True, + doc = """\ +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. +""", + ), "deps": swift_deps_attr( aspects = [swift_clang_module_aspect], doc = "Dependencies of the target being built.", diff --git a/mixed_language/mixed_language_library.bzl b/mixed_language/mixed_language_library.bzl index 9e27d78be..2c62d3baf 100644 --- a/mixed_language/mixed_language_library.bzl +++ b/mixed_language/mixed_language_library.bzl @@ -39,6 +39,7 @@ def mixed_language_library( clang_copts = [], clang_defines = [], clang_srcs, + data = [], enable_modules = False, hdrs = [], includes = [], @@ -84,6 +85,12 @@ def mixed_language_library( substitution and Bourne shell tokenization. clang_srcs: The list of C, C++, Objective-C, or Objective-C++ sources for the clang library. + 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. enable_modules: Enables clang module support (via `-fmodules`). Setting this to `True` will allow you to `@import` system headers and other targets: `@import UIKit;` `@import path_to_package_target;`. @@ -371,6 +378,7 @@ a mixed language Swift library, use a clang only library rule like \ name = name, aspect_hints = aspect_hints, clang_target = ":" + clang_library_name, + data = data, features = features, module_map = module_map, module_name = module_name, From c6ec7990d19ce81f080e50d111cb32479e2ec83c Mon Sep 17 00:00:00 2001 From: Brentley Jones Date: Wed, 30 Oct 2024 20:17:52 -0500 Subject: [PATCH 34/92] Set `alwayslink` on the clang target of `mixed_language_library` as well (#1451) Signed-off-by: Brentley Jones --- doc/rules.md | 2 +- mixed_language/mixed_language_library.bzl | 13 +++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/doc/rules.md b/doc/rules.md index ef85bc3e9..9723e51c0 100644 --- a/doc/rules.md +++ b/doc/rules.md @@ -1083,7 +1083,7 @@ Once that is the case, this macro will be deprecated. | Name | Description | Default Value | | :------------- | :------------- | :------------- | | name | The name of the target. | none | -| alwayslink | If true, any binary that depends (directly or indirectly) on this library will link in all the object files for the files listed in `swift_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. | `False` | +| alwayslink | If true, any binary that depends (directly or indirectly) on this library will link in all the object files for the files listed in `clang_srcs` and `swift_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. | `False` | | clang_copts | The compiler flags for the clang library. These will only be used for the clang library. If you want them to affect the swift library as well, you need to pass them with `-Xcc` in `swift_copts`. | `[]` | | clang_defines | Extra clang `-D` flags to pass to the compiler. They should be in the form `KEY=VALUE` or simply `KEY` and are passed not only to the compiler for this target (as `clang_copts` are) but also to all dependers of this target. Subject to "Make variable" substitution and Bourne shell tokenization. | `[]` | | clang_srcs | The list of C, C++, Objective-C, or Objective-C++ sources for the clang library. | none | diff --git a/mixed_language/mixed_language_library.bzl b/mixed_language/mixed_language_library.bzl index 2c62d3baf..927d1c25c 100644 --- a/mixed_language/mixed_language_library.bzl +++ b/mixed_language/mixed_language_library.bzl @@ -69,12 +69,12 @@ def mixed_language_library( name: The name of the target. alwayslink: If true, any binary that depends (directly or indirectly) on this library will link in all the object files for the files listed - in `swift_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. + in `clang_srcs` and `swift_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. clang_copts: The compiler flags for the clang library. These will only be used for the clang library. If you want them to affect the swift library as well, you need to pass them with `-Xcc` in `swift_copts`. @@ -341,6 +341,7 @@ a mixed language Swift library, use a clang only library rule like \ native.objc_library( name = clang_library_name, srcs = clang_srcs, + alwayslink = alwayslink, hdrs = adjusted_hdrs, non_arc_srcs = non_arc_srcs, # `internal_swift_interop_name` isn't needed here because From a0ad3ed29da7cf2fe0c5b1487f8d4ad72bf21ae6 Mon Sep 17 00:00:00 2001 From: Ed Schouten Date: Fri, 8 Nov 2024 00:49:25 +0100 Subject: [PATCH 35/92] Stop using config.string(allow_multiple = True) (#1452) According to the Bazel documentation, this option is deprecated. config.string_list(repeatable = True) should be used instead. This PR is similar to this one, which I sent out for rules_rust: https://github.com/bazelbuild/rules_rust/pull/2983 --- swift/BUILD | 2 +- swift/internal/build_settings.bzl | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/swift/BUILD b/swift/BUILD index 0a5d1f09d..271f5e31f 100644 --- a/swift/BUILD +++ b/swift/BUILD @@ -367,7 +367,7 @@ swift_interop_hint( # User settable flag that specifies additional Swift copts on a per-swiftmodule basis. per_module_swiftcopt_flag( name = "per_module_swiftcopt", - build_setting_default = "", + build_setting_default = [], ) # NOTE: Enabling this flag will transition --proto_compiler to diff --git a/swift/internal/build_settings.bzl b/swift/internal/build_settings.bzl index 61f937b90..b42affa91 100644 --- a/swift/internal/build_settings.bzl +++ b/swift/internal/build_settings.bzl @@ -89,9 +89,9 @@ def _per_module_swiftcopt_flag_impl(ctx): return PerModuleSwiftCoptSettingInfo(value = value) per_module_swiftcopt_flag = rule( - build_setting = config.string( + build_setting = config.string_list( flag = True, - allow_multiple = True, + repeatable = True, ), # TODO(b/186869451): Support adding swiftcopts by module name in addition # to the target label. From 9ddceb738b7cbacd16be3001997e8d32c3669555 Mon Sep 17 00:00:00 2001 From: Tony Allevato Date: Wed, 28 Aug 2024 05:28:15 -0700 Subject: [PATCH 36/92] Add an API that lets aspects ask whether a target is a `swift_overlay` PiperOrigin-RevId: 668406915 (cherry picked from commit b01724715c0d72ccda8df1080f5c9b234490970f) Signed-off-by: Brentley Jones --- doc/BUILD | 1 + doc/api.md | 28 +++++++++++++++++++++++++++ doc/doc.bzl | 5 +++++ swift/BUILD | 9 +++++++++ swift/swift_overlay_helpers.bzl | 34 +++++++++++++++++++++++++++++++++ 5 files changed, 77 insertions(+) create mode 100644 swift/swift_overlay_helpers.bzl diff --git a/doc/BUILD b/doc/BUILD index 2498ccec3..ce78caab3 100644 --- a/doc/BUILD +++ b/doc/BUILD @@ -8,6 +8,7 @@ _DOC_SRCS = { "api": [ "create_swift_interop_info", "derive_swift_module_name", + "is_swift_overlay", "swift_common", ], "providers": [ diff --git a/doc/api.md b/doc/api.md index 0052eebf3..655ed1deb 100644 --- a/doc/api.md +++ b/doc/api.md @@ -107,6 +107,34 @@ This mapping is intended to be fairly predictable, but not reversible. The module name derived from the label. + + +## is_swift_overlay + +
+is_swift_overlay(target)
+
+ +Returns a value indicating whether the given target is a `swift_overlay`. + +This is meant to be used by aspects that visit the `aspect_hints` of a +target to identify the `swift_overlay` target (if present) without making +the provider public or requiring those aspects to propagate the information +themselves. + + +**PARAMETERS** + + +| Name | Description | Default Value | +| :------------- | :------------- | :------------- | +| target | A `Target`; for example, an element of `ctx.rule.attr.aspect_hints` accessed inside an aspect. | none | + +**RETURNS** + +True if the target is a `swift_overlay`, otherwise False. + + ## swift_common.cc_feature_configuration diff --git a/doc/doc.bzl b/doc/doc.bzl index 90fb65af5..f6696fd7e 100644 --- a/doc/doc.bzl +++ b/doc/doc.bzl @@ -99,6 +99,10 @@ load( _swift_module_mapping_test = "swift_module_mapping_test", ) load("//swift:swift_overlay.bzl", _swift_overlay = "swift_overlay") +load( + "//swift:swift_overlay_helpers.bzl", + _is_swift_overlay = "is_swift_overlay", +) load( "//swift:swift_package_configuration.bzl", _swift_package_configuration = "swift_package_configuration", @@ -118,6 +122,7 @@ swift_proto_library_group = _swift_proto_library_group # swift symbols create_swift_interop_info = _create_swift_interop_info derive_swift_module_name = _derive_swift_module_name +is_swift_overlay = _is_swift_overlay swift_common = _swift_common SwiftInfo = _SwiftInfo SwiftToolchainInfo = _SwiftToolchainInfo diff --git a/swift/BUILD b/swift/BUILD index 271f5e31f..0f6dd8db3 100644 --- a/swift/BUILD +++ b/swift/BUILD @@ -251,6 +251,14 @@ bzl_library( ], ) +bzl_library( + name = "swift_overlay_helpers", + srcs = ["swift_overlay_helpers.bzl"], + deps = [ + "//swift/internal:providers", + ], +) + bzl_library( name = "swift_package_configuration", srcs = ["swift_package_configuration.bzl"], @@ -313,6 +321,7 @@ bzl_library( ":swift_module_mapping", ":swift_module_mapping_test", ":swift_overlay", + ":swift_overlay_helpers", ":swift_package_configuration", ":swift_symbol_graph_aspect", ":swift_test", diff --git a/swift/swift_overlay_helpers.bzl b/swift/swift_overlay_helpers.bzl new file mode 100644 index 000000000..5e2ac24f6 --- /dev/null +++ b/swift/swift_overlay_helpers.bzl @@ -0,0 +1,34 @@ +# Copyright 2024 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. + +"""Helper functions for users of Swift overlays.""" + +load("//swift/internal:providers.bzl", "SwiftOverlayCompileInfo") + +def is_swift_overlay(target): + """Returns a value indicating whether the given target is a `swift_overlay`. + + This is meant to be used by aspects that visit the `aspect_hints` of a + target to identify the `swift_overlay` target (if present) without making + the provider public or requiring those aspects to propagate the information + themselves. + + Args: + target: A `Target`; for example, an element of + `ctx.rule.attr.aspect_hints` accessed inside an aspect. + + Returns: + True if the target is a `swift_overlay`, otherwise False. + """ + return SwiftOverlayCompileInfo in target From 8945e282f5581be3c8444f56c374e7662a0b1a92 Mon Sep 17 00:00:00 2001 From: Tony Allevato Date: Thu, 3 Oct 2024 05:41:05 -0700 Subject: [PATCH 37/92] Make sure that the path to the generated header in the generated module map is workspace-relative when the feature configuration asks for it PiperOrigin-RevId: 681838149 (cherry picked from commit f2eba62788e1abf31dd4eddcbe443c4e97d9c817) Signed-off-by: Brentley Jones --- swift/internal/compiling.bzl | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/swift/internal/compiling.bzl b/swift/internal/compiling.bzl index d5ea3e1f1..fd184fc9a 100644 --- a/swift/internal/compiling.bzl +++ b/swift/internal/compiling.bzl @@ -46,6 +46,7 @@ load( "SWIFT_FEATURE_HEADERS_ALWAYS_ACTION_INPUTS", "SWIFT_FEATURE_INDEX_WHILE_BUILDING", "SWIFT_FEATURE_MODULAR_INDEXING", + "SWIFT_FEATURE_MODULE_MAP_HOME_IS_CWD", "SWIFT_FEATURE_OPT", "SWIFT_FEATURE_OPT_USES_WMO", "SWIFT_FEATURE_PROPAGATE_GENERATED_MODULE_MAP", @@ -1257,6 +1258,10 @@ def _declare_compile_outputs( module_map_file = generated_module_map, module_name = module_name, public_headers = [generated_header], + workspace_relative = is_feature_enabled( + feature_configuration = feature_configuration, + feature_name = SWIFT_FEATURE_MODULE_MAP_HOME_IS_CWD, + ), ) else: generated_module_map = None From 6eb89d1ba51cbd23e5fa3801f9092ba7e63cbd91 Mon Sep 17 00:00:00 2001 From: Brentley Jones Date: Mon, 18 Nov 2024 14:45:35 -0600 Subject: [PATCH 38/92] Don't drop `swift_overlay` linking contexts when an overlay depends on another library that has an overlay (#1440) PiperOrigin-RevId: 665946723 (cherry picked from commit 6d0fc0f4f71a430958a7453d51cf8f8ea70e0520) Signed-off-by: Brentley Jones Co-authored-by: Tony Allevato --- swift/swift_clang_module_aspect.bzl | 11 ++++++++++- swift/swift_overlay.bzl | 4 +++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/swift/swift_clang_module_aspect.bzl b/swift/swift_clang_module_aspect.bzl index da99aac89..066cae727 100644 --- a/swift/swift_clang_module_aspect.bzl +++ b/swift/swift_clang_module_aspect.bzl @@ -552,7 +552,16 @@ def _compile_swift_overlay( label = overlay_info.label, linking_contexts = [ cc_info.linking_context - for cc_info in overlay_info.deps.cc_infos + overlay_info.private_deps.cc_infos + for cc_info in ( + overlay_info.deps.cc_infos + + overlay_info.private_deps.cc_infos + ) + ] + [ + overlay_info.linking_context + for overlay_info in ( + overlay_info.deps.swift_overlay_infos + + overlay_info.private_deps.swift_overlay_infos + ) ], module_context = compile_result.module_context, swift_toolchain = swift_toolchain, diff --git a/swift/swift_overlay.bzl b/swift/swift_overlay.bzl index c855b11c4..6a7706a5d 100644 --- a/swift/swift_overlay.bzl +++ b/swift/swift_overlay.bzl @@ -35,7 +35,7 @@ load( "get_providers", "include_developer_search_paths", ) -load(":providers.bzl", "SwiftInfo") +load(":providers.bzl", "SwiftInfo", "SwiftOverlayInfo") load(":swift_clang_module_aspect.bzl", "swift_clang_module_aspect") def _swift_overlay_impl(ctx): @@ -65,11 +65,13 @@ def _swift_overlay_impl(ctx): private_deps = struct( cc_infos = get_providers(private_deps, CcInfo), swift_infos = get_providers(private_deps, SwiftInfo), + swift_overlay_infos = get_providers(private_deps, SwiftOverlayInfo), ), alwayslink = ctx.attr.alwayslink, deps = struct( cc_infos = get_providers(deps, CcInfo), swift_infos = get_providers(deps, SwiftInfo), + swift_overlay_infos = get_providers(deps, SwiftOverlayInfo), ), )] From be95b33eafc4a19070c4f264de2fe7bebc1118a5 Mon Sep 17 00:00:00 2001 From: Aaron Sky Date: Tue, 19 Nov 2024 15:29:39 -0500 Subject: [PATCH 39/92] Add Aaron to `CODEOWNERS` (#1455) see also: bazelbuild/apple_support#349 bazelbuild/rules_apple#2602 --- CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CODEOWNERS b/CODEOWNERS index e21872944..a249870eb 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -1 +1 @@ -* @BalestraPatrick @brentleyjones @keith @luispadron @mattrobmattrob @thii +* @aaronsky @BalestraPatrick @brentleyjones @keith @luispadron @mattrobmattrob @thii From 26d4f8f3e65fc852367a820661f414ca30cd4ba0 Mon Sep 17 00:00:00 2001 From: Brentley Jones Date: Fri, 22 Nov 2024 12:32:26 -0600 Subject: [PATCH 40/92] Fix `apple.swizzle_absolute_xcttestsourcelocation` usage (#1457) Regressed in 43af3816b5bea4e0add6523ee1642d75157f783d. Signed-off-by: Brentley Jones --- swift/swift_test.bzl | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/swift/swift_test.bzl b/swift/swift_test.bzl index ad2a13b19..66332ecce 100644 --- a/swift/swift_test.bzl +++ b/swift/swift_test.bzl @@ -282,17 +282,6 @@ def _swift_test_impl(ctx): discover_tests = ctx.attr.discover_tests objc_test_discovery = swift_toolchain.test_configuration.objc_test_discovery - # `is_feature_enabled` isn't used, as it requires the prefix of the feature - # to start with `swift.` - swizzle_absolute_xcttestsourcelocation = ( - "apple.swizzle_absolute_xcttestsourcelocation" in - feature_configuration._enabled_features - ) - - extra_link_deps = [] - if swizzle_absolute_xcttestsourcelocation: - extra_link_deps.append(ctx.attr._swizzle_absolute_xcttestsourcelocation) - deps = list(ctx.attr.deps) test_runner_deps = list(ctx.attr._test_runner_deps) @@ -305,6 +294,17 @@ def _swift_test_impl(ctx): else: additional_link_deps = [] + # `is_feature_enabled` isn't used, as it requires the prefix of the feature + # to start with `swift.` + swizzle_absolute_xcttestsourcelocation = ( + "apple.swizzle_absolute_xcttestsourcelocation" in + feature_configuration._enabled_features + ) + if swizzle_absolute_xcttestsourcelocation: + additional_link_deps.append( + ctx.attr._swizzle_absolute_xcttestsourcelocation, + ) + # We also need to collect nested providers from `SwiftBinaryInfo` since we # support testing those. deps_cc_infos = [] From 61e2c46dbfac6b9c11ecfb2c8e92f3e157ea9f27 Mon Sep 17 00:00:00 2001 From: Luis Padron Date: Fri, 22 Nov 2024 17:55:05 -0500 Subject: [PATCH 41/92] Add support for `package_name` in `mixed_language_library` (#1458) --- doc/rules.md | 7 ++++--- mixed_language/mixed_language_library.bzl | 5 +++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/doc/rules.md b/doc/rules.md index 9723e51c0..c1c62514e 100644 --- a/doc/rules.md +++ b/doc/rules.md @@ -1066,9 +1066,9 @@ swift_library(
 mixed_language_library(name, alwayslink, clang_copts, clang_defines, clang_srcs, data,
                        enable_modules, hdrs, includes, linkopts, module_map, module_name,
-                       non_arc_srcs, private_deps, sdk_dylibs, sdk_frameworks, swift_copts,
-                       swift_defines, swift_srcs, swiftc_inputs, textual_hdrs, umbrella_header,
-                       weak_sdk_frameworks, deps, kwargs)
+                       non_arc_srcs, package_name, private_deps, sdk_dylibs, sdk_frameworks,
+                       swift_copts, swift_defines, swift_srcs, swiftc_inputs, textual_hdrs,
+                       umbrella_header, weak_sdk_frameworks, deps, kwargs)
 
Creates a mixed language library from a Clang and Swift library target pair. @@ -1095,6 +1095,7 @@ Once that is the case, this macro will be deprecated. | 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 `hdrs`. If this argument is provided, then `module_name` must also be provided.

Warning: If a module map (whether provided here or not) is able to be found via an include path, it will result in duplicate module definition errors for downstream targets unless sandboxing or remote execution is used. | `None` | | 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. | `None` | | non_arc_srcs | The list of Objective-C files that are processed to create the library target that DO NOT use ARC. The files in this attribute are treated very similar to those in the `clang_srcs` attribute, but are compiled without ARC enabled. | `[]` | +| package_name | The semantic package of the Swift target being built. Targets with the same `package_name` can access APIs using the 'package' access control modifier in Swift 5.9+. | `None` | | 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. | `[]` | | sdk_dylibs | A list of of SDK `.dylib` libraries to link with. For instance, "libz" or "libarchive". "libc++" is included automatically if the binary has any C++ or Objective-C++ sources in its dependency tree. When linking a binary, all libraries named in that binary's transitive dependency graph are used. | `[]` | | sdk_frameworks | A list of SDK frameworks to link with (e.g. "AddressBook", "QuartzCore").

When linking a top level Apple binary, all SDK frameworks listed in that binary's transitive dependency graph are linked. | `[]` | diff --git a/mixed_language/mixed_language_library.bzl b/mixed_language/mixed_language_library.bzl index 927d1c25c..5e603c24e 100644 --- a/mixed_language/mixed_language_library.bzl +++ b/mixed_language/mixed_language_library.bzl @@ -47,6 +47,7 @@ def mixed_language_library( module_map = None, module_name = None, non_arc_srcs = [], + package_name = None, private_deps = [], sdk_dylibs = [], sdk_frameworks = [], @@ -118,6 +119,9 @@ def mixed_language_library( the library target that DO NOT use ARC. The files in this attribute are treated very similar to those in the `clang_srcs` attribute, but are compiled without ARC enabled. + package_name: The semantic package of the Swift target being built. Targets + with the same `package_name` can access APIs using the 'package' + access control modifier in Swift 5.9+. 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 @@ -314,6 +318,7 @@ a mixed language Swift library, use a clang only library rule like \ generated_header_name = module_name + "-Swift.h", linkopts = linkopts, module_name = module_name, + package_name = package_name, private_deps = private_deps, swiftc_inputs = swiftc_inputs, tags = internal_tags, From ce89d743bd50a7af29f82d551d61656f995aec11 Mon Sep 17 00:00:00 2001 From: Jonathan Schear Date: Wed, 4 Dec 2024 13:27:05 -0500 Subject: [PATCH 42/92] =?UTF-8?q?Apply=20-enable-bare-slash-regex=20action?= =?UTF-8?q?=20config=20to=20SWIFT=5FACTION=5FDERIVE=5FF=E2=80=A6=20(#1464)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit …ILES This action name was dropped when the declaration of this `ActionConfigInfo` was moved: https://github.com/bazelbuild/rules_swift/commit/407becabb204c20a82e12f62818a1793ff36d853#diff-535d0dbea9614c6905a30dfdb0e708df3b16fc68f8930d6aba519342509dc657L347-L359 https://github.com/bazelbuild/rules_swift/commit/407becabb204c20a82e12f62818a1793ff36d853#diff-2785fcfbf39fcb0e5f53eb796c9711765273e1895af7213f7bdcbdcf6ed8a18eR1050-R1059 --- swift/toolchains/config/compile_config.bzl | 1 + 1 file changed, 1 insertion(+) diff --git a/swift/toolchains/config/compile_config.bzl b/swift/toolchains/config/compile_config.bzl index bb118f212..4976752ea 100644 --- a/swift/toolchains/config/compile_config.bzl +++ b/swift/toolchains/config/compile_config.bzl @@ -1137,6 +1137,7 @@ def compile_action_configs( actions = [ SWIFT_ACTION_COMPILE, SWIFT_ACTION_COMPILE_MODULE_INTERFACE, + SWIFT_ACTION_DERIVE_FILES, ], configurators = [add_arg("-enable-bare-slash-regex")], features = [SWIFT_FEATURE_ENABLE_BARE_SLASH_REGEX], From 9bba63722aabad26b577a29a8a84d76f1ec0b5a0 Mon Sep 17 00:00:00 2001 From: Keith Smiley Date: Thu, 5 Dec 2024 15:51:55 -0800 Subject: [PATCH 43/92] Run stardoc with older bazel (#1466) Just to make CI green for now --- .bazelci/presubmit.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.bazelci/presubmit.yml b/.bazelci/presubmit.yml index 0ca4a71b5..f2dabfee8 100644 --- a/.bazelci/presubmit.yml +++ b/.bazelci/presubmit.yml @@ -126,7 +126,8 @@ tasks: doc_tests: name: "Doc tests" - bazel: last_green + # TODO: Bump when https://github.com/bazelbuild/stardoc/issues/94#issuecomment-2492218404 is resolved + bazel: 7.x <<: *linux_environment shell_commands: - "echo --- Downloading and extracting Swift $SWIFT_VERSION to $SWIFT_HOME" From cc6af560bfb093693ce60d92dfd686a736420424 Mon Sep 17 00:00:00 2001 From: Brentley Jones Date: Mon, 16 Dec 2024 10:12:46 -0600 Subject: [PATCH 44/92] Add implicit deps to `swift_library_group` (#1468) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Without this, we won’t add required linkopts to `apple_xcframework_import` targets. Signed-off-by: Brentley Jones --- swift/BUILD | 1 + swift/swift_library_group.bzl | 14 ++++++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/swift/BUILD b/swift/BUILD index 0f6dd8db3..348c80e67 100644 --- a/swift/BUILD +++ b/swift/BUILD @@ -217,6 +217,7 @@ bzl_library( ":providers", ":swift_clang_module_aspect", "//swift/internal:attrs", + "//swift/internal:toolchain_utils", "//swift/internal:utils", ], ) diff --git a/swift/swift_library_group.bzl b/swift/swift_library_group.bzl index 7025f94ec..4b52684e1 100644 --- a/swift/swift_library_group.bzl +++ b/swift/swift_library_group.bzl @@ -15,24 +15,33 @@ """Implementation of the `swift_library_group` rule.""" load("//swift/internal:attrs.bzl", "swift_deps_attr") +load( + "//swift/internal:toolchain_utils.bzl", + "get_swift_toolchain", + "use_swift_toolchain", +) load("//swift/internal:utils.bzl", "get_providers") load(":providers.bzl", "SwiftInfo") load(":swift_clang_module_aspect.bzl", "swift_clang_module_aspect") def _swift_library_group_impl(ctx): + swift_toolchain = get_swift_toolchain(ctx) + deps = ctx.attr.deps return [ DefaultInfo(), cc_common.merge_cc_infos( - cc_infos = [dep[CcInfo] for dep in deps if CcInfo in dep], + cc_infos = ([dep[CcInfo] for dep in deps if CcInfo in dep] + + swift_toolchain.implicit_deps_providers.cc_infos), ), coverage_common.instrumented_files_info( ctx, dependency_attributes = ["deps"], ), SwiftInfo( - swift_infos = get_providers(deps, SwiftInfo), + swift_infos = (get_providers(deps, SwiftInfo) + + swift_toolchain.implicit_deps_providers.swift_infos), ), # Propagate an `apple_common.Objc` provider with linking info about the # library so that linking with Apple Starlark APIs/rules works @@ -60,4 +69,5 @@ A new module isn't created for this target, you need to import the grouped libraries directly. """, implementation = _swift_library_group_impl, + toolchains = use_swift_toolchain(), ) From 263505e203552c7b40c1425725cecd071da2c8a6 Mon Sep 17 00:00:00 2001 From: Thanh Vu <10118456+vikage@users.noreply.github.com> Date: Fri, 17 Jan 2025 11:01:18 +0700 Subject: [PATCH 45/92] Map const-values in output file map for incremental (#1471) Since we extracting `swiftconstvalues` from https://github.com/bazelbuild/rules_swift/pull/1170 But we forgot to update the path for swiftconstvalues in the incremental output file map. It leads to the incremental feature not working. For example, demo: https://github.com/vikage/DemoBazelSwiftIncremental When I change file B.swift (No other file uses struct B) but log shows build A.swift and C.swift as well I used `-driver-show-job-lifecycle` and `-driver-show-incremental` flags to show debug log ``` WARNING: Build option --swiftcopt has changed, discarding analysis cache (this can be expensive, see https://bazel.build/advanced/performance/iteration-speed). INFO: Analyzed target //:Demo (99 packages loaded, 1088 targets configured). INFO: From Compiling Swift module //:Demo: remark: Incremental compilation: Read dependency graph '/private/var/tmp/_bazel_elliotvu/e512a8189164dd2ae5386a6bdb0cc34e/execroot/_main/bazel-out/darwin_arm64-fastbuild/bin/_swift_incremental/Demo.output_file_map.priors' remark: Incremental compilation: Enabling incremental cross-module building remark: Incremental compilation: May skip current input: {compile: A.swift.o <= A.swift} remark: Incremental compilation: May skip current input: {compile: B.swift.o <= B.swift} remark: Incremental compilation: May skip current input: {compile: C.swift.o <= C.swift} remark: Incremental compilation: Missing an output; will queue {compile: A.swift.o <= A.swift} remark: Incremental compilation: Missing an output; will queue {compile: B.swift.o <= B.swift} remark: Incremental compilation: Missing an output; will queue {compile: C.swift.o <= C.swift} remark: Incremental compilation: Queuing (initial): {compile: A.swift.o <= A.swift} remark: Incremental compilation: Queuing (initial): {compile: B.swift.o <= B.swift} remark: Incremental compilation: Queuing (initial): {compile: C.swift.o <= C.swift} remark: Found 3 batchable jobs remark: Forming into 1 batch remark: Adding {compile: A.swift} to batch 0 remark: Adding {compile: B.swift} to batch 0 remark: Adding {compile: C.swift} to batch 0 remark: Forming batch job from 3 constituents: A.swift, B.swift, C.swift remark: Starting Emitting module for Demo remark: Finished Emitting module for Demo remark: Starting Compiling A.swift, B.swift, C.swift remark: Finished Compiling A.swift, B.swift, C.swift remark: Incremental compilation: Reading dependencies from A.swift remark: Incremental compilation: Reading dependencies from B.swift remark: Incremental compilation: Reading dependencies from C.swift remark: Incremental compilation: Scheduling all post-compile jobs because something was compiled ``` These are logs after fix. As you can see, only B.swift will be compile ``` INFO: Analyzed target //:Demo (0 packages loaded, 0 targets configured). INFO: From Compiling Swift module //:Demo: remark: Incremental compilation: Read dependency graph '/private/var/tmp/_bazel_elliotvu/e512a8189164dd2ae5386a6bdb0cc34e/execroot/_main/bazel-out/darwin_arm64-fastbuild/bin/_swift_incremental/Demo.output_file_map.priors' remark: Incremental compilation: Enabling incremental cross-module building remark: Incremental compilation: May skip current input: {compile: A.swift.o <= A.swift} remark: Incremental compilation: Scheduling changed input {compile: B.swift.o <= B.swift} remark: Incremental compilation: May skip current input: {compile: C.swift.o <= C.swift} remark: Incremental compilation: Queuing (initial): {compile: B.swift.o <= B.swift} remark: Incremental compilation: not scheduling dependents of B.swift; unknown changes remark: Incremental compilation: Skipping input: {compile: A.swift.o <= A.swift} remark: Incremental compilation: Skipping input: {compile: C.swift.o <= C.swift} remark: Found 1 batchable job remark: Forming into 1 batch remark: Adding {compile: B.swift} to batch 0 remark: Forming batch job from 1 constituents: B.swift remark: Starting Emitting module for Demo remark: Finished Emitting module for Demo remark: Starting Compiling B.swift remark: Finished Compiling B.swift remark: Incremental compilation: Reading dependencies from B.swift remark: Incremental compilation: Fingerprint changed for existing interface of top-level name 'B' in B.swift remark: Incremental compilation: Fingerprint changed for existing implementation of top-level name 'B' in B.swift remark: Incremental compilation: Fingerprint changed for existing interface of type '4Demo1BV' in B.swift remark: Incremental compilation: Fingerprint changed for existing implementation of type '4Demo1BV' in B.swift remark: Incremental compilation: Fingerprint changed for existing interface of potential members of '4Demo1BV' in B.swift remark: Incremental compilation: Fingerprint changed for existing implementation of potential members of '4Demo1BV' in B.swift remark: Incremental compilation: Scheduling all post-compile jobs because something was compiled remark: Skipped Compiling A.swift remark: Skipped Compiling C.swift ``` The solution here should be to remap swiftconstvalues path to `_swift_incremental` directory Related issue: https://github.com/bazelbuild/rules_swift/issues/1291 --- tools/worker/output_file_map.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/worker/output_file_map.cc b/tools/worker/output_file_map.cc index af3d0a803..5a4e9faef 100644 --- a/tools/worker/output_file_map.cc +++ b/tools/worker/output_file_map.cc @@ -99,8 +99,8 @@ void OutputFileMap::UpdateForIncremental( auto kind = output.key(); auto path = output.value().get(); - if (kind == "object") { - // If the file kind is "object", we want to update the path to point to + if (kind == "object" || kind == "const-values") { + // If the file kind is "object" or "const-values", we want to update the path to point to // the incremental storage area and then add a "swift-dependencies" // in the same location. auto new_path = MakeIncrementalOutputPath(path, derived); From 8692bb812e6ee5796717f90a0394198b907fca5c Mon Sep 17 00:00:00 2001 From: Aaron Sky Date: Thu, 16 Jan 2025 23:01:34 -0500 Subject: [PATCH 46/92] upgrade to Xcode 15.4 and Swift 5.10 on CI (#1472) WIP, to unblock CI on open PRs --- .bazelci/presubmit.yml | 4 ++-- .bcr/presubmit.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.bazelci/presubmit.yml b/.bazelci/presubmit.yml index f2dabfee8..c7ffcd2c9 100644 --- a/.bazelci/presubmit.yml +++ b/.bazelci/presubmit.yml @@ -4,7 +4,7 @@ x_defaults: # it doesn't know about; so that is used to avoid repeating common subparts. mac_common: &mac_common platform: macos_arm64 - xcode_version: "15.2" + xcode_version: "15.4" build_targets: - "//examples/..." test_targets: @@ -14,7 +14,7 @@ x_defaults: platform: ubuntu2004 environment: CC: clang - SWIFT_VERSION: "5.9.2" + SWIFT_VERSION: "5.10.1" SWIFT_HOME: "$HOME/swift-$SWIFT_VERSION" PATH: "$SWIFT_HOME/usr/bin:$PATH" linux_common: &linux_common diff --git a/.bcr/presubmit.yml b/.bcr/presubmit.yml index 130e94264..3b9f38b48 100644 --- a/.bcr/presubmit.yml +++ b/.bcr/presubmit.yml @@ -19,7 +19,7 @@ tasks: platform: ${{ platform }} environment: CC: "clang" - SWIFT_VERSION: "5.9.2" + SWIFT_VERSION: "5.10.1" SWIFT_HOME: "$HOME/swift-$SWIFT_VERSION" PATH: "$PATH:$SWIFT_HOME/usr/bin" shell_commands: *shell_commands From 991d7c650b9b241aef2d452076d5758833e1743f Mon Sep 17 00:00:00 2001 From: Sebastian Shanus Date: Thu, 16 Jan 2025 23:02:52 -0500 Subject: [PATCH 47/92] Include swift_proto_library attrs in compilation (#1473) Using `package_name` or `copt` attributes weren't being propogated down into the actual swiftc compilation. --- proto/swift_proto_utils.bzl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proto/swift_proto_utils.bzl b/proto/swift_proto_utils.bzl index d27742b46..56fc6f55d 100644 --- a/proto/swift_proto_utils.bzl +++ b/proto/swift_proto_utils.bzl @@ -263,11 +263,11 @@ def compile_swift_protos_for_target( compile_result = swift_common.compile( actions = ctx.actions, cc_infos = get_providers(compiler_deps, CcInfo), - copts = ["-parse-as-library"], + copts = ["-parse-as-library"] + getattr(attr, "copts", []), feature_configuration = feature_configuration, include_dev_srch_paths = include_dev_srch_paths, module_name = module_name, - package_name = None, + package_name = getattr(attr, "package_name", None), srcs = generated_swift_srcs, swift_toolchain = swift_toolchain, swift_infos = get_providers(compiler_deps, SwiftInfo), From 9cd260597aa4102bb1fa8a7a42f9fceb66c726e9 Mon Sep 17 00:00:00 2001 From: Corentin Kerisit Date: Sat, 25 Jan 2025 00:17:32 +0100 Subject: [PATCH 48/92] Mark non_module_deps extension as reproducible (#1481) --- MODULE.bazel | 2 +- swift/extensions.bzl | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/MODULE.bazel b/MODULE.bazel index ba159bfa3..bce62ac41 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -6,7 +6,7 @@ module( repo_name = "build_bazel_rules_swift", ) -bazel_dep(name = "bazel_features", version = "1.3.0") +bazel_dep(name = "bazel_features", version = "1.9.0") bazel_dep(name = "bazel_skylib", version = "1.3.0") bazel_dep(name = "apple_support", version = "1.15.1", repo_name = "build_bazel_apple_support") bazel_dep(name = "rules_cc", version = "0.0.2") diff --git a/swift/extensions.bzl b/swift/extensions.bzl index dc2a7ff5c..8986e8dcf 100644 --- a/swift/extensions.bzl +++ b/swift/extensions.bzl @@ -14,9 +14,18 @@ """Definitions for bzlmod module extensions.""" +load("@bazel_features//:features.bzl", "bazel_features") load("//swift:repositories.bzl", "swift_rules_dependencies") -def _non_module_deps_impl(_): +def _non_module_deps_impl(module_ctx): swift_rules_dependencies(include_bzlmod_ready_dependencies = False) + metadata_kwargs = {} + if bazel_features.external_deps.extension_metadata_has_reproducible: + metadata_kwargs["reproducible"] = True + + return module_ctx.extension_metadata( + **metadata_kwargs + ) + non_module_deps = module_extension(implementation = _non_module_deps_impl) From b5124a1e0541902791e3926742810bcd3603390e Mon Sep 17 00:00:00 2001 From: Brentley Jones Date: Tue, 4 Feb 2025 12:10:12 -0600 Subject: [PATCH 49/92] Print a warning, instead of an error, when not finding `swiftc.exe` (#1483) Similar to bd0e6e009165b8826f51f682bd9bd55b9f969a0e. Signed-off-by: Brentley Jones --- swift/internal/swift_autoconfiguration.bzl | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/swift/internal/swift_autoconfiguration.bzl b/swift/internal/swift_autoconfiguration.bzl index c120b6981..b2353f29b 100644 --- a/swift/internal/swift_autoconfiguration.bzl +++ b/swift/internal/swift_autoconfiguration.bzl @@ -352,7 +352,18 @@ def _get_python_bin(repository_ctx): def _create_windows_toolchain(repository_ctx): path_to_swiftc = repository_ctx.which("swiftc.exe") if not path_to_swiftc: - fail("No 'swiftc.exe' executable found in Path") + print("""\ +No 'swiftc.exe' executable found in $PATH. Not auto-generating a Windows Swift \ +toolchain. +""") # buildifier: disable=print + repository_ctx.file( + "BUILD", + """\ +# No 'swiftc.exe' executable found in $PATH. Not auto-generating a Windows \ +Swift toolchain. +""", + ) + return root = path_to_swiftc.dirname.dirname enabled_features = [ From fa4bbed836629ef82c79713f1b4c4065043ea581 Mon Sep 17 00:00:00 2001 From: Brentley Jones Date: Tue, 4 Feb 2025 12:21:03 -0600 Subject: [PATCH 50/92] Fix typo in `_check_supports_language_mode_6` (#1484) Signed-off-by: Brentley Jones --- swift/internal/swift_autoconfiguration.bzl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/swift/internal/swift_autoconfiguration.bzl b/swift/internal/swift_autoconfiguration.bzl index b2353f29b..19a6600e8 100644 --- a/swift/internal/swift_autoconfiguration.bzl +++ b/swift/internal/swift_autoconfiguration.bzl @@ -80,7 +80,7 @@ def _check_supports_language_mode_6(repository_ctx, swiftc_path, _temp_dir): if not almost_version: return False - major_version, _, _ = almost_version.parition(".") + major_version, _, _ = almost_version.partition(".") if not major_version: return False From 6cc410f3e98f9e9964b92860cb3a89565f6232f2 Mon Sep 17 00:00:00 2001 From: Brentley Jones Date: Thu, 6 Feb 2025 07:11:41 -0600 Subject: [PATCH 51/92] Create fewer Xcode toolchains (#1487) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We support all macOS execution platforms, we don’t need to specify the CPU as well. This creates half as many toolchains. Signed-off-by: Brentley Jones --- swift/internal/swift_autoconfiguration.bzl | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/swift/internal/swift_autoconfiguration.bzl b/swift/internal/swift_autoconfiguration.bzl index 19a6600e8..3857271cb 100644 --- a/swift/internal/swift_autoconfiguration.bzl +++ b/swift/internal/swift_autoconfiguration.bzl @@ -304,11 +304,6 @@ load( package(default_visibility = ["//visibility:public"]) -_OSX_DEVELOPER_PLATFORM_CPUS = [ - "arm64", - "x86_64", -] - xcode_swift_toolchain( name = "toolchain", features = [{feature_list}], @@ -316,10 +311,9 @@ xcode_swift_toolchain( [ toolchain( - name = "xcode-toolchain-" + arch + "-" + cpu, + name = "xcode-toolchain-" + arch, exec_compatible_with = [ "@platforms//os:macos", - "@platforms//cpu:" + cpu, ], target_compatible_with = APPLE_PLATFORMS_CONSTRAINTS[arch], toolchain = ":toolchain", @@ -327,7 +321,6 @@ xcode_swift_toolchain( visibility = ["//visibility:public"], ) for arch in APPLE_PLATFORMS_CONSTRAINTS.keys() - for cpu in _OSX_DEVELOPER_PLATFORM_CPUS ] """.format( feature_list = ", ".join([ From c9c70ee598e088cabbe1b98760556ec9132257dc Mon Sep 17 00:00:00 2001 From: Brentley Jones Date: Sun, 9 Feb 2025 14:26:37 -0600 Subject: [PATCH 52/92] Always create all toolchains (#1488) --- swift/internal/swift_autoconfiguration.bzl | 147 ++++++++------------- 1 file changed, 58 insertions(+), 89 deletions(-) diff --git a/swift/internal/swift_autoconfiguration.bzl b/swift/internal/swift_autoconfiguration.bzl index 3857271cb..667f96d81 100644 --- a/swift/internal/swift_autoconfiguration.bzl +++ b/swift/internal/swift_autoconfiguration.bzl @@ -212,14 +212,10 @@ def _create_linux_toolchain(repository_ctx): No 'swiftc' executable found in $PATH. Not auto-generating a Linux Swift \ toolchain. """) # buildifier: disable=print - repository_ctx.file( - "BUILD", - """\ + return """\ # No 'swiftc' executable found in $PATH. Not auto-generating a Linux Swift \ toolchain. -""", - ) - return +""" root = path_to_swiftc.dirname.dirname feature_values = _compute_feature_values(repository_ctx, path_to_swiftc) @@ -232,18 +228,9 @@ toolchain. feature_values.append(SWIFT_FEATURE_USE_AUTOLINK_EXTRACT) feature_values.append(SWIFT_FEATURE_USE_MODULE_WRAP) - repository_ctx.file( - "BUILD", - """\ -load( - "@build_bazel_rules_swift//swift/toolchains:swift_toolchain.bzl", - "swift_toolchain", -) - -package(default_visibility = ["//visibility:public"]) - + return """\ swift_toolchain( - name = "toolchain", + name = "linux-toolchain", arch = "{cpu}", features = [{feature_list}], os = "linux", @@ -261,27 +248,23 @@ toolchain( "@platforms//os:linux", "@platforms//cpu:{cpu}", ], - toolchain = ":toolchain", + toolchain = ":linux-toolchain", toolchain_type = "{toolchain_type}", visibility = ["//visibility:public"], ) """.format( - cpu = _normalized_linux_cpu(repository_ctx.os.arch), - feature_list = ", ".join([ - '"{}"'.format(feature) - for feature in feature_values - ]), - root = root, - toolchain_type = SWIFT_TOOLCHAIN_TYPE, - version_file = version_file, - ), + cpu = _normalized_linux_cpu(repository_ctx.os.arch), + feature_list = ", ".join([ + '"{}"'.format(feature) + for feature in feature_values + ]), + root = root, + toolchain_type = SWIFT_TOOLCHAIN_TYPE, + version_file = version_file, ) -def _create_xcode_toolchain(repository_ctx): +def _create_xcode_toolchain(): """Creates BUILD targets for the Swift toolchain on macOS using Xcode. - - Args: - repository_ctx: The repository rule context. """ feature_values = [ # TODO: This should be removed so that private headers can be used with @@ -290,22 +273,9 @@ def _create_xcode_toolchain(repository_ctx): SWIFT_FEATURE_MODULE_MAP_NO_PRIVATE_HEADERS, ] - repository_ctx.file( - "BUILD", - """ -load( - "@build_bazel_apple_support//configs:platforms.bzl", - "APPLE_PLATFORMS_CONSTRAINTS", -) -load( - "@build_bazel_rules_swift//swift/toolchains:xcode_swift_toolchain.bzl", - "xcode_swift_toolchain", -) - -package(default_visibility = ["//visibility:public"]) - + return """\ xcode_swift_toolchain( - name = "toolchain", + name = "xcode-toolchain", features = [{feature_list}], ) @@ -316,19 +286,18 @@ xcode_swift_toolchain( "@platforms//os:macos", ], target_compatible_with = APPLE_PLATFORMS_CONSTRAINTS[arch], - toolchain = ":toolchain", + toolchain = ":xcode-toolchain", toolchain_type = "{toolchain_type}", visibility = ["//visibility:public"], ) for arch in APPLE_PLATFORMS_CONSTRAINTS.keys() ] """.format( - feature_list = ", ".join([ - '"{}"'.format(feature) - for feature in feature_values - ]), - toolchain_type = SWIFT_TOOLCHAIN_TYPE, - ), + feature_list = ", ".join([ + '"{}"'.format(feature) + for feature in feature_values + ]), + toolchain_type = SWIFT_TOOLCHAIN_TYPE, ) def _get_python_bin(repository_ctx): @@ -349,14 +318,10 @@ def _create_windows_toolchain(repository_ctx): No 'swiftc.exe' executable found in $PATH. Not auto-generating a Windows Swift \ toolchain. """) # buildifier: disable=print - repository_ctx.file( - "BUILD", - """\ + return """\ # No 'swiftc.exe' executable found in $PATH. Not auto-generating a Windows \ Swift toolchain. -""", - ) - return +""" root = path_to_swiftc.dirname.dirname enabled_features = [ @@ -382,18 +347,9 @@ Swift toolchain. "ProgramData": repository_ctx.os.environ["ProgramData"], } - repository_ctx.file( - "BUILD", - """ -load( - "@build_bazel_rules_swift//swift/toolchains:swift_toolchain.bzl", - "swift_toolchain", -) - -package(default_visibility = ["//visibility:public"]) - + return """\ swift_toolchain( - name = "toolchain", + name = "windows-toolchain", arch = "x86_64", features = [{features}], os = "windows", @@ -412,32 +368,45 @@ toolchain( "@platforms//cpu:x86_64", ], target_compatible_with = APPLE_PLATFORMS_CONSTRAINTS[arch], - toolchain = ":toolchain", + toolchain = ":windows-toolchain", toolchain_type = "{toolchain_type}", visibility = ["//visibility:public"], ) """.format( - features = ", ".join(['"{}"'.format(feature) for feature in enabled_features] + ['"-{}"'.format(feature) for feature in disabled_features]), - root = root, - env = env, - sdkroot = repository_ctx.os.environ["SDKROOT"].replace("\\", "/"), - toolchain_type = SWIFT_TOOLCHAIN_TYPE, - xctest_version = xctest_version.stdout.rstrip(), - version_file = version_file, - ), + features = ", ".join(['"{}"'.format(feature) for feature in enabled_features] + ['"-{}"'.format(feature) for feature in disabled_features]), + root = root, + env = env, + sdkroot = repository_ctx.os.environ["SDKROOT"].replace("\\", "/"), + toolchain_type = SWIFT_TOOLCHAIN_TYPE, + xctest_version = xctest_version.stdout.rstrip(), + version_file = version_file, ) def _swift_autoconfiguration_impl(repository_ctx): - # TODO(allevato): This is expedient and fragile. Use the - # platforms/toolchains APIs instead to define proper toolchains, and make it - # possible to support non-Xcode toolchains on macOS as well. - os_name = repository_ctx.os.name.lower() - if os_name.startswith("mac os"): - _create_xcode_toolchain(repository_ctx) - elif os_name.startswith("windows"): - _create_windows_toolchain(repository_ctx) - else: - _create_linux_toolchain(repository_ctx) + repository_ctx.file( + "BUILD", + "\n".join([ + """\ +load( + "@build_bazel_apple_support//configs:platforms.bzl", + "APPLE_PLATFORMS_CONSTRAINTS", +) +load( + "@build_bazel_rules_swift//swift/toolchains:swift_toolchain.bzl", + "swift_toolchain", +) +load( + "@build_bazel_rules_swift//swift/toolchains:xcode_swift_toolchain.bzl", + "xcode_swift_toolchain", +) + +package(default_visibility = ["//visibility:public"]) +""", + _create_xcode_toolchain(), + _create_windows_toolchain(repository_ctx), + _create_linux_toolchain(repository_ctx), + ]), + ) swift_autoconfiguration = repository_rule( environ = ["CC", "PATH", "ProgramData", "Path"], From 4980ef1d8a7e27b61cb2b62eba4341736f43895d Mon Sep 17 00:00:00 2001 From: Aaron Sky Date: Mon, 10 Feb 2025 13:34:56 -0500 Subject: [PATCH 53/92] add `swift.suppress_warnings` feature (#1489) This is something I could use in my codebase. I would like have a way to propagate the `-suppress-warnings` flag to all swift libraries, and specifically, selectively disable it in my REPO.bazel and individual targets. This would allow me to globally suppress warnings for external repositories, without enabling it in my own code, and without needing to juggle the state of the flag setting (`-suppress-warnings` and other diagnostic flags are mutually exclusive afaik). This is the same as how we organize the `swift.warnings_as_errors` feature today. Companion change in apple_support is bazelbuild/apple_support#367 --- swift/internal/feature_names.bzl | 3 +++ swift/toolchains/config/compile_config.bzl | 14 ++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/swift/internal/feature_names.bzl b/swift/internal/feature_names.bzl index 21ae31fde..655324eac 100644 --- a/swift/internal/feature_names.bzl +++ b/swift/internal/feature_names.bzl @@ -374,6 +374,9 @@ SWIFT_FEATURE__SUPPORTS_V6 = "swift._supports_v6" # Enabled by default for Swift 5.10+ on macOS. SWIFT_FEATURE_DISABLE_SWIFT_SANDBOX = "swift.disable_swift_sandbox" +# Pass -warnings-as-errors to the compiler. +SWIFT_FEATURE_SUPPRESS_WARNINGS = "swift.suppress_warnings" + # Pass -warnings-as-errors to the compiler. SWIFT_FEATURE_TREAT_WARNINGS_AS_ERRORS = "swift.treat_warnings_as_errors" diff --git a/swift/toolchains/config/compile_config.bzl b/swift/toolchains/config/compile_config.bzl index 4976752ea..c752ad53b 100644 --- a/swift/toolchains/config/compile_config.bzl +++ b/swift/toolchains/config/compile_config.bzl @@ -71,6 +71,7 @@ load( "SWIFT_FEATURE_OPT_USES_WMO", "SWIFT_FEATURE_REWRITE_GENERATED_HEADER", "SWIFT_FEATURE_SPLIT_DERIVED_FILES_GENERATION", + "SWIFT_FEATURE_SUPPRESS_WARNINGS", "SWIFT_FEATURE_SYSTEM_MODULE", "SWIFT_FEATURE_THIN_LTO", "SWIFT_FEATURE_TREAT_WARNINGS_AS_ERRORS", @@ -414,6 +415,19 @@ def compile_action_configs( features = [SWIFT_FEATURE_ENABLE_TESTING], ), + # Enable suppress-warnings if requested. + ActionConfigInfo( + actions = [ + SWIFT_ACTION_COMPILE, + SWIFT_ACTION_DERIVE_FILES, + SWIFT_ACTION_DUMP_AST, + ], + configurators = [ + add_arg("-suppress-warnings"), + ], + features = [SWIFT_FEATURE_SUPPRESS_WARNINGS], + ), + # Enable warnings-as-errors if requested. ActionConfigInfo( actions = [ From 540ed419f4538b24681c4226c948b5782db876d6 Mon Sep 17 00:00:00 2001 From: Brentley Jones Date: Mon, 10 Feb 2025 13:39:20 -0600 Subject: [PATCH 54/92] Only warn about missing `swiftc` on matching host platform (#1490) Signed-off-by: Brentley Jones --- swift/internal/swift_autoconfiguration.bzl | 39 ++++++++++++++++++---- 1 file changed, 33 insertions(+), 6 deletions(-) diff --git a/swift/internal/swift_autoconfiguration.bzl b/swift/internal/swift_autoconfiguration.bzl index 667f96d81..e6ef58978 100644 --- a/swift/internal/swift_autoconfiguration.bzl +++ b/swift/internal/swift_autoconfiguration.bzl @@ -200,15 +200,18 @@ def _normalized_linux_cpu(cpu): return "x86_64" return cpu -def _create_linux_toolchain(repository_ctx): +def _create_linux_toolchain(*, repository_ctx, warn_on_no_swiftc): """Creates BUILD targets for the Swift toolchain on Linux. Args: repository_ctx: The repository rule context. + warn_on_no_swiftc: If True, print a warning if 'swiftc' is not found in + $PATH. """ path_to_swiftc = repository_ctx.which("swiftc") if not path_to_swiftc: - print("""\ + if warn_on_no_swiftc: + print("""\ No 'swiftc' executable found in $PATH. Not auto-generating a Linux Swift \ toolchain. """) # buildifier: disable=print @@ -311,10 +314,18 @@ def _get_python_bin(repository_ctx): return out return None -def _create_windows_toolchain(repository_ctx): +def _create_windows_toolchain(*, repository_ctx, warn_on_no_swiftc): + """Creates BUILD targets for the Swift toolchain on Linux. + + Args: + repository_ctx: The repository rule context. + warn_on_no_swiftc: If True, print a warning if 'swiftc.exe' is not found + in $PATH. + """ path_to_swiftc = repository_ctx.which("swiftc.exe") if not path_to_swiftc: - print("""\ + if warn_on_no_swiftc: + print("""\ No 'swiftc.exe' executable found in $PATH. Not auto-generating a Windows Swift \ toolchain. """) # buildifier: disable=print @@ -383,6 +394,16 @@ toolchain( ) def _swift_autoconfiguration_impl(repository_ctx): + os_name = repository_ctx.os.name.lower() + is_linux = False + is_windows = False + if os_name.startswith("mac os"): + pass + elif os_name.startswith("windows"): + is_windows = True + else: + is_linux = True + repository_ctx.file( "BUILD", "\n".join([ @@ -403,8 +424,14 @@ load( package(default_visibility = ["//visibility:public"]) """, _create_xcode_toolchain(), - _create_windows_toolchain(repository_ctx), - _create_linux_toolchain(repository_ctx), + _create_windows_toolchain( + repository_ctx = repository_ctx, + warn_on_no_swiftc = is_windows, + ), + _create_linux_toolchain( + repository_ctx = repository_ctx, + warn_on_no_swiftc = is_linux, + ), ]), ) From 83cfb7721442638d7f42af68a348ba7f6b969195 Mon Sep 17 00:00:00 2001 From: Brentley Jones Date: Mon, 10 Feb 2025 14:16:03 -0600 Subject: [PATCH 55/92] Swift 6.0.3 for Linux (#1491) Needed to unblock some upstream changed. --------- Signed-off-by: Brentley Jones --- .bazelci/presubmit.yml | 2 +- .bcr/presubmit.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.bazelci/presubmit.yml b/.bazelci/presubmit.yml index c7ffcd2c9..342e6803b 100644 --- a/.bazelci/presubmit.yml +++ b/.bazelci/presubmit.yml @@ -14,7 +14,7 @@ x_defaults: platform: ubuntu2004 environment: CC: clang - SWIFT_VERSION: "5.10.1" + SWIFT_VERSION: "6.0.3" SWIFT_HOME: "$HOME/swift-$SWIFT_VERSION" PATH: "$SWIFT_HOME/usr/bin:$PATH" linux_common: &linux_common diff --git a/.bcr/presubmit.yml b/.bcr/presubmit.yml index 3b9f38b48..39df58372 100644 --- a/.bcr/presubmit.yml +++ b/.bcr/presubmit.yml @@ -19,7 +19,7 @@ tasks: platform: ${{ platform }} environment: CC: "clang" - SWIFT_VERSION: "5.10.1" + SWIFT_VERSION: "6.0.3" SWIFT_HOME: "$HOME/swift-$SWIFT_VERSION" PATH: "$PATH:$SWIFT_HOME/usr/bin" shell_commands: *shell_commands From b78e082e38f5e0fb87d1707a8ae95b9a603f63ff Mon Sep 17 00:00:00 2001 From: Tony Allevato Date: Tue, 13 Aug 2024 12:45:39 -0700 Subject: [PATCH 56/92] Refactor the test observer to extract xUnit recording from the XCTest observer This is work done in preparation for supporting the swift-testing framework. PiperOrigin-RevId: 662617920 (cherry picked from commit 5aa34d4c66ae24c020022ef22980cb71b7244a5c) Signed-off-by: Brentley Jones --- tools/test_discoverer/ObjcTestPrinter.swift | 90 +++---- .../SymbolGraphTestPrinter.swift | 37 +-- tools/test_discoverer/TestDiscoverer.swift | 33 ++- tools/test_observer/BUILD | 2 + .../test_observer/BazelXMLTestObserver.swift | 153 ++++------- .../BazelXMLTestObserverRegistration.swift | 5 +- tools/test_observer/Locked.swift | 66 +++++ tools/test_observer/XUnitTestRecorder.swift | 244 ++++++++++++++++++ 8 files changed, 451 insertions(+), 179 deletions(-) create mode 100644 tools/test_observer/Locked.swift create mode 100644 tools/test_observer/XUnitTestRecorder.swift diff --git a/tools/test_discoverer/ObjcTestPrinter.swift b/tools/test_discoverer/ObjcTestPrinter.swift index 4236c021a..e2f319aca 100644 --- a/tools/test_discoverer/ObjcTestPrinter.swift +++ b/tools/test_discoverer/ObjcTestPrinter.swift @@ -20,65 +20,52 @@ import Foundation /// on an XCTest bundle but instead is an executable that queries the XCTest framework directly for /// the tests to run. struct ObjcTestPrinter { - /// Prints the main test runner to a Swift source file. - func printTestRunner(toFileAt url: URL) { + /// Returns the Swift source code for the test runner. + func testRunnerSource() -> String { var contents = """ import BazelTestObservation import Foundation import XCTest - @main \(availabilityAttribute) - struct Runner { - static func main() { - loadXCTest() - if let xmlObserver = BazelXMLTestObserver.default { - XCTestObservationCenter.shared.addTestObserver(xmlObserver) - } - do { - var testCollector = try ShardingFilteringTestCollector() - let shardedSuite = testCollector.shard(XCTestSuite.default) - shardedSuite.run() - if let testRun = shardedSuite.testRun { - if testRun.testCaseCount == 0 { - print("ERROR: No tests were executed") - exit(1) - } - exit(testRun.totalFailureCount == 0 ? EXIT_SUCCESS : EXIT_FAILURE) - } - } catch { - print("ERROR: \\(error); exiting.") - exit(1) - } + @MainActor + struct XCTestRunner { + struct Error: Swift.Error, CustomStringConvertible { + let description: String } - } - private func loadXCTest() { - // We weakly linked to XCTest.framework and the Swift support dylib because the machine - // that links the test binary might not be the same that runs it, and they might have Xcode - // installed at different paths. Find the path that Bazel says they're installed at on - // *this* machine and load them. - guard let sdkRoot = ProcessInfo.processInfo.environment["SDKROOT"] else { - print("ERROR: Bazel must set the SDKROOT in order to find XCTest") - exit(1) - } - let sdkRootURL = URL(fileURLWithPath: sdkRoot) - let platformDeveloperPath = sdkRootURL // .../Developer/SDKs/MacOSX.sdk - .deletingLastPathComponent() // .../Developer/SDKs - .deletingLastPathComponent() // .../Developer - let xcTestPath = platformDeveloperPath - .appendingPathComponent("Library/Frameworks/XCTest.framework/XCTest") - .path - guard dlopen(xcTestPath, RTLD_NOW) != nil else { - print("ERROR: dlopen(\\"\\(xcTestPath)\\") failed") - exit(1) + static func run() throws { + try loadXCTest() + XCTestObservationCenter.shared.addTestObserver(BazelXMLTestObserver.default) + var testCollector = try ShardingFilteringTestCollector() + let shardedSuite = testCollector.shard(XCTestSuite.default) + shardedSuite.run() } - let xcTestSwiftSupportPath = platformDeveloperPath - .appendingPathComponent("usr/lib/libXCTestSwiftSupport.dylib") - .path - guard dlopen(xcTestSwiftSupportPath, RTLD_NOW) != nil else { - print("ERROR: dlopen(\\"\\(xcTestSwiftSupportPath)\\") failed") - exit(1) + + private static func loadXCTest() throws { + // We weakly linked to XCTest.framework and the Swift support dylib because the machine + // that links the test binary might not be the same that runs it, and they might have Xcode + // installed at different paths. Find the path that Bazel says they're installed at on + // *this* machine and load them. + guard let sdkRoot = ProcessInfo.processInfo.environment["SDKROOT"] else { + throw Error(description: "ERROR: Bazel must set the SDKROOT in order to find XCTest") + } + let sdkRootURL = URL(fileURLWithPath: sdkRoot) + let platformDeveloperPath = sdkRootURL // .../Developer/SDKs/MacOSX.sdk + .deletingLastPathComponent() // .../Developer/SDKs + .deletingLastPathComponent() // .../Developer + let xcTestPath = platformDeveloperPath + .appendingPathComponent("Library/Frameworks/XCTest.framework/XCTest") + .path + guard dlopen(xcTestPath, RTLD_NOW) != nil else { + throw Error(description: "ERROR: dlopen(\\"\\(xcTestPath)\\") failed") + } + let xcTestSwiftSupportPath = platformDeveloperPath + .appendingPathComponent("usr/lib/libXCTestSwiftSupport.dylib") + .path + guard dlopen(xcTestSwiftSupportPath, RTLD_NOW) != nil else { + throw Error(description: "ERROR: dlopen(\\"\\(xcTestSwiftSupportPath)\\") failed") + } } } @@ -129,7 +116,6 @@ struct ObjcTestPrinter { } """ - - createTextFile(at: url, contents: contents) + return contents } } diff --git a/tools/test_discoverer/SymbolGraphTestPrinter.swift b/tools/test_discoverer/SymbolGraphTestPrinter.swift index 469330433..27b70e5e3 100644 --- a/tools/test_discoverer/SymbolGraphTestPrinter.swift +++ b/tools/test_discoverer/SymbolGraphTestPrinter.swift @@ -119,15 +119,21 @@ struct SymbolGraphTestPrinter { createTextFile(at: url, contents: contents) } - /// Prints the main test runner to a Swift source file. - func printTestRunner(toFileAt url: URL) { + /// Returns the Swift source code for the test runner. + func testRunnerSource() -> String { guard !discoveredTests.modules.isEmpty else { // If no tests were discovered, the user likely wrote non-XCTest-style tests that pass or fail // based on the exit code of the process. Generate an empty source file here, which will be // harmlessly compiled as an empty module, and the user's `main` from their own sources will // be used instead. - createTextFile(at: url, contents: "// No tests discovered; this is intentionally empty.\n") - return + return """ + @MainActor + struct XCTestRunner { + static func run() { + // No XCTest-based tests discovered; this is intentionally empty. + } + } + """ } var contents = """ @@ -135,15 +141,12 @@ struct SymbolGraphTestPrinter { import Foundation import XCTest - @main \(availabilityAttribute) - struct Runner { - static func main() { - if let xmlObserver = BazelXMLTestObserver.default { - XCTestObservationCenter.shared.addTestObserver(xmlObserver) - } - do { - var testCollector = try ShardingFilteringTestCollector() + @MainActor + struct XCTestRunner { + static func run() throws { + XCTestObservationCenter.shared.addTestObserver(BazelXMLTestObserver.default) + var testCollector = try ShardingFilteringTestCollector() """ @@ -158,11 +161,9 @@ struct SymbolGraphTestPrinter { // We don't pass the test filter as an argument because we've already filtered the tests in the // collector; this lets us do better filtering (i.e., regexes) than XCTest itself allows. contents += """ - XCTMain(testCollector.testsToRun) - } catch { - print("ERROR: \\(error); exiting.") - exit(1) - } + // The preferred overload is one that calls `exit`, which we don't want because we have + // post-work to do, so force the one that returns an exit code instead. + let _: CInt = XCTMain(testCollector.testsToRun) } } @@ -219,6 +220,6 @@ struct SymbolGraphTestPrinter { """ - createTextFile(at: url, contents: contents) + return contents } } diff --git a/tools/test_discoverer/TestDiscoverer.swift b/tools/test_discoverer/TestDiscoverer.swift index ee81ce912..01dbcdbd4 100644 --- a/tools/test_discoverer/TestDiscoverer.swift +++ b/tools/test_discoverer/TestDiscoverer.swift @@ -103,11 +103,38 @@ struct TestDiscoverer: ParsableCommand { } } + var contents = """ + import BazelTestObservation + + \(availabilityAttribute) + @main + struct Main { + static func main() async { + do { + try XCTestRunner.run() + + try XUnitTestRecorder.shared.writeXML() + guard !XUnitTestRecorder.shared.hasFailure else { + exit(1) + } + guard XUnitTestRecorder.shared.testCount > 0 else { + print("ERROR: No tests were executed") + exit(1) + } + } catch { + print("Test runner failed with \\(error)") + exit(1) + } + } + } + + """ + let mainFileURL = URL(fileURLWithPath: mainOutput) if objcTestDiscovery { // Print the runner source file, which implements the `@main` type that executes the tests. let testPrinter = ObjcTestPrinter() - testPrinter.printTestRunner(toFileAt: mainFileURL) + contents.append(testPrinter.testRunnerSource()) } else { // For each module, print the list of test entries that were discovered in a source file that // extends that module. @@ -116,7 +143,9 @@ struct TestDiscoverer: ParsableCommand { testPrinter.printTestEntries(forModule: output.moduleName, toFileAt: output.outputURL) } // Print the runner source file, which implements the `@main` type that executes the tests. - testPrinter.printTestRunner(toFileAt: mainFileURL) + contents.append(testPrinter.testRunnerSource()) } + + createTextFile(at: mainFileURL, contents: contents) } } diff --git a/tools/test_observer/BUILD b/tools/test_observer/BUILD index f9ed5451e..a42ab1365 100644 --- a/tools/test_observer/BUILD +++ b/tools/test_observer/BUILD @@ -9,7 +9,9 @@ swift_library( srcs = [ "BazelXMLTestObserver.swift", "BazelXMLTestObserverRegistration.swift", + "Locked.swift", "StringInterpolation+XMLEscaping.swift", + "XUnitTestRecorder.swift", ], module_name = "BazelTestObservation", visibility = ["//visibility:public"], diff --git a/tools/test_observer/BazelXMLTestObserver.swift b/tools/test_observer/BazelXMLTestObserver.swift index 9941b7a7e..b2724e5d0 100644 --- a/tools/test_observer/BazelXMLTestObserver.swift +++ b/tools/test_observer/BazelXMLTestObserver.swift @@ -15,112 +15,28 @@ import Foundation import XCTest -/// An XCTest observer that generates an XML file in the format described by the -/// [JUnit test result schema](https://windyroad.com.au/dl/Open%20Source/JUnit.xsd). +/// An XCTest observer that reports its events to the shared `XUnitTestRecorder`. public final class BazelXMLTestObserver: NSObject { - /// The file handle to which the XML content will be written. - private let fileHandle: FileHandle - - /// The current indentation to print before each line, as UTF-8 code units. - private var indentation: Data - - /// The default XML-generating XCTest observer, which determines the output file based on the - /// value of the `XML_OUTPUT_FILE` environment variable. - /// - /// If the `XML_OUTPUT_FILE` environment variable is not set or the file at that path could not be - /// created and opened for writing, the value of this property will be nil. + /// The default XCTest observer. @MainActor - public static let `default`: BazelXMLTestObserver? = { - guard - let outputPath = ProcessInfo.processInfo.environment["XML_OUTPUT_FILE"], - FileManager.default.createFile(atPath: outputPath, contents: nil, attributes: nil), - let fileHandle = FileHandle(forWritingAtPath: outputPath) - else { - return nil - } - return .init(fileHandle: fileHandle) - }() - - /// Creates a new XML-generating XCTest observer that writes its content to the given file handle. - private init(fileHandle: FileHandle) { - self.fileHandle = fileHandle - self.indentation = Data() - } - - /// Writes the given string to the observer's file handle. - private func writeLine(_ string: S) { - if !indentation.isEmpty { - fileHandle.write(indentation) - } - fileHandle.write(string.data(using: .utf8)!) // Conversion to UTF-8 cannot fail. - fileHandle.write(Data([UInt8(ascii: "\n")])) - } - - /// Increases the current indentation level by two spaces. - private func indent() { - indentation.append(contentsOf: [UInt8(ascii: " "), UInt8(ascii: " ")]) - } + public static let `default`: BazelXMLTestObserver = .init() - /// Reduces the current indentation level by two spaces. - private func dedent() { - indentation.removeLast(2) - } - - /// Canonicalizes the name of the test case for printing into the XML file. - /// - /// The canonical name of the test is `TestClass.testMethod` (i.e., Swift-style syntax). When - /// running tests under the Objective-C runtime, the test cases will have Objective-C-style names - /// (i.e., `-[TestClass testMethod]`), so this method converts those to the desired syntax. - /// - /// Any test name that does not match one of those two syntaxes is returned unchanged. - private func canonicalizedName(of testCase: XCTestCase) -> String { - let name = testCase.name - guard name.hasPrefix("-[") && name.hasSuffix("]") else { - return name - } - - let trimmedName = name.dropFirst(2).dropLast() - guard let spaceIndex = trimmedName.lastIndex(of: " ") else { - return String(trimmedName) - } - - return "\(trimmedName[.."#) - writeLine("") - indent() - } - - public func testBundleDidFinish(_ testBundle: Bundle) { - dedent() - writeLine("") - } - - public func testSuiteWillStart(_ testSuite: XCTestSuite) { - writeLine( - #""#) - indent() - } - - public func testSuiteDidFinish(_ testSuite: XCTestSuite) { - dedent() - writeLine("") - } - public func testCaseWillStart(_ testCase: XCTestCase) { - writeLine( - #""#) - indent() + XUnitTestRecorder.shared.recordTestStarted( + nameComponents: testCase.xUnitNameComponents, + time: SuspendingClock.now) } public func testCaseDidFinish(_ testCase: XCTestCase) { - dedent() - writeLine("") + XUnitTestRecorder.shared.recordTestEnded( + nameComponents: testCase.xUnitNameComponents, + time: SuspendingClock.now) } // On platforms with the Objective-C runtime, we use the richer `XCTIssue`-based APIs. Anywhere @@ -128,17 +44,19 @@ extension BazelXMLTestObserver: XCTestObservation { // `didFailWithDescription` API. #if canImport(ObjectiveC) public func testCase(_ testCase: XCTestCase, didRecord issue: XCTIssue) { - let tag: String + let tag: RecordedIssue.Kind switch issue.type { case .assertionFailure, .performanceRegression, .unmatchedExpectedFailure: - tag = "failure" + tag = .failure case .system, .thrownError, .uncaughtException: - tag = "error" + tag = .error @unknown default: - tag = "failure" + tag = .failure } - writeLine(#"<\#(tag) message="\#(xmlEscaping: issue.compactDescription)"/>"#) + XUnitTestRecorder.shared.recordTestIssue( + nameComponents: testCase.xUnitNameComponents, + issue: .init(kind: tag, reason: issue.compactDescription)) } #else public func testCase( @@ -147,8 +65,10 @@ extension BazelXMLTestObserver: XCTestObservation { inFile filePath: String?, atLine lineNumber: Int ) { - let tag = description.hasPrefix(#"threw error ""#) ? "error" : "failure" - writeLine(#"<\#(tag) message="\#(xmlEscaping: description)"/>"#) + let tag: RecordedIssue.Kind = description.hasPrefix(#"threw error ""#) ? .error : .failure + XUnitTestRecorder.shared.recordTestIssue( + nameComponents: testCase.xUnitNameComponents, + issue: .init(kind: tag, reason: description)) } #endif } @@ -205,7 +125,34 @@ extension BazelXMLTestObserver: XCTestObservation { didRecordSkipWithDescription description: String, sourceCodeContext: XCTSourceCodeContext? ) { - writeLine(#""#) + XUnitTestRecorder.shared.recordTestIssue( + nameComponents: testCase.xUnitNameComponents, + issue: .init(kind: .skipped, reason: description)) } } #endif + +extension XCTestCase { + /// Canonicalizes the name of the test case to the list of string components expected by + /// `XUnitTestRecorder`. + /// + /// The canonical name components of a test are `["TestClass", "testMethod"]`. XCTests run on + /// Linux will be named `TestClass.testMethod` and are split on the dot. Tests run under the + /// Objective-C runtime will have Objective-C-style names (i.e., `-[TestClass testMethod]`), which + /// are likewise converted to the desired form. + var xUnitNameComponents: [String] { + guard name.hasPrefix("-[") && name.hasSuffix("]") else { + return name.split(separator: ".").map(String.init) + } + + let trimmedName = name.dropFirst(2).dropLast() + guard let spaceIndex = trimmedName.lastIndex(of: " ") else { + return String(trimmedName).split(separator: ".").map(String.init) + } + + return [ + String(trimmedName[..: Sendable where Value: Sendable { + private final class _Storage: ManagedBuffer { + deinit { + withUnsafeMutablePointerToElements { lock in + _ = pthread_mutex_destroy(lock) + } + } + } + + private nonisolated(unsafe) var _storage: ManagedBuffer + + /// The value behind the lock. + public var value: Value { + withLock { $0 } + } + + /// Creates a new locked wrapper around the given value. + public init(_ value: Value) { + _storage = _Storage.create(minimumCapacity: 1, makingHeaderWith: { _ in value }) + _storage.withUnsafeMutablePointerToElements { lock in + _ = pthread_mutex_init(lock, nil) + } + } + + /// Runs the given body with the lock held. + /// + /// Upon acquiring the lock, the body is passed a mutable copy of the value, which it has + /// exclusive access to for the duration of the body. Mutations will affect the underlying value. + public nonmutating func withLock( + _ body: (inout Value) throws -> Result + ) rethrows -> Result { + try _storage.withUnsafeMutablePointers { rawValue, lock in + _ = pthread_mutex_lock(lock) + defer { _ = pthread_mutex_unlock(lock) } + return try body(&rawValue.pointee) + } + } +} diff --git a/tools/test_observer/XUnitTestRecorder.swift b/tools/test_observer/XUnitTestRecorder.swift new file mode 100644 index 000000000..2a0197a28 --- /dev/null +++ b/tools/test_observer/XUnitTestRecorder.swift @@ -0,0 +1,244 @@ +// Copyright 2024 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 + +/// An issue that occurred during a test. +public struct RecordedIssue: Sendable { + /// The kind of issue that occurred. + public enum Kind: String, Sendable { + case failure + case error + case skipped + } + + /// The kind of issue that occurred. + public var kind: Kind + + /// A descriptive message that explains the issue. + public var reason: String + + public init(kind: Kind, reason: String) { + self.kind = kind + self.reason = reason + } +} + +/// A recorder for test events that is agnostic to the test framework but emits output using the +/// [xUnit/JUnit test result schema](https://windyroad.com.au/dl/Open%20Source/JUnit.xsd). +/// +/// This recorder treats tests as a tree structure, where tests occur at leaf nodes and those leaves +/// contain timing data and issues recorded during the test. This allows us to support both XCTest +/// tests (where each path through the tree is of length two -- the class name, then the test name) +/// as well as swift-testing (which supports top-level tests outside of suites and also arbitrarily +/// nested suites). +/// +/// In an async-first world, it would make sense to make this an actor instead of using manual +/// locking. However, since both the XCTest and swift-testing frameworks deliver their events in +/// synchronous contexts, it's easier to do things the old-fashioned way. +public final class XUnitTestRecorder { + /// Context that is mutated by the test reader, protected by a lock. + private struct Context: Sendable { + /// The total number of tests that have run. + var testCount: Int = 0 + + /// A tree structure that contains all of the test results. + var testData: TestTree = .init() + + /// Indicates whether any failures have been recorded. + var hasFailure: Bool = false + } + + /// The shared instance of the recorder. + public static let shared = XUnitTestRecorder() + + /// The context that is mutated by the test reader, protected by a lock. + private var context: Locked = Locked(.init()) + + /// Indicates whether any failures have been recorded. + public var hasFailure: Bool { + context.withLock { context in + context.hasFailure + } + } + + /// The total number of tests that have run. + public var testCount: Int { + context.withLock { context in + context.testCount + } + } + + /// Writes the test results to the XML output file dictated by the environment variable passed by + /// Bazel. + public func writeXML() throws { + guard let outputPath = ProcessInfo.processInfo.environment["XML_OUTPUT_FILE"] else { + return + } + let output = context.withLock { context in + var output = #""" + + + + """# + + writeTestTree(context.testData, indentation: " ", to: &output) + + output += #""" + + + """# + return output + } + try output.write(to: URL(fileURLWithPath: outputPath), atomically: true, encoding: .utf8) + } + + /// Recursively writes the information in the test tree to the given string. + private func writeTestTree(_ tree: TestTree, indentation: String, to output: inout String) { + for (name, child) in tree.children { + if let testInfo = child.value { + // This is a test case, so write the appropriate node and all of its issues. + output += #""" + \#(indentation) + + """# + for issue in testInfo.issues { + output += #""" + \#(indentation) <\#(issue.kind.rawValue) message="\#(xmlEscaping: issue.reason)"/> + + """# + } + output += #""" + \#(indentation) + + """# + } else { + // There's no test data, so it must be a suite. Write the structure and then traverse the + // children. + output += #""" + \#(indentation) + + """# + writeTestTree(child, indentation: indentation + " ", to: &output) + output += #""" + \#(indentation) + + """# + } + } + } + + /// Records that a test has started. + public func recordTestStarted(nameComponents: [String], time: any InstantProtocol) { + context.withLock { context in + context.testCount += 1 + context.testData[nameComponents] = .init(startTime: time) + } + } + + /// Records that a test has ended. + public func recordTestEnded(nameComponents: [String], time: any InstantProtocol) { + context.withLock { context in + context.testData[nameComponents]?.endTime = time + } + } + + /// Records that an issue has occurred during a test. + public func recordTestIssue(nameComponents: [String], issue: RecordedIssue) { + context.withLock { context in + context.testData[nameComponents]?.issues.append(issue) + + switch issue.kind { + case .failure, .error: + context.hasFailure = true + default: + break + } + } + } +} + +/// A tree structure that stores information about test suites, tests, and their results. +private struct TestTree: Sendable { + /// The test information for this node, if it represents a test case. + var value: TestInfo? = nil + + /// The child nodes of this node. + var children: [String: TestTree] = [:] + + /// Gets or sets the test information for a node at the given path in the tree. + subscript(keyPath: some Collection) -> TestInfo? { + get { + if let key = keyPath.first { + return children[key]?[keyPath.dropFirst()] + } else { + return self.value + } + } + set { + if let key = keyPath.first { + var child = children[key] ?? TestTree() + child[keyPath.dropFirst()] = newValue + children[key] = child + } else { + self.value = newValue + } + } + } +} + +/// Information about a test case. +private struct TestInfo: Sendable { + /// The time that the test started. + var startTime: any InstantProtocol + + /// The time that the test ended. + var endTime: (any InstantProtocol)? + + /// Issues that were recorded during the test. + var issues: [RecordedIssue] + + /// The duration of the test, in seconds, as a string. + var durationInSeconds: String { + guard let endTime = endTime, let duration = startTime.duration(to: endTime) as? Duration else { + return "" + } + let seconds = + Double(duration.components.seconds) + Double(duration.components.attoseconds) / 1e18 + return String(format: "%.3f", seconds) + } + + /// Creates a new test that started at the given time. + init(startTime: any InstantProtocol) { + self.startTime = startTime + self.endTime = nil + self.issues = [] + } +} + +extension InstantProtocol { + /// Returns the duration between this instant and the given instant. + /// + /// The two instants must be of the same type. + fileprivate func duration(to other: any InstantProtocol) -> Duration { + guard let other = other as? Self else { + preconditionFailure(""" + Internal error: Instant types must be the same, but got \ + \(type(of: self)) and \(type(of: other)) + """) + } + return self.duration(to: other) + } +} From 446b13b219303a53fbbe5ea49e622a4737ea44cd Mon Sep 17 00:00:00 2001 From: Tony Allevato Date: Wed, 14 Aug 2024 08:10:51 -0700 Subject: [PATCH 57/92] Remove `async` from the `main` function for the time being This is causing some of our tests to terminate abnormally with an error out of the Swift runtime: "freed pointer was not the last allocation". We're going to need `async` eventually to support swift-testing, but in the meantime, this fixes the broken projects while I investigate further. PiperOrigin-RevId: 662923954 (cherry picked from commit 9fb1eaaf4118609e1a99d1cb381acbe864ce05a3) Signed-off-by: Brentley Jones --- tools/test_discoverer/TestDiscoverer.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/test_discoverer/TestDiscoverer.swift b/tools/test_discoverer/TestDiscoverer.swift index 01dbcdbd4..0c0666b05 100644 --- a/tools/test_discoverer/TestDiscoverer.swift +++ b/tools/test_discoverer/TestDiscoverer.swift @@ -109,7 +109,7 @@ struct TestDiscoverer: ParsableCommand { \(availabilityAttribute) @main struct Main { - static func main() async { + static func main() { do { try XCTestRunner.run() From 24aa4b5f7ff2966eabbbc7d302c6def461be0e0c Mon Sep 17 00:00:00 2001 From: Tony Allevato Date: Wed, 14 Aug 2024 08:20:49 -0700 Subject: [PATCH 58/92] Delete `BazelXMLTestObserverRegistration` This was used as the principal class when `swift_test` built `MH_BUNDLE`-type binaries, but now that we build standard executables and register the observer manually, it's no longer needed or used. PiperOrigin-RevId: 662926687 (cherry picked from commit 722ec914526baa1d2590cdf18fb645439143c691) Signed-off-by: Brentley Jones --- tools/test_observer/BUILD | 1 - .../BazelXMLTestObserverRegistration.swift | 29 ------------------- 2 files changed, 30 deletions(-) delete mode 100644 tools/test_observer/BazelXMLTestObserverRegistration.swift diff --git a/tools/test_observer/BUILD b/tools/test_observer/BUILD index a42ab1365..97c6a21be 100644 --- a/tools/test_observer/BUILD +++ b/tools/test_observer/BUILD @@ -8,7 +8,6 @@ swift_library( testonly = True, srcs = [ "BazelXMLTestObserver.swift", - "BazelXMLTestObserverRegistration.swift", "Locked.swift", "StringInterpolation+XMLEscaping.swift", "XUnitTestRecorder.swift", diff --git a/tools/test_observer/BazelXMLTestObserverRegistration.swift b/tools/test_observer/BazelXMLTestObserverRegistration.swift deleted file mode 100644 index de3427845..000000000 --- a/tools/test_observer/BazelXMLTestObserverRegistration.swift +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2022 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. - -#if canImport(ObjectiveC) - import Foundation - import XCTest - - /// The principal class in an XCTest bundle on Darwin-based platforms, which registers the - /// XML-generating observer with the XCTest observation center when the bundle is loaded. - @objc(BazelXMLTestObserverRegistration) - @MainActor - public final class BazelXMLTestObserverRegistration: NSObject { - @objc public override init() { - super.init() - XCTestObservationCenter.shared.addTestObserver(BazelXMLTestObserver.default) - } - } -#endif From bcacecd52883caecc50f28a6b3a697861851e8bc Mon Sep 17 00:00:00 2001 From: Tony Allevato Date: Wed, 14 Aug 2024 09:18:45 -0700 Subject: [PATCH 59/92] Fix `async` XCTest execution on Darwin when the runner's `main` is `async` There is what appears to be a strange bug on Darwin platforms here. If the calling context (e.g., `main`) is `async` and there are any `async` tests in the suite to be run, calling `XCTestSuite.run()` will cause the test process to terminate abnormally with the error "freed pointer was not the last allocation" out of the Swift runtime. We "avoid" (work around) this by wrapping the call in a detached task and then awaiting its result. The task must be detached so that we don't block the main actor, which the tests may also be depending on. PiperOrigin-RevId: 662944537 (cherry picked from commit d7f555d1a2a36bce9cc15cb426bd1e15b30897d6) Signed-off-by: Brentley Jones --- tools/test_discoverer/ObjcTestPrinter.swift | 18 ++++++++++++++---- tools/test_discoverer/TestDiscoverer.swift | 4 ++-- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/tools/test_discoverer/ObjcTestPrinter.swift b/tools/test_discoverer/ObjcTestPrinter.swift index e2f319aca..9d36068c4 100644 --- a/tools/test_discoverer/ObjcTestPrinter.swift +++ b/tools/test_discoverer/ObjcTestPrinter.swift @@ -34,12 +34,22 @@ struct ObjcTestPrinter { let description: String } - static func run() throws { + static func run() async throws { try loadXCTest() XCTestObservationCenter.shared.addTestObserver(BazelXMLTestObserver.default) - var testCollector = try ShardingFilteringTestCollector() - let shardedSuite = testCollector.shard(XCTestSuite.default) - shardedSuite.run() + + // There is what appears to be a strange bug on Darwin platforms here. If the calling + // context (e.g., `main`) is `async` and there are any `async` tests in the suite to be + // run, calling `shardedSuite.run()` below will cause the test process to terminate + // abnormally with the error "freed pointer was not the last allocation" out of the Swift + // runtime. We "avoid" (work around) this by wrapping the call in a detached task and then + // awaiting its result. The task must be detached so that we don't block the main actor, + // which the tests may also be depending on. + _ = try await Task.detached { + var testCollector = try ShardingFilteringTestCollector() + let shardedSuite = testCollector.shard(XCTestSuite.default) + shardedSuite.run() + }.value } private static func loadXCTest() throws { diff --git a/tools/test_discoverer/TestDiscoverer.swift b/tools/test_discoverer/TestDiscoverer.swift index 0c0666b05..26c2fc8a6 100644 --- a/tools/test_discoverer/TestDiscoverer.swift +++ b/tools/test_discoverer/TestDiscoverer.swift @@ -109,9 +109,9 @@ struct TestDiscoverer: ParsableCommand { \(availabilityAttribute) @main struct Main { - static func main() { + static func main() async { do { - try XCTestRunner.run() + try await XCTestRunner.run() try XUnitTestRecorder.shared.writeXML() guard !XUnitTestRecorder.shared.hasFailure else { From a89971d3c9fe3a1f7c9a8f2ce19a3767656b2c26 Mon Sep 17 00:00:00 2001 From: Tony Allevato Date: Mon, 19 Aug 2024 05:30:02 -0700 Subject: [PATCH 60/92] Pull the XCTest runner(s) and test sharding/filtering logic out of the generator and just make them regular Swift classes in the observer module There's no compelling reason to generate all of this code; the only thing that actually needs to be generated is the list of tests for Linux, and the small `main` that invokes it. For simplicity, we generate it on Darwin platforms as well, even though it's not strictly necessary because all the discovery there is dynamic. This change also removes the detached `Task` hack that I introduced previously. This worked when all the tests pass (the common case), but if a test failed, XCTest complained that the `recordIssue` method wasn't being called on the main thread. I'll revisit this in a subsequent change to support swift-testing. PiperOrigin-RevId: 664761141 (cherry picked from commit 6d6727ae0ad5805b74b9383d217230fd0c479100) Signed-off-by: Brentley Jones --- tools/test_discoverer/BUILD | 1 - tools/test_discoverer/ObjcTestPrinter.swift | 131 ------------------ .../SymbolGraphTestPrinter.swift | 87 ++---------- tools/test_discoverer/TestDiscoverer.swift | 17 ++- tools/test_discoverer/TestPrinterCommon.swift | 65 --------- tools/test_observer/BUILD | 3 + tools/test_observer/LinuxXCTestRunner.swift | 101 ++++++++++++++ .../ObjectiveCXCTestRunner.swift | 124 +++++++++++++++++ .../ShardingFilteringTestCollector.swift | 116 ++++++++++++++++ 9 files changed, 366 insertions(+), 279 deletions(-) delete mode 100644 tools/test_discoverer/ObjcTestPrinter.swift create mode 100644 tools/test_observer/LinuxXCTestRunner.swift create mode 100644 tools/test_observer/ObjectiveCXCTestRunner.swift create mode 100644 tools/test_observer/ShardingFilteringTestCollector.swift diff --git a/tools/test_discoverer/BUILD b/tools/test_discoverer/BUILD index 309635ecf..e762ad651 100644 --- a/tools/test_discoverer/BUILD +++ b/tools/test_discoverer/BUILD @@ -7,7 +7,6 @@ swift_binary( name = "test_discoverer", srcs = [ "DiscoveredTests.swift", - "ObjcTestPrinter.swift", "SymbolCollector.swift", "SymbolGraphTestPrinter.swift", "SymbolKitExtensions.swift", diff --git a/tools/test_discoverer/ObjcTestPrinter.swift b/tools/test_discoverer/ObjcTestPrinter.swift deleted file mode 100644 index 9d36068c4..000000000 --- a/tools/test_discoverer/ObjcTestPrinter.swift +++ /dev/null @@ -1,131 +0,0 @@ -// Copyright 2024 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 - -/// Prints a test runner as Swift source code to be compiled in order to run the tests. -/// -/// To support Bazel's test sharding protocol and better test filtering, this runner is not based -/// on an XCTest bundle but instead is an executable that queries the XCTest framework directly for -/// the tests to run. -struct ObjcTestPrinter { - /// Returns the Swift source code for the test runner. - func testRunnerSource() -> String { - var contents = """ - import BazelTestObservation - import Foundation - import XCTest - - \(availabilityAttribute) - @MainActor - struct XCTestRunner { - struct Error: Swift.Error, CustomStringConvertible { - let description: String - } - - static func run() async throws { - try loadXCTest() - XCTestObservationCenter.shared.addTestObserver(BazelXMLTestObserver.default) - - // There is what appears to be a strange bug on Darwin platforms here. If the calling - // context (e.g., `main`) is `async` and there are any `async` tests in the suite to be - // run, calling `shardedSuite.run()` below will cause the test process to terminate - // abnormally with the error "freed pointer was not the last allocation" out of the Swift - // runtime. We "avoid" (work around) this by wrapping the call in a detached task and then - // awaiting its result. The task must be detached so that we don't block the main actor, - // which the tests may also be depending on. - _ = try await Task.detached { - var testCollector = try ShardingFilteringTestCollector() - let shardedSuite = testCollector.shard(XCTestSuite.default) - shardedSuite.run() - }.value - } - - private static func loadXCTest() throws { - // We weakly linked to XCTest.framework and the Swift support dylib because the machine - // that links the test binary might not be the same that runs it, and they might have Xcode - // installed at different paths. Find the path that Bazel says they're installed at on - // *this* machine and load them. - guard let sdkRoot = ProcessInfo.processInfo.environment["SDKROOT"] else { - throw Error(description: "ERROR: Bazel must set the SDKROOT in order to find XCTest") - } - let sdkRootURL = URL(fileURLWithPath: sdkRoot) - let platformDeveloperPath = sdkRootURL // .../Developer/SDKs/MacOSX.sdk - .deletingLastPathComponent() // .../Developer/SDKs - .deletingLastPathComponent() // .../Developer - let xcTestPath = platformDeveloperPath - .appendingPathComponent("Library/Frameworks/XCTest.framework/XCTest") - .path - guard dlopen(xcTestPath, RTLD_NOW) != nil else { - throw Error(description: "ERROR: dlopen(\\"\\(xcTestPath)\\") failed") - } - let xcTestSwiftSupportPath = platformDeveloperPath - .appendingPathComponent("usr/lib/libXCTestSwiftSupport.dylib") - .path - guard dlopen(xcTestSwiftSupportPath, RTLD_NOW) != nil else { - throw Error(description: "ERROR: dlopen(\\"\\(xcTestSwiftSupportPath)\\") failed") - } - } - } - - """ - contents += createShardingFilteringTestCollector() - contents += """ - extension ShardingFilteringTestCollector { - mutating func shard(_ suite: XCTestSuite) -> XCTestSuite { - guard shardCount != 0 || filter != nil else { - // If we're not sharding or filtering, just return the original suite. - return suite - } - - // Create an empty suite with the same name. We'll recurse through the original suite, - // retaining any nested suite structures but only adding 1 of every `shard_count` tests - // we encounter. - let shardedSuite = XCTestSuite(name: suite.name) - for test in suite.tests { - switch test { - case let childSuite as XCTestSuite: - shardedSuite.addTest(shard(childSuite)) - default: - guard isIncludedByFilter(nameForFiltering(of: test)) else { - break - } - if isIncludedInShard() { - shardedSuite.addTest(test) - } - seenTestCount += 1 - } - } - return shardedSuite - } - - private func nameForFiltering(of test: XCTest) -> String { - let name = test.name - guard name.hasPrefix("-[") && name.hasSuffix("]") else { - return name - } - - let trimmedName = name.dropFirst(2).dropLast() - guard let spaceIndex = trimmedName.lastIndex(of: " ") else { - return String(trimmedName) - } - - return "\\(trimmedName[..( - _ suiteName: String, - _ tests: [(String, (T) -> () -> Void)] - ) { - guard shardCount != 0 || filter != nil else { - // If we're not sharding or filtering, just add all the tests. - testsToRun.append(testCase(tests)) - return - } - var shardTests: [(String, (T) -> () -> Void)] = [] - for test in tests { - guard isIncludedByFilter("\\(suiteName)/\\(test.0)") else { - continue - } - if isIncludedInShard() { - shardTests.append(test) - } - seenTestCount += 1 - } - testsToRun.append(testCase(shardTests)) - } - - mutating func addTests( - _ suiteName: String, - _ tests: [(String, (T) -> () throws -> Void)] - ) { - guard shardCount != 0 || filter != nil else { - // If we're not sharding or filtering, just add all the tests. - testsToRun.append(testCase(tests)) - return - } - var shardTests: [(String, (T) -> () throws -> Void)] = [] - for test in tests { - guard isIncludedByFilter("\\(suiteName)/\\(test.0)") else { - continue - } - if isIncludedInShard() { - shardTests.append(test) - } - seenTestCount += 1 - } - testsToRun.append(testCase(shardTests)) - } - } + return allTests + }() """ diff --git a/tools/test_discoverer/TestDiscoverer.swift b/tools/test_discoverer/TestDiscoverer.swift index 26c2fc8a6..10a5d7601 100644 --- a/tools/test_discoverer/TestDiscoverer.swift +++ b/tools/test_discoverer/TestDiscoverer.swift @@ -105,13 +105,15 @@ struct TestDiscoverer: ParsableCommand { var contents = """ import BazelTestObservation + import Foundation + import XCTest \(availabilityAttribute) @main struct Main { - static func main() async { + static func main() { do { - try await XCTestRunner.run() + try XCTestRunner.run(__allDiscoveredXCTests) try XUnitTestRecorder.shared.writeXML() guard !XUnitTestRecorder.shared.hasFailure else { @@ -132,9 +134,14 @@ struct TestDiscoverer: ParsableCommand { let mainFileURL = URL(fileURLWithPath: mainOutput) if objcTestDiscovery { - // Print the runner source file, which implements the `@main` type that executes the tests. - let testPrinter = ObjcTestPrinter() - contents.append(testPrinter.testRunnerSource()) + // On Darwin platforms, tests are discovered by the Objective-C runtime, so we don't need to + // generate anything. We use a dummy parameter to keep the call site the same on both + // platforms. + contents.append(""" + // Unused by the Objective-C XCTestRunner; tests are discovered by the runtime. + private let __allDiscoveredXCTests: () = () + + """) } else { // For each module, print the list of test entries that were discovered in a source file that // extends that module. diff --git a/tools/test_discoverer/TestPrinterCommon.swift b/tools/test_discoverer/TestPrinterCommon.swift index 3b76b79cf..41c7e1283 100644 --- a/tools/test_discoverer/TestPrinterCommon.swift +++ b/tools/test_discoverer/TestPrinterCommon.swift @@ -23,68 +23,3 @@ let availabilityAttribute = """ func createTextFile(at url: URL, contents: String) { FileManager.default.createFile(atPath: url.path, contents: contents.data(using: .utf8)!) } - -/// The parts of the `ShardingAwareTestCollector` type that are common to both the Objective-C and -/// symbol-graph-based implementations. Those generators then should add extensions to provide -/// runner-specific logic. -func createShardingFilteringTestCollector(extraProperties: String = "") -> String { - return """ - struct ShardingFilteringTestCollector { - struct Error: Swift.Error, CustomStringConvertible { - var message: String - var description: String { message } - } - - private var shardCount: Int - private var shardIndex: Int - private var seenTestCount: Int - private var filter: Regex? - \(extraProperties) - - init() throws { - // Bazel requires us to write out an empty file at this path to tell it that we support - // sharding. - if let statusPath = ProcessInfo.processInfo.environment["TEST_SHARD_STATUS_FILE"] { - guard FileManager.default.createFile(atPath: statusPath, contents: nil, attributes: nil) - else { - throw Error(message: "Could not create TEST_SHARD_STATUS_FILE (\\(statusPath))") - } - } - - self.shardCount = - ProcessInfo.processInfo.environment["TEST_TOTAL_SHARDS"].flatMap { Int($0, radix: 10) } ?? 0 - self.shardIndex = - ProcessInfo.processInfo.environment["TEST_SHARD_INDEX"].flatMap { Int($0, radix: 10) } ?? 0 - self.seenTestCount = 0 - - guard (shardCount == 0 && shardIndex == 0) || (shardCount > 0 && shardIndex < shardCount) - else { - throw Error( - message: "Invalid shard count (\\(shardCount)) and shard index (\\(shardIndex))") - } - if let filterString = ProcessInfo.processInfo.environment["TESTBRIDGE_TEST_ONLY"] { - guard let maybeFilter = try? Regex(filterString) else { - throw Error(message: "Could not parse '--test_filter' string as a regular expression") - } - filter = maybeFilter - } else { - filter = nil - } - } - - private func isIncludedByFilter(_ testName: String) -> Bool { - guard let filter = self.filter else { return true } - do { - return try filter.firstMatch(in: testName) != nil - } catch { - return false - } - } - - private func isIncludedInShard() -> Bool { - return shardCount == 0 || seenTestCount % shardCount == shardIndex - } - } - - """ -} diff --git a/tools/test_observer/BUILD b/tools/test_observer/BUILD index 97c6a21be..6dd7084f5 100644 --- a/tools/test_observer/BUILD +++ b/tools/test_observer/BUILD @@ -8,7 +8,10 @@ swift_library( testonly = True, srcs = [ "BazelXMLTestObserver.swift", + "LinuxXCTestRunner.swift", "Locked.swift", + "ObjectiveCXCTestRunner.swift", + "ShardingFilteringTestCollector.swift", "StringInterpolation+XMLEscaping.swift", "XUnitTestRecorder.swift", ], diff --git a/tools/test_observer/LinuxXCTestRunner.swift b/tools/test_observer/LinuxXCTestRunner.swift new file mode 100644 index 000000000..61bad52d1 --- /dev/null +++ b/tools/test_observer/LinuxXCTestRunner.swift @@ -0,0 +1,101 @@ +// Copyright 2024 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. + +#if os(Linux) + import Foundation + import XCTest + + @available( + *, deprecated, + message: """ + Not actually deprecated. Marked as deprecated to allow inclusion of deprecated tests (which \ + test deprecated functionality) without warnings. + """ + ) + public typealias XCTestRunner = LinuxXCTestRunner + + @available( + *, deprecated, + message: """ + Not actually deprecated. Marked as deprecated to allow inclusion of deprecated tests (which \ + test deprecated functionality) without warnings. + """ + ) + @MainActor + public enum LinuxXCTestRunner { + /// A wrapper around a single test from an `XCTestCaseEntry` used by the test collector. + private struct Test: Testable { + /// The type of the `XCTestCase` that contains the test. + var testCaseClass: XCTestCase.Type + + /// The name of the test and the closure that runs it. + var xcTest: (String, XCTestCaseClosure) + + var testIdentifier: String { + "\(String(describing: testCaseClass))/\(xcTest.0)" + } + } + + /// A thin wrapper around a `XCTestCase` metatype that allows it to be used as a key in a + /// dictionary. + private struct TestCaseType: Equatable, Hashable { + var testCaseClass: XCTestCase.Type + + init(_ testCaseClass: XCTestCase.Type) { + self.testCaseClass = testCaseClass + } + + static func == (lhs: TestCaseType, rhs: TestCaseType) -> Bool { + return lhs.testCaseClass == rhs.testCaseClass + } + + func hash(into hasher: inout Hasher) { + return hasher.combine(ObjectIdentifier(testCaseClass)) + } + } + + public static func run(_ testCaseEntries: [XCTestCaseEntry]) throws { + XCTestObservationCenter.shared.addTestObserver(BazelXMLTestObserver.default) + // The preferred overload normally chosen by the compiler is one that calls `exit`, which we + // don't want because we have post-work to do, so force the one that returns an exit code + // instead (which we ignore). + let _: CInt = XCTMain(try shard(testCaseEntries)) + } + + /// Returns a filtered and sharded copy of the given list of test case entries. + private static func shard(_ testCaseEntries: [XCTestCaseEntry]) throws -> [XCTestCaseEntry] { + var collector = try ShardingFilteringTestCollector() + guard collector.willShardOrFilter else { + return testCaseEntries + } + + for testCaseEntry in testCaseEntries { + for test in testCaseEntry.allTests { + collector.addTest(Test(testCaseClass: testCaseEntry.testCaseClass, xcTest: test)) + } + } + + // Group the sharded tests back into buckets based on their test case class. + var shardedEntryMap: [TestCaseType: [(String, XCTestCaseClosure)]] = [:] + for shardedTest in collector.testsInCurrentShard { + shardedEntryMap[TestCaseType(shardedTest.testCaseClass), default: []] + .append(shardedTest.xcTest) + } + return + shardedEntryMap + .map { ($0.testCaseClass, $1) } + .sorted { String(describing: $0.0) < String(describing: $1.0) } + } + } +#endif diff --git a/tools/test_observer/ObjectiveCXCTestRunner.swift b/tools/test_observer/ObjectiveCXCTestRunner.swift new file mode 100644 index 000000000..16b3a130d --- /dev/null +++ b/tools/test_observer/ObjectiveCXCTestRunner.swift @@ -0,0 +1,124 @@ +// Copyright 2024 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. + +#if canImport(ObjectiveC) + import Foundation + import XCTest + + @available(*, deprecated, message: """ + Not actually deprecated. Marked as deprecated to allow inclusion of deprecated tests (which \ + test deprecated functionality) without warnings. + """) + public typealias XCTestRunner = ObjectiveCXCTestRunner + + @available(*, deprecated, message: """ + Not actually deprecated. Marked as deprecated to allow inclusion of deprecated tests (which \ + test deprecated functionality) without warnings. + """) + @MainActor + public enum ObjectiveCXCTestRunner { + struct Error: Swift.Error, CustomStringConvertible { + let description: String + } + + /// A wrapper around an `XCTestCase` used by the test collector. + struct Test: Testable { + /// The underlying `XCTestCase` that this wrapper represents. + private(set) var xcTest: XCTest + + var testIdentifier: String { + let name = xcTest.name + guard name.hasPrefix("-[") && name.hasSuffix("]") else { + // If it's not an Objective-C method name, just return it verbatim. + return name + } + // Split the class name from the method name and then re-join them using the slash form that + // the test filter expects. + let trimmedName = name.dropFirst(2).dropLast() + guard let spaceIndex = trimmedName.lastIndex(of: " ") else { + return String(trimmedName) + } + return "\(trimmedName[.. XCTestSuite { + var testCollector = try ShardingFilteringTestCollector() + guard testCollector.willShardOrFilter else { + // If we're not sharding or filtering, just return the original suite. + return suite + } + + let shardedSuite = XCTestSuite(name: suite.name) + shard(suite, into: &testCollector) + for test in testCollector.testsInCurrentShard { + shardedSuite.addTest(test.xcTest) + } + return shardedSuite + } + + private static func shard( + _ suite: XCTestSuite, + into collector: inout ShardingFilteringTestCollector + ) { + // Create an empty suite with the same name. We'll recurse through the original suite, + // retaining any nested suite structures but only adding 1 of every `shard_count` tests + // we encounter. + for test in suite.tests { + switch test { + case let childSuite as XCTestSuite: + shard(childSuite, into: &collector) + default: + collector.addTest(Test(xcTest: test)) + } + } + } + } +#endif diff --git a/tools/test_observer/ShardingFilteringTestCollector.swift b/tools/test_observer/ShardingFilteringTestCollector.swift new file mode 100644 index 000000000..47fc51528 --- /dev/null +++ b/tools/test_observer/ShardingFilteringTestCollector.swift @@ -0,0 +1,116 @@ +// Copyright 2024 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 + +/// Types representing tests that can be processed by the test collector should implement this +/// protocol to provide the test identifier that will be checked against the `--test_filter` +/// regular expression. +protocol Testable { + /// The unique identifier for this test, which will be checked against the `--test_filter` + /// regular expression. + var testIdentifier: String { get } +} + +/// A test collector that filters out tests that should not be run in the current shard and also +/// filters out tests that do not match the `--test_filter` regular expression. +struct ShardingFilteringTestCollector { + struct Error: Swift.Error, CustomStringConvertible { + var message: String + var description: String { message } + } + + private(set) var testsInCurrentShard: [Test] + + private var shardCount: Int + private var shardIndex: Int + private var seenTestCount: Int + private var filter: Regex? + + /// Indicates whether the next test added to the collector should be included in the current + /// shard. + private var isCurrentShard: Bool { + return shardCount == 0 || seenTestCount % shardCount == shardIndex + } + + /// Indicates whether sharding or filtering was requested. + /// + /// This property can be used as a fast-path to avoid walking the test hierarchy if no + /// sharding or filtering is requested. + var willShardOrFilter: Bool { + shardCount != 0 || filter != nil + } + + /// Creates a new test collector. + /// + /// - Throws: If the environment variables indicating sharding and/or filtering are invalid. + init() throws { + // Bazel requires us to write out an empty file at this path to tell it that we support + // sharding. + let environment = ProcessInfo.processInfo.environment + if let statusPath = environment["TEST_SHARD_STATUS_FILE"] { + guard FileManager.default.createFile(atPath: statusPath, contents: nil, attributes: nil) + else { + throw Error(message: "Could not create TEST_SHARD_STATUS_FILE (\(statusPath))") + } + } + + self.shardCount = environment["TEST_TOTAL_SHARDS"].flatMap { Int($0, radix: 10) } ?? 0 + self.shardIndex = environment["TEST_SHARD_INDEX"].flatMap { Int($0, radix: 10) } ?? 0 + self.seenTestCount = 0 + self.testsInCurrentShard = [] + + guard (shardCount == 0 && shardIndex == 0) || (shardCount > 0 && shardIndex < shardCount) + else { + throw Error( + message: "Invalid shard count (\(shardCount)) and shard index (\(shardIndex))") + } + if let filterString = environment["TESTBRIDGE_TEST_ONLY"] { + guard let maybeFilter = try? Regex(filterString) else { + throw Error( + message: """ + Could not parse '--test_filter' string as a regular expression: \(filterString) + """) + } + self.filter = maybeFilter + } else { + self.filter = nil + } + } + + /// Adds a test to the collector. + /// + /// If the test does not match the `--test_filter` regular expression, it will be ignored. If it + /// belongs in the current shard, it will be added to the list of tests to run in that shard. + mutating func addTest(_ test: Test) { + guard isIncludedByFilter(test.testIdentifier) else { + // Tests that are filtered out do not advance the shard. + return + } + if isCurrentShard { + self.testsInCurrentShard.append(test) + } + self.seenTestCount += 1 + } + + /// Returns `true` if the given test identifier matches the `--test_filter` regular expression. + private func isIncludedByFilter(_ testIdentifier: String) -> Bool { + guard let filter = self.filter else { return true } + do { + return try filter.firstMatch(in: testIdentifier) != nil + } catch { + return false + } + } +} From f90d13e2a220d0342512009412afbb334b1c17a7 Mon Sep 17 00:00:00 2001 From: Tony Allevato Date: Mon, 19 Aug 2024 05:40:27 -0700 Subject: [PATCH 61/92] Make the test_observer compatible with Swift 6 language mode PiperOrigin-RevId: 664764173 (cherry picked from commit 84c67e5437ede471ee4e746c6c627c556ce5e6e4) Signed-off-by: Brentley Jones --- tools/test_observer/Locked.swift | 8 +++++++- tools/test_observer/XUnitTestRecorder.swift | 8 ++++---- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/tools/test_observer/Locked.swift b/tools/test_observer/Locked.swift index dfdc3bfbd..baf968492 100644 --- a/tools/test_observer/Locked.swift +++ b/tools/test_observer/Locked.swift @@ -35,7 +35,13 @@ public struct Locked: Sendable where Value: Sendable { } } - private nonisolated(unsafe) var _storage: ManagedBuffer + // Swift 6 requires this to be declared as `nonisolated(unsafe)`, but older compilers emit a + // warning claiming (incorrectly) that it's redundant. + #if compiler(>=6) + private nonisolated(unsafe) var _storage: ManagedBuffer + #else + private var _storage: ManagedBuffer + #endif /// The value behind the lock. public var value: Value { diff --git a/tools/test_observer/XUnitTestRecorder.swift b/tools/test_observer/XUnitTestRecorder.swift index 2a0197a28..2a66b20a3 100644 --- a/tools/test_observer/XUnitTestRecorder.swift +++ b/tools/test_observer/XUnitTestRecorder.swift @@ -45,9 +45,9 @@ public struct RecordedIssue: Sendable { /// nested suites). /// /// In an async-first world, it would make sense to make this an actor instead of using manual -/// locking. However, since both the XCTest and swift-testing frameworks deliver their events in -/// synchronous contexts, it's easier to do things the old-fashioned way. -public final class XUnitTestRecorder { +/// locking. However, since XCTest delivers its events in a synchronous context, it's easier to use +/// old-fashioned locking. +public final class XUnitTestRecorder: Sendable { /// Context that is mutated by the test reader, protected by a lock. private struct Context: Sendable { /// The total number of tests that have run. @@ -64,7 +64,7 @@ public final class XUnitTestRecorder { public static let shared = XUnitTestRecorder() /// The context that is mutated by the test reader, protected by a lock. - private var context: Locked = Locked(.init()) + private let context: Locked = Locked(.init()) /// Indicates whether any failures have been recorded. public var hasFailure: Bool { From afd69f98095a6330da045bb4e05ca568865ddd3e Mon Sep 17 00:00:00 2001 From: Tony Allevato Date: Tue, 20 Aug 2024 05:09:26 -0700 Subject: [PATCH 62/92] Add swift-testing support to `swift_test` Test discovery and execution is handled through the v0 JSON ABI entry point provided by the swift-testing framework. This requires version 0.11.0 or higher of the package (or Xcode 16 beta 5 or higher). PiperOrigin-RevId: 665308132 (cherry picked from commit 26e26241bd118776af214c6f9ea66b918eac87f6) Signed-off-by: Brentley Jones --- swift/toolchains/xcode_swift_toolchain.bzl | 10 +- tools/test_discoverer/TestDiscoverer.swift | 33 +- tools/test_observer/BUILD | 3 + tools/test_observer/JSON.swift | 211 ++++++++++++ tools/test_observer/LinuxXCTestRunner.swift | 18 +- .../ObjectiveCXCTestRunner.swift | 47 +-- tools/test_observer/RuntimeLibraries.swift | 85 +++++ tools/test_observer/SwiftTestingRunner.swift | 320 ++++++++++++++++++ 8 files changed, 664 insertions(+), 63 deletions(-) create mode 100644 tools/test_observer/JSON.swift create mode 100644 tools/test_observer/RuntimeLibraries.swift create mode 100644 tools/test_observer/SwiftTestingRunner.swift diff --git a/swift/toolchains/xcode_swift_toolchain.bzl b/swift/toolchains/xcode_swift_toolchain.bzl index e05a90358..9ca530def 100644 --- a/swift/toolchains/xcode_swift_toolchain.bzl +++ b/swift/toolchains/xcode_swift_toolchain.bzl @@ -203,7 +203,11 @@ def _swift_linkopts_cc_info( ), ) -def _test_linking_context(apple_toolchain, target_triple, toolchain_label): +def _test_linking_context( + apple_toolchain, + target_triple, + toolchain_label, + xcode_config): """Returns a `CcLinkingContext` containing linker flags for test binaries. Args: @@ -211,6 +215,7 @@ def _test_linking_context(apple_toolchain, target_triple, toolchain_label): target_triple: The target triple `struct`. 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. Returns: A `CcLinkingContext` that will provide linker flags to `swift_test` @@ -229,6 +234,8 @@ def _test_linking_context(apple_toolchain, target_triple, toolchain_label): "-Wl,-weak_framework,XCTest", "-Wl,-weak-lXCTestSwiftSupport", ] + if _is_xcode_at_least_version(xcode_config, "16.0"): + linkopts.append("-Wl,-weak_framework,Testing") if platform_developer_framework_dir: linkopts.extend([ @@ -619,6 +626,7 @@ def _xcode_swift_toolchain_impl(ctx): apple_toolchain = apple_toolchain, target_triple = target_triple, toolchain_label = ctx.label, + xcode_config = xcode_config, ) # `--define=SWIFT_USE_TOOLCHAIN_ROOT=` is a rapid development feature diff --git a/tools/test_discoverer/TestDiscoverer.swift b/tools/test_discoverer/TestDiscoverer.swift index 10a5d7601..db50abe2a 100644 --- a/tools/test_discoverer/TestDiscoverer.swift +++ b/tools/test_discoverer/TestDiscoverer.swift @@ -103,6 +103,10 @@ struct TestDiscoverer: ParsableCommand { } } + // These shenanigans are necessary because `XCTestSuite.default.run()` doesn't like to be called + // from an `async main()` (it crashes in the runtime's stack allocator if it tries to run an + // async test method), but we have to do async work to run swift-testing tests. See + // https://forums.swift.org/t/74010 for additional context. var contents = """ import BazelTestObservation import Foundation @@ -112,21 +116,38 @@ struct TestDiscoverer: ParsableCommand { @main struct Main { static func main() { + do { + try loadTestingLibraries() + } catch { + print("Fatal error loading runtime libraries: \\(error)") + exit(1) + } do { try XCTestRunner.run(__allDiscoveredXCTests) - - try XUnitTestRecorder.shared.writeXML() - guard !XUnitTestRecorder.shared.hasFailure else { + } catch { + print("Fatal error running XCTest tests: \\(error)") + exit(1) + } + Task { + do { + try await SwiftTestingRunner.run() + } catch { + print("Fatal error running swift-testing tests: \\(error)") + exit(1) + } + do { + try XUnitTestRecorder.shared.writeXML() + } catch { + print("Fatal error writing test results to XML: \\(error)") exit(1) } guard XUnitTestRecorder.shared.testCount > 0 else { print("ERROR: No tests were executed") exit(1) } - } catch { - print("Test runner failed with \\(error)") - exit(1) + exit(XUnitTestRecorder.shared.hasFailure ? 1 : 0) } + _asyncMainDrainQueue() } } diff --git a/tools/test_observer/BUILD b/tools/test_observer/BUILD index 6dd7084f5..199f3b3f3 100644 --- a/tools/test_observer/BUILD +++ b/tools/test_observer/BUILD @@ -8,11 +8,14 @@ swift_library( testonly = True, srcs = [ "BazelXMLTestObserver.swift", + "JSON.swift", "LinuxXCTestRunner.swift", "Locked.swift", "ObjectiveCXCTestRunner.swift", + "RuntimeLibraries.swift", "ShardingFilteringTestCollector.swift", "StringInterpolation+XMLEscaping.swift", + "SwiftTestingRunner.swift", "XUnitTestRecorder.swift", ], module_name = "BazelTestObservation", diff --git a/tools/test_observer/JSON.swift b/tools/test_observer/JSON.swift new file mode 100644 index 000000000..d2fc5f6cb --- /dev/null +++ b/tools/test_observer/JSON.swift @@ -0,0 +1,211 @@ +// Copyright 2024 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 + +/// A lightweight `Codable` JSON type. +public enum JSON: Sendable { + case null + case bool(Bool) + case number(Number) + case string(String) + case array([JSON]) + case object([String: JSON]) + + public static func number(_ value: Int) -> JSON { + .number(Number(value)) + } + + public static func number(_ value: Double) -> JSON { + .number(Number(value)) + } +} + +/// A wrapper around `NSNumber` that is `Sendable` and simplifies other interactions. +/// +/// The only way to represent 64-bit integers without loss of precision in Foundation's JSON +/// `Codable` implementations is to use `NSNumber` as the encoded type. +public struct Number: @unchecked Sendable { + /// The underlying `NSNumber` that wraps the numeric value. + private let value: NSNumber + + /// The `Int` value of the receiver. + public var intValue: Int { + return value.intValue + } + + /// The `Double` value of the receiver. + public var doubleValue: Double { + return value.doubleValue + } + + /// Creates a new `Number` from the given integer. + public init(_ value: Int) { + self.value = NSNumber(value: value) + } + + /// Creates a new `Number` from the given floating-point value. + public init(_ value: Double) { + self.value = NSNumber(value: value) + } +} + +extension JSON { + /// Creates a new JSON value by decoding the given UTF-8-encoded JSON string represented as + /// `Data`. + public init(byDecoding data: Data) throws { + self = try JSONDecoder().decode(JSON.self, from: data) + } + + /// A `Data` representing the UTF-8-encoded JSON string value of the receiver. + public var encodedData: Data { + get throws { + return try JSONEncoder().encode(self) + } + } +} + +extension JSON: ExpressibleByNilLiteral { + public init(nilLiteral: ()) { + self = .null + } +} + +extension JSON: ExpressibleByBooleanLiteral { + public init(booleanLiteral value: Bool) { + self = .bool(value) + } +} + +extension JSON: ExpressibleByFloatLiteral { + public init(floatLiteral value: Double) { + self = .number(value) + } +} + +extension JSON: ExpressibleByIntegerLiteral { + public init(integerLiteral value: Int) { + self = .number(value) + } +} + +extension JSON: ExpressibleByStringLiteral { + public init(stringLiteral value: String) { + self = .string(value) + } +} + +extension JSON: ExpressibleByArrayLiteral { + public init(arrayLiteral elements: JSON...) { + self = .array(elements) + } +} + +extension JSON: ExpressibleByDictionaryLiteral { + public init(dictionaryLiteral elements: (String, JSON)...) { + self = .object(.init(uniqueKeysWithValues: elements)) + } +} + +extension JSON: Codable { + public init(from decoder: Decoder) throws { + let container = try decoder.singleValueContainer() + guard !container.decodeNil() else { + self = .null + return + } + + if let bool = try container.decode(ifValueIs: Bool.self) { + self = .bool(bool) + } else if let number = try container.decode(ifValueIs: Double.self) { + // In what appears to be a bug in Foundation, perfectly legitimate floating point values + // (e.g., 348956.52160425) are failing to decode through `NSNumber`. But we have to use + // `NSNumber` to handle 64-bit integers, like the `Int.min` that swift-testing requires for + // the verbosity level to avoid printing anything to stdout when listing tests. To deal with + // both cases, we try to decode as a `Double` first, and if that fails, we try to decode as an + // `NSNumber`. + self = .number(Number(number)) + } else if let number = try container.decode(ifValueIs: Number.self) { + self = .number(number) + } else if let string = try container.decode(ifValueIs: String.self) { + self = .string(string) + } else if let array = try container.decode(ifValueIs: [JSON].self) { + self = .array(array) + } else if let object = try container.decode(ifValueIs: [String: JSON].self) { + self = .object(object) + } else { + throw DecodingError.typeMismatch( + JSON.self, + DecodingError.Context( + codingPath: decoder.codingPath, + debugDescription: "unable to decode as a supported JSON type")) + } + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + switch self { + case .null: + try container.encodeNil() + case .bool(let bool): + try container.encode(bool) + case .number(let number): + try container.encode(number) + case .string(let string): + try container.encode(string) + case .array(let array): + try container.encode(array) + case .object(let object): + try container.encode(object) + } + } +} + +extension Number: Codable { + public init(from decoder: Decoder) throws { + let container = try decoder.singleValueContainer() + if let int = try container.decode(ifValueIs: Int.self) { + self = .init(int) + } else if let double = try container.decode(ifValueIs: Double.self) { + self = .init(double) + } else { + throw DecodingError.typeMismatch( + JSON.self, + DecodingError.Context( + codingPath: decoder.codingPath, + debugDescription: "unable to decode as a supported number type")) + } + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + if value.objCType.pointee == UInt8(ascii: "d") { + try container.encode(value.doubleValue) + } else { + try container.encode(value.intValue) + } + } +} + +extension SingleValueDecodingContainer { + /// Decodes a value of the given type if the value in the container is of the same type, or + /// returns nil if the value is of a different type. + fileprivate func decode(ifValueIs type: T.Type) throws -> T? { + do { + return try self.decode(type) + } catch DecodingError.typeMismatch { + return nil + } + } +} diff --git a/tools/test_observer/LinuxXCTestRunner.swift b/tools/test_observer/LinuxXCTestRunner.swift index 61bad52d1..0ffd9a5b1 100644 --- a/tools/test_observer/LinuxXCTestRunner.swift +++ b/tools/test_observer/LinuxXCTestRunner.swift @@ -16,22 +16,12 @@ import Foundation import XCTest - @available( - *, deprecated, - message: """ - Not actually deprecated. Marked as deprecated to allow inclusion of deprecated tests (which \ - test deprecated functionality) without warnings. - """ - ) public typealias XCTestRunner = LinuxXCTestRunner - @available( - *, deprecated, - message: """ - Not actually deprecated. Marked as deprecated to allow inclusion of deprecated tests (which \ - test deprecated functionality) without warnings. - """ - ) + /// A test runner for tests that use the XCTest framework on Linux. + /// + /// This test runner uses test case entries that were constructed by scanning the symbol graph + /// output of the compiler. @MainActor public enum LinuxXCTestRunner { /// A wrapper around a single test from an `XCTestCaseEntry` used by the test collector. diff --git a/tools/test_observer/ObjectiveCXCTestRunner.swift b/tools/test_observer/ObjectiveCXCTestRunner.swift index 16b3a130d..670a5340a 100644 --- a/tools/test_observer/ObjectiveCXCTestRunner.swift +++ b/tools/test_observer/ObjectiveCXCTestRunner.swift @@ -16,22 +16,13 @@ import Foundation import XCTest - @available(*, deprecated, message: """ - Not actually deprecated. Marked as deprecated to allow inclusion of deprecated tests (which \ - test deprecated functionality) without warnings. - """) public typealias XCTestRunner = ObjectiveCXCTestRunner - @available(*, deprecated, message: """ - Not actually deprecated. Marked as deprecated to allow inclusion of deprecated tests (which \ - test deprecated functionality) without warnings. - """) + /// A test runner for tests that use the XCTest framework on Apple platforms. + /// + /// This test runner uses the Objective-C runtime to discover the tests to run. @MainActor public enum ObjectiveCXCTestRunner { - struct Error: Swift.Error, CustomStringConvertible { - let description: String - } - /// A wrapper around an `XCTestCase` used by the test collector. struct Test: Testable { /// The underlying `XCTestCase` that this wrapper represents. @@ -49,44 +40,16 @@ guard let spaceIndex = trimmedName.lastIndex(of: " ") else { return String(trimmedName) } - return "\(trimmedName[.. XCTestSuite { diff --git a/tools/test_observer/RuntimeLibraries.swift b/tools/test_observer/RuntimeLibraries.swift new file mode 100644 index 000000000..b771595d4 --- /dev/null +++ b/tools/test_observer/RuntimeLibraries.swift @@ -0,0 +1,85 @@ +// Copyright 2024 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 + +/// Dynamically load the testing libraries needed by the test observer. +/// +/// On Apple platforms, we weakly link against XCTest.framework, the XCTest Swift support dylib, and +/// Testing.framework because the machine that links the test binary might not be the same that runs +/// it, and they might have Xcode installed at different paths. To handle this, we find the path +/// that Bazel says Xcode they're installed at on the machine where the test is running and load +/// them dynamically. +@MainActor +public func loadTestingLibraries() throws { + #if os(Linux) + // Nothing to do here. All dependencies, including testing frameworks, are statically linked. + #else + guard let sdkRoot = ProcessInfo.processInfo.environment["SDKROOT"] else { + throw LibraryLoadError("ERROR: Bazel must set the SDKROOT in order to find XCTest") + } + let sdkRootURL = URL(fileURLWithPath: sdkRoot) + let platformDeveloperPath = + sdkRootURL // .../Developer/SDKs/MacOSX.sdk + .deletingLastPathComponent() // .../Developer/SDKs + .deletingLastPathComponent() // .../Developer + + let xcTestPath = + platformDeveloperPath + .appendingPathComponent("Library/Frameworks/XCTest.framework/XCTest") + .path + guard dlopen(xcTestPath, RTLD_NOW) != nil else { + throw LibraryLoadError( + #""" + ERROR: dlopen("\#(xcTestPath)") failed: \#(String(cString: dlerror())) + """#) + } + + // In versions of Xcode that have Testing.framework (Xcode 16 and above), + // libXCTestSwiftSupport.dylib links to it so we need to load the former first. We allow this to + // fail silently, however, to maintain compatibility with older versions of Xcode (where it + // doesn't exist, and thus the support library doesn't use it). + let testingPath = + platformDeveloperPath + .appendingPathComponent("Library/Frameworks/Testing.framework/Testing") + .path + _ = dlopen(testingPath, RTLD_NOW) + + let xcTestSwiftSupportPath = + platformDeveloperPath + .appendingPathComponent("usr/lib/libXCTestSwiftSupport.dylib") + .path + guard dlopen(xcTestSwiftSupportPath, RTLD_NOW) != nil else { + throw LibraryLoadError( + #""" + ERROR: dlopen("\#(xcTestSwiftSupportPath)") failed: \#(String(cString: dlerror())) + """#) + } + #endif +} + +/// An error that is thrown when a runtime library cannot be loaded. +struct LibraryLoadError: Swift.Error, CustomStringConvertible { + let description: String + + init(_ description: String) { + self.description = description + } +} + +/// We call this from the generated `main` so that we can declare it non-async (to make XCTest +/// happy) but then safely wait for an async task (swift-testing) to complete. This is part of the +/// concurrency ABI, so it can't realistically change much in the future. +@_silgen_name("swift_task_asyncMainDrainQueue") +public func _asyncMainDrainQueue() -> Swift.Never diff --git a/tools/test_observer/SwiftTestingRunner.swift b/tools/test_observer/SwiftTestingRunner.swift new file mode 100644 index 000000000..f8634c1e5 --- /dev/null +++ b/tools/test_observer/SwiftTestingRunner.swift @@ -0,0 +1,320 @@ +// Copyright 2024 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 + +#if canImport(Darwin) + import Darwin +#elseif canImport(Glibc) + import Glibc +#else + #error("Unsupported platform") +#endif + +/// A test runner that runs tests discovered by the swift-testing framework. +public final class SwiftTestingRunner: Sendable { + /// A wrapper around a swift-testing test identifier used by the test collector. + private struct Test: Testable { + /// The identifier of the test. + let testIdentifier: String + } + + /// A test or suite discovered by the swift-testing framework. + private enum TestOrSuite { + case suite(String) + case test(Test) + } + + /// Discovers and runs the tests. + public static func run() async throws { + guard let entryPoint = SwiftTestingEntryPoint() else { + // The entry point wasn't found, meaning swift-testing wasn't linked in and there are + // no tests to run. This is not an error. + return + } + try await SwiftTestingRunner(entryPoint: entryPoint).run() + } + + /// The JSON ABI entry point of the swift-testing framework. + private let entryPoint: SwiftTestingEntryPoint + + /// A set consisting only of the test suites that are discovered. + private let discoveredSuites: Locked> = .init([]) + + /// Creates a new runner that will use the given entry point to communicate with the + /// swift-testing framework. + private init(entryPoint: SwiftTestingEntryPoint) { + self.entryPoint = entryPoint + } + + /// Discovers and runs the tests. + private func run() async throws { + var collector = try ShardingFilteringTestCollector() + let selectedTests: [Test]? + + // We have to do this even when we're not sharding to filtering because we need to know which of + // the tests are actually suites (so that we don't include them in our xUnit results). + for try await testOrSuite in try await listTests() { + switch testOrSuite { + case .suite(let suiteID): + discoveredSuites.withLock { $0.insert(suiteID) } + case .test(let test): + collector.addTest(test) + } + } + if collector.willShardOrFilter { + selectedTests = collector.testsInCurrentShard + } else { + selectedTests = nil + } + + // Run the tests in the current shard. + try await runTests(selectedTests: selectedTests) + } + + /// Returns an async stream of values representing the tests and suites discovered in the binary + /// by the swift-testing framework. + private func listTests() async throws -> AsyncThrowingStream { + let listTestsConfiguration: JSON = [ + "listTests": true, + "verbosity": .number(Int.min), // Don't print anything to stdout. + ] + + // All of this could really just be a simple `.compactMap` on the stream and we'd return an + // opaque type, but primary associated types on `AsyncSequence` aren't usable before the runtime + // included with macOS 15.0. See SE-0421 for details + // (https://github.com/swiftlang/swift-evolution/blob/main/proposals/0421-generalize-async-sequence.md). + var escapedContinuation: AsyncThrowingStream.Continuation? = nil + let stream = AsyncThrowingStream(TestOrSuite.self, bufferingPolicy: .unbounded) { + continuation in escapedContinuation = continuation + } + guard let escapedContinuation else { + preconditionFailure("Stream continuation was never set") + } + do { + for try await recordJSON in try await entryPoint(configuration: listTestsConfiguration) { + guard + case .object(let record) = recordJSON, + case .object(let payload) = record["payload"], + case .string(let id) = payload["id"] + else { + continue + } + switch payload["kind"] { + case .string("function"): + escapedContinuation.yield(.test(Test(testIdentifier: id))) + case .string("suite"): + escapedContinuation.yield(.suite(id)) + default: + continue + } + } + escapedContinuation.finish() + } catch { + escapedContinuation.finish(throwing: error) + } + return stream + } + + /// Runs the given list of tests (or all tests). + /// + /// - Parameter selectedTests: If this parameter is not nil, only the given list of tests will be + /// run by passing them as filters to the entry point. If nil, all tests will be run. + private func runTests(selectedTests: [Test]?) async throws { + var runTestsConfiguration: [String: JSON] = [:] + if let selectedTests { + runTestsConfiguration["filter"] = .array( + selectedTests.map { + JSON.string(NSRegularExpression.escapedPattern(for: $0.testIdentifier)) + }) + } + for try await recordJSON in try await entryPoint(configuration: .object(runTestsConfiguration)) + { + guard case .object(let record) = recordJSON, + case .string("event") = record["kind"], + case .object(let payload) = record["payload"] + else { + continue + } + recordEvent(payload) + } + } + + private func recordEvent(_ payload: [String: JSON]) { + // We only care about test events that have a test ID and an instant (when they occurred). + guard + case .string(let kind) = payload["kind"], + case .string(let testID) = payload["testID"], + // Ignore suites. The xUnit recorder reconstructs the hierarchy. + !discoveredSuites.withLock { $0.contains(testID) }, + case .object(let instantJSON) = payload["instant"], + case .number(let absolute) = instantJSON["absolute"] + else { + return + } + let instant = EncodedInstant(seconds: absolute.doubleValue) + let nameComponents = nameComponents(for: testID) + + switch kind { + case "testStarted": + XUnitTestRecorder.shared.recordTestStarted(nameComponents: nameComponents, time: instant) + + case "testEnded": + XUnitTestRecorder.shared.recordTestEnded(nameComponents: nameComponents, time: instant) + + case "issueRecorded": + guard + case .array(let messages) = payload["messages"], + // Don't record known issues. + case .object(let issue) = payload["issue"], + case .bool(false) = issue["isKnown"] + else { + return + } + // The issue may have multiple messages, some of which are extra details. Pick out the main + // message for the failure. + // TODO: b/301468828 - Handle the extra detail messages as well. + for case .object(let message) in messages { + guard + case .string("fail") = message["symbol"], + case .string(let text) = message["text"] + else { + return + } + XUnitTestRecorder.shared.recordTestIssue( + nameComponents: nameComponents, + issue: RecordedIssue(kind: .failure, reason: text)) + } + + default: + break + } + } + + /// Returns a list of name components by parsing the given test identifier. + private func nameComponents(for testID: String) -> [String] { + let components = testID.split(separator: "/") + // Some test IDs end with the source location of the test, which is not typically useful to show + // as part of the hierarchy. + if let last = components.last, last.firstMatch(of: /\.swift:\d+:\d+/) != nil { + return components[..<(components.count - 1)].map(String.init) + } + return components.map(String.init) + } +} + +/// Represents an instant in time that is encoded as part of a test event. +/// +/// The instant is encoded as a double representing the number of seconds retrieved from +/// `SuspendingClock` at the time the event occurred. We can't reconstitute that value back into a +/// `SuspendingClock.Instant`, but they're all relative to each other so we can provide our own +/// `InstantProtocol` implementation that is used to compute the duration between two events. +private struct EncodedInstant: Comparable, InstantProtocol { + /// The number of seconds since the test clock's basis. + var seconds: Double + + static func < (lhs: EncodedInstant, rhs: EncodedInstant) -> Bool { + return lhs.seconds < rhs.seconds + } + + func advanced(by duration: Swift.Duration) -> EncodedInstant { + let components = duration.components + return EncodedInstant( + seconds: seconds + Double(components.seconds) + Double(components.attoseconds) / 1e18) + } + + func duration(to other: EncodedInstant) -> Swift.Duration { + return .seconds(other.seconds - self.seconds) + } +} + +/// Represents the entry point of the swift-testing framework and handles the translation of +/// requests and responses between structured JSON and raw byte buffers. +private struct SwiftTestingEntryPoint { + private typealias ABIv0EntryPoint = @convention(thin) @Sendable ( + _ configurationJSON: UnsafeRawBufferPointer?, + _ recordHandler: @escaping @Sendable (_ recordJSON: UnsafeRawBufferPointer) -> Void + ) async throws -> Bool + + private let entryPoint: ABIv0EntryPoint + + /// Creates the entry point by looking it up by name in the current process, or fails if the + /// entry point is not found. + init?() { + guard let entryPointRaw = dlsym(rtldDefault, "swt_abiv0_getEntryPoint") else { + return nil + } + let abiv0_getEntryPoint = unsafeBitCast( + entryPointRaw, to: (@convention(c) () -> UnsafeRawPointer).self) + self.entryPoint = unsafeBitCast(abiv0_getEntryPoint(), to: ABIv0EntryPoint.self) + } + + /// Calls the entry point with the given configuration JSON and returns an asynchronous stream of + /// the JSON records that the framework produces as a response. + func callAsFunction( + configuration configurationJSON: JSON + ) async throws -> AsyncThrowingStream { + // Since `withUnsafeBytes` is not `async`, we have to copy the data out into a separate + // buffer and then invoke the entry point. + let configurationJSONBytes = try configurationJSON.encodedData.withUnsafeBytes { bytes in + let result = UnsafeMutableRawBufferPointer.allocate(byteCount: bytes.count, alignment: 1) + result.copyMemory(from: bytes) + return result + } + defer { configurationJSONBytes.deallocate() } + + // `Async(Throwing)Stream` is explicitly designed to allow its continuation to escape. We have + // to do this since the entry point function that we're calling is `async`, but the closure + // passed to the stream's initializer is not allowed to be `async`. + var escapedContinuation: AsyncThrowingStream.Continuation? = nil + let stream = AsyncThrowingStream(JSON.self, bufferingPolicy: .unbounded) { continuation in + escapedContinuation = continuation + } + guard let escapedContinuation else { + preconditionFailure("Stream continuation was never set") + } + do { + _ = try await self.entryPoint(UnsafeRawBufferPointer(configurationJSONBytes)) { + recordJSONBytes in + let data = Data(bytes: recordJSONBytes.baseAddress!, count: recordJSONBytes.count) + do { + let json = try JSON(byDecoding: data) + escapedContinuation.yield(json) + } catch { + escapedContinuation.finish(throwing: error) + } + } + escapedContinuation.finish() + } catch { + escapedContinuation.finish(throwing: error) + } + return stream + } +} + +// `RTLD_DEFAULT` is only defined on Linux when `_GNU_SOURCE` is defined. Just redefine it +// here for convenience. +#if compiler(>=5.10) + #if os(Linux) + private nonisolated(unsafe) let rtldDefault = UnsafeMutableRawPointer(bitPattern: 0) + #else + private nonisolated(unsafe) let rtldDefault = UnsafeMutableRawPointer(bitPattern: -2) + #endif +#else + #if os(Linux) + private let rtldDefault = UnsafeMutableRawPointer(bitPattern: 0) + #else + private let rtldDefault = UnsafeMutableRawPointer(bitPattern: -2) + #endif +#endif From c8da2018ccb13a17034fc90068310d913187f64c Mon Sep 17 00:00:00 2001 From: Tony Allevato Date: Mon, 26 Aug 2024 06:59:09 -0700 Subject: [PATCH 63/92] Make the generated discovered test code compile without concurrency warnings PiperOrigin-RevId: 667570499 (cherry picked from commit 1dd927f82aeb6b1ebd68f346982afef5f00756b1) Signed-off-by: Brentley Jones --- tools/test_discoverer/SymbolGraphTestPrinter.swift | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tools/test_discoverer/SymbolGraphTestPrinter.swift b/tools/test_discoverer/SymbolGraphTestPrinter.swift index 40c77c78b..089b58486 100644 --- a/tools/test_discoverer/SymbolGraphTestPrinter.swift +++ b/tools/test_discoverer/SymbolGraphTestPrinter.swift @@ -66,7 +66,7 @@ struct SymbolGraphTestPrinter { var contents = """ import XCTest - @testable import \(moduleName) + @preconcurrency @testable import \(moduleName) """ @@ -78,6 +78,7 @@ struct SymbolGraphTestPrinter { fileprivate extension \(className) { \(availabilityAttribute) + @MainActor static let \(allTestsIdentifier(for: testClass)) = [ """ @@ -99,6 +100,7 @@ struct SymbolGraphTestPrinter { contents += """ \(availabilityAttribute) + @MainActor let \(allTestsIdentifier(for: discoveredModule)) = [ """ @@ -127,6 +129,7 @@ struct SymbolGraphTestPrinter { // harmlessly compiled as an empty module, and the user's `main` from their own sources will // be used instead. return """ + @MainActor private let __allDiscoveredXCTests: [XCTestCaseEntry] = [] """ @@ -134,6 +137,7 @@ struct SymbolGraphTestPrinter { var contents = """ \(availabilityAttribute) + @MainActor private let __allDiscoveredXCTests: [XCTestCaseEntry] = { var allTests: [XCTestCaseEntry] = [] From b520f692446e568f4707d4e91fd4d52f56af3e60 Mon Sep 17 00:00:00 2001 From: Tony Allevato Date: Wed, 4 Sep 2024 08:42:38 -0700 Subject: [PATCH 64/92] Fix filtering of swift-testing tests * Remove the module name from the test identifier used for filtering. * If no tests matched the filter, request explicit skipping of all tests so that the runner doesn't interpret the empty filter set as "run all tests". PiperOrigin-RevId: 670984261 (cherry picked from commit 7c149cf490e8e9ad587d751a54a9dbadd379f5b5) Signed-off-by: Brentley Jones --- tools/test_observer/SwiftTestingRunner.swift | 27 +++++++++++++++++--- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/tools/test_observer/SwiftTestingRunner.swift b/tools/test_observer/SwiftTestingRunner.swift index f8634c1e5..b6e549c04 100644 --- a/tools/test_observer/SwiftTestingRunner.swift +++ b/tools/test_observer/SwiftTestingRunner.swift @@ -28,6 +28,18 @@ public final class SwiftTestingRunner: Sendable { private struct Test: Testable { /// The identifier of the test. let testIdentifier: String + + init(testIdentifier: String) { + // The test identifier given by swift-testing starts with the name of the module, which we + // strip off because it isn't terribly useful for filtering -- it's burdensome for users to + // type and IDEs cannot easily determine it without querying the build system. + let components = testIdentifier.split(separator: ".", maxSplits: 1) + if components.count > 1 { + self.testIdentifier = String(components[1]) + } else { + self.testIdentifier = testIdentifier + } + } } /// A test or suite discovered by the swift-testing framework. @@ -134,10 +146,17 @@ public final class SwiftTestingRunner: Sendable { private func runTests(selectedTests: [Test]?) async throws { var runTestsConfiguration: [String: JSON] = [:] if let selectedTests { - runTestsConfiguration["filter"] = .array( - selectedTests.map { - JSON.string(NSRegularExpression.escapedPattern(for: $0.testIdentifier)) - }) + // If we applied a test filter and no tests were selected, setting the `filter` configuration + // value to an empty array will be treated by the runner as if there were no filter (and thus + // run *all* tests). To handle this correctly, we ask the runner to explicitly skip all tests. + if selectedTests.isEmpty { + runTestsConfiguration["skip"] = [".*"] + } else { + runTestsConfiguration["filter"] = .array( + selectedTests.map { + JSON.string(NSRegularExpression.escapedPattern(for: $0.testIdentifier)) + }) + } } for try await recordJSON in try await entryPoint(configuration: .object(runTestsConfiguration)) { From 1696c7364c8afebf342a7dc921f3e062f916f422 Mon Sep 17 00:00:00 2001 From: Tony Allevato Date: Thu, 12 Sep 2024 07:00:52 -0700 Subject: [PATCH 65/92] Additional changes to XCTest symbol graph test discovery for Swift 6 compatibility MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Making all of the discovered tests returned by `static func`s instead of stored `static let`s removes some problematic `@Sendable`-related function conversions that were causing a runtime crash when Swift 6 more is enabled. SwiftPM avoids this problem by... not ever compiling the discovered test runner in Swift 6 mode even if the package requests it. 🫤 PiperOrigin-RevId: 673829386 (cherry picked from commit 6988259ff795c3b61dc7586a84754288a288868e) Signed-off-by: Brentley Jones --- .../SymbolGraphTestPrinter.swift | 31 +++++++++++-------- tools/test_discoverer/TestDiscoverer.swift | 4 +-- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/tools/test_discoverer/SymbolGraphTestPrinter.swift b/tools/test_discoverer/SymbolGraphTestPrinter.swift index 089b58486..1e4c1e8bf 100644 --- a/tools/test_discoverer/SymbolGraphTestPrinter.swift +++ b/tools/test_discoverer/SymbolGraphTestPrinter.swift @@ -21,7 +21,7 @@ import Foundation /// function if necessary. private func generatedTestEntry(for method: DiscoveredTests.Method) -> String { if method.isAsync { - return "asyncTest(\(method.name))" + return "asyncTest({ type in type.\(method.name) })" } else { return method.name } @@ -66,7 +66,7 @@ struct SymbolGraphTestPrinter { var contents = """ import XCTest - @preconcurrency @testable import \(moduleName) + @testable import \(moduleName) """ @@ -78,20 +78,23 @@ struct SymbolGraphTestPrinter { fileprivate extension \(className) { \(availabilityAttribute) - @MainActor - static let \(allTestsIdentifier(for: testClass)) = [ + static func \(allTestsIdentifier(for: testClass))() + -> [(String, (\(className)) -> () throws -> Void)] + { + return [ """ for testMethod in testClass.methods.sorted(by: { $0.name < $1.name }) { contents += """ - ("\(testMethod.name)", \(generatedTestEntry(for: testMethod))), + ("\(testMethod.name)", \(generatedTestEntry(for: testMethod))), """ } contents += """ - ] + ] + } } """ @@ -101,20 +104,22 @@ struct SymbolGraphTestPrinter { \(availabilityAttribute) @MainActor - let \(allTestsIdentifier(for: discoveredModule)) = [ + func \(allTestsIdentifier(for: discoveredModule))() -> [XCTestCaseEntry] { + return [ """ for className in sortedClassNames { let testClass = discoveredModule.classes[className]! contents += """ - testCase(\(className).\(allTestsIdentifier(for: testClass))), + testCase(\(className).\(allTestsIdentifier(for: testClass))()), """ } contents += """ - ] + ] + } """ @@ -130,7 +135,7 @@ struct SymbolGraphTestPrinter { // be used instead. return """ @MainActor - private let __allDiscoveredXCTests: [XCTestCaseEntry] = [] + private func __allDiscoveredXCTests() -> [XCTestCaseEntry] { [] } """ } @@ -138,7 +143,7 @@ struct SymbolGraphTestPrinter { var contents = """ \(availabilityAttribute) @MainActor - private let __allDiscoveredXCTests: [XCTestCaseEntry] = { + private func __allDiscoveredXCTests() -> [XCTestCaseEntry] { var allTests: [XCTestCaseEntry] = [] """ @@ -146,14 +151,14 @@ struct SymbolGraphTestPrinter { for moduleName in discoveredTests.modules.keys.sorted() { let module = discoveredTests.modules[moduleName]! contents += """ - allTests.append(contentsOf: \(allTestsIdentifier(for: module))) + allTests.append(contentsOf: \(allTestsIdentifier(for: module))()) """ } contents += """ return allTests - }() + } """ diff --git a/tools/test_discoverer/TestDiscoverer.swift b/tools/test_discoverer/TestDiscoverer.swift index db50abe2a..29a0c9074 100644 --- a/tools/test_discoverer/TestDiscoverer.swift +++ b/tools/test_discoverer/TestDiscoverer.swift @@ -123,7 +123,7 @@ struct TestDiscoverer: ParsableCommand { exit(1) } do { - try XCTestRunner.run(__allDiscoveredXCTests) + try XCTestRunner.run(__allDiscoveredXCTests()) } catch { print("Fatal error running XCTest tests: \\(error)") exit(1) @@ -160,7 +160,7 @@ struct TestDiscoverer: ParsableCommand { // platforms. contents.append(""" // Unused by the Objective-C XCTestRunner; tests are discovered by the runtime. - private let __allDiscoveredXCTests: () = () + private func __allDiscoveredXCTests() {} """) } else { From b7eb892f8b9b6167ef4b4ef1ccc8dd43d4a22bf2 Mon Sep 17 00:00:00 2001 From: Qing Yang Date: Tue, 18 Feb 2025 12:56:41 -0800 Subject: [PATCH 66/92] Support swift plugins in mixed_language_library (#1493) --- doc/rules.md | 5 +++-- mixed_language/mixed_language_library.bzl | 3 +++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/doc/rules.md b/doc/rules.md index c1c62514e..ddbba2091 100644 --- a/doc/rules.md +++ b/doc/rules.md @@ -1067,8 +1067,8 @@ swift_library( mixed_language_library(name, alwayslink, clang_copts, clang_defines, clang_srcs, data, enable_modules, hdrs, includes, linkopts, module_map, module_name, non_arc_srcs, package_name, private_deps, sdk_dylibs, sdk_frameworks, - swift_copts, swift_defines, swift_srcs, swiftc_inputs, textual_hdrs, - umbrella_header, weak_sdk_frameworks, deps, kwargs) + swift_copts, swift_defines, swift_plugins, swift_srcs, swiftc_inputs, + textual_hdrs, umbrella_header, weak_sdk_frameworks, deps, kwargs) Creates a mixed language library from a Clang and Swift library target pair. @@ -1101,6 +1101,7 @@ Once that is the case, this macro will be deprecated. | sdk_frameworks | A list of SDK frameworks to link with (e.g. "AddressBook", "QuartzCore").

When linking a top level Apple binary, all SDK frameworks listed in that binary's transitive dependency graph are linked. | `[]` | | swift_copts | The compiler flags for the swift library. | `[]` | | swift_defines | A list of Swift 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 `swift_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 `swift_copts`, only using this feature in the rare case that a library needs to propagate a symbol up to those that depend on it. | `[]` | +| swift_plugins | A list of Swift plugins for the swift library. | `[]` | | swift_srcs | The sources for the swift library. | none | | swiftc_inputs | Additional files that are referenced using `$(location ...)` in attributes that support location expansion. | `[]` | | textual_hdrs | The list of C, C++, Objective-C, or Objective-C++ files that are included as headers by source files in this rule or by users of this library. Unlike `hdrs`, these will not be compiled separately from the sources. | `[]` | diff --git a/mixed_language/mixed_language_library.bzl b/mixed_language/mixed_language_library.bzl index 5e603c24e..4f8a08f04 100644 --- a/mixed_language/mixed_language_library.bzl +++ b/mixed_language/mixed_language_library.bzl @@ -53,6 +53,7 @@ def mixed_language_library( sdk_frameworks = [], swift_copts = [], swift_defines = [], + swift_plugins = [], swift_srcs, swiftc_inputs = [], textual_hdrs = [], @@ -152,6 +153,7 @@ def mixed_language_library( It is preferred that you add defines directly to `swift_copts`, only using this feature in the rare case that a library needs to propagate a symbol up to those that depend on it. + swift_plugins: A list of Swift plugins for the swift library. swift_srcs: The sources for the swift library. swiftc_inputs: Additional files that are referenced using `$(location ...)` in attributes that support location expansion. @@ -319,6 +321,7 @@ a mixed language Swift library, use a clang only library rule like \ linkopts = linkopts, module_name = module_name, package_name = package_name, + plugins = swift_plugins, private_deps = private_deps, swiftc_inputs = swiftc_inputs, tags = internal_tags, From aae0cab63fa7464c8b5b25fe574379b281d5564a Mon Sep 17 00:00:00 2001 From: Qing Yang Date: Thu, 20 Feb 2025 13:01:01 -0800 Subject: [PATCH 67/92] Deduplicate plugin arguments (#1495) Currently, if we have the following setup, ``` swift_library ( name = "LibA" plugins = ["//:Macros"], ) swift_library ( name = "LibB" plugins = ["//:Macros"], deps = [":LibA"], ) ``` We would see `-load-plugin-executable /path/to/Macros#Macros` twice while compiling LibB. With a more complicated dependency graph, we are seeing hundreds of -load-plugin-executable of the same plugin on some modules. This PR deduplicates the plugins arguments. --- swift/internal/compiling.bzl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/swift/internal/compiling.bzl b/swift/internal/compiling.bzl index fd184fc9a..576e6e7f5 100644 --- a/swift/internal/compiling.bzl +++ b/swift/internal/compiling.bzl @@ -14,6 +14,7 @@ """Implementation of compilation logic for Swift.""" +load("@bazel_skylib//lib:collections.bzl", "collections") load("@bazel_skylib//lib:paths.bzl", "paths") load("@bazel_skylib//lib:sets.bzl", "sets") load( @@ -645,7 +646,7 @@ to use swift_common.compile(include_dev_srch_paths = ...) instead.\ module_name = module_name, original_module_name = original_module_name, package_name = package_name, - plugins = used_plugins, + plugins = collections.uniq(used_plugins), source_files = srcs, target_label = feature_configuration._label, transitive_modules = transitive_modules, From 1bc8f270b56c9cdd63a92940b989084b8e3bccde Mon Sep 17 00:00:00 2001 From: Corentin Kerisit Date: Mon, 3 Mar 2025 21:33:14 +0100 Subject: [PATCH 68/92] Introduce a pure Swift runfiles library (#1310) Fixes #890 # Main implementation I followed guidance from @fmeum and based this implementation on the runfiles library of `rules_python` where applicable since this was pointed as the reference implementation. In addition to `rules_python` implementation, this implementation uses a similar mechanism as the C++ implementation for deducing the `RUNFILES_DIR` and `RUNFILES_MANIFEST_FILE` location based on `argv0`. --------- Co-authored-by: Brentley Jones Co-authored-by: Luis Padron Co-authored-by: Fabian Meumertzheim --- examples/runfiles/BUILD | 13 + examples/runfiles/data/sample.txt | 1 + examples/runfiles/main.swift | 17 + swift/internal/BUILD | 6 + swift/runfiles/BUILD | 10 + swift/runfiles/README.md | 76 +++++ swift/runfiles/Runfiles.swift | 307 +++++++++++++++++ test/runfiles/BUILD | 9 + test/runfiles/RunfilesTests.swift | 540 ++++++++++++++++++++++++++++++ 9 files changed, 979 insertions(+) create mode 100644 examples/runfiles/BUILD create mode 100644 examples/runfiles/data/sample.txt create mode 100644 examples/runfiles/main.swift create mode 100644 swift/runfiles/BUILD create mode 100644 swift/runfiles/README.md create mode 100644 swift/runfiles/Runfiles.swift create mode 100644 test/runfiles/BUILD create mode 100644 test/runfiles/RunfilesTests.swift diff --git a/examples/runfiles/BUILD b/examples/runfiles/BUILD new file mode 100644 index 000000000..675e669b7 --- /dev/null +++ b/examples/runfiles/BUILD @@ -0,0 +1,13 @@ +load("//swift:swift.bzl", "swift_binary") + +swift_binary( + name = "runfiles_example", + srcs = ["main.swift"], + data = [ + "data/sample.txt", + ], + visibility = ["//visibility:public"], + deps = [ + "//swift/runfiles", + ], +) diff --git a/examples/runfiles/data/sample.txt b/examples/runfiles/data/sample.txt new file mode 100644 index 000000000..e021b42d9 --- /dev/null +++ b/examples/runfiles/data/sample.txt @@ -0,0 +1 @@ +Hello runfiles \ No newline at end of file diff --git a/examples/runfiles/main.swift b/examples/runfiles/main.swift new file mode 100644 index 000000000..8a471899d --- /dev/null +++ b/examples/runfiles/main.swift @@ -0,0 +1,17 @@ +import BazelRunfiles + +do { + let runfiles = try Runfiles.create() + // Runfiles lookup paths have the form `my_workspace/package/file`. + // Runfiles path lookup may throw. + let fileURL = try runfiles.rlocation("build_bazel_rules_swift/examples/runfiles/data/sample.txt") + print("file: \(fileURL)") + + // Runfiles path lookup may return a non-existent path. + let content = try String(contentsOf: fileURL, encoding: .utf8) + + assert(content == "Hello runfiles") + print(content) +} catch { + print("runfiles error: \(error)") +} diff --git a/swift/internal/BUILD b/swift/internal/BUILD index 21bffb5b4..4806e901c 100644 --- a/swift/internal/BUILD +++ b/swift/internal/BUILD @@ -214,6 +214,12 @@ bzl_library( ], ) +bzl_library( + name = "runfiles", + srcs = ["runfiles.bzl"], + visibility = ["//swift:__subpackages__"], +) + bzl_library( name = "swift_autoconfiguration", srcs = ["swift_autoconfiguration.bzl"], diff --git a/swift/runfiles/BUILD b/swift/runfiles/BUILD new file mode 100644 index 000000000..130d19200 --- /dev/null +++ b/swift/runfiles/BUILD @@ -0,0 +1,10 @@ +load("//swift:swift_library.bzl", "swift_library") + +swift_library( + name = "runfiles", + srcs = [ + "Runfiles.swift", + ], + module_name = "BazelRunfiles", + visibility = ["//visibility:public"], +) diff --git a/swift/runfiles/README.md b/swift/runfiles/README.md new file mode 100644 index 000000000..ce893ef30 --- /dev/null +++ b/swift/runfiles/README.md @@ -0,0 +1,76 @@ +# Swift BazelRunfiles library + +This is a Bazel Runfiles lookup library for Bazel-built Swift binaries and tests. + +Learn about runfiles: read [Runfiles guide](https://bazel.build/extending/rules#runfiles) +or watch [Fabian's BazelCon talk](https://www.youtube.com/watch?v=5NbgUMH1OGo). + +## Usage + +1. Depend on this runfiles library from your build rule: + +```python +swift_binary( + name = "my_binary", + ... + data = ["//path/to/my/data.txt"], + deps = ["@build_bazel_rules_swift//swift/runfiles"], +) +``` + +2. Include the runfiles library: + +```swift +import BazelRunfiles +``` + +3. Create a Runfiles instance and use `rlocation` to look up runfile urls: + +```swift +import BazelRunfiles + +do { + let runfiles = try Runfiles.create() + let fileURL = try runfiles.rlocation("my_workspace/path/to/my/data.txt") + print("file: \(fileURL)") +} catch { + print("runfiles error: \(error)") +} +``` + +The code above: + +- Creates a manifest- or directory-based implementation based on + the environment variables in `Process.processInfo.environment`. + See `Runfiles.create()` for more info. +- The `Runfiles.create` function uses the runfiles manifest and the runfiles + directory from the `RUNFILES_MANIFEST_FILE` and `RUNFILES_DIR` environment + variables. If not present, the function looks for the manifest and directory + near `CommandLine.arguments.first` (e.g. `argv[0]` the path of the main program). + +If you want to start subprocesses, the runfiles library helps you set the required environment variables for them to find their runfiles: + +```swift +import BazelRunfiles +import Foundation + +do { + + let runfiles = try Runfiles.create() + let executableURL = try runfiles.rlocation("my_workspace/path/to/binary") + + let process = Process() + process.executableURL = executableURL + process.environment = runfiles.envVars() + + do { + // Launch the process + try process.run() + process.waitUntilExit() + } catch { + // ... + } +} catch { + fatalError("runfiles error: \(error)") +} +``` diff --git a/swift/runfiles/Runfiles.swift b/swift/runfiles/Runfiles.swift new file mode 100644 index 000000000..36c591cc7 --- /dev/null +++ b/swift/runfiles/Runfiles.swift @@ -0,0 +1,307 @@ +// Copyright 2025 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 + +enum RunfilesEnv { + static let runfilesManifestFile: String = "RUNFILES_MANIFEST_FILE" + static let runfilesDir: String = "RUNFILES_DIR" +} + +protocol LookupStrategy { + func rlocationChecked(path: String) throws -> URL + func envVars() -> [String: String] +} + +struct DirectoryBased: LookupStrategy { + private let runfilesRoot: URL + init(path: URL) { + runfilesRoot = path + } + + func rlocationChecked(path: String) throws -> URL { + runfilesRoot.appendingPathComponent(path) + } + + func envVars() -> [String: String] { + [ + RunfilesEnv.runfilesDir: runfilesRoot.path, + ] + } +} + +struct ManifestBased: LookupStrategy { + private let manifestPath: URL + private let runfiles: [String: String] + + init(manifestPath: URL) throws { + self.manifestPath = manifestPath + runfiles = try Self.loadRunfiles(from: manifestPath) + } + + func rlocationChecked(path: String) throws -> URL { + if let runfile = runfiles[path] { + return URL(fileURLWithPath: runfile) + } + + // Search for prefixes in the path + for end in path.indices.reversed() where path[end] == "/" { + let prefix = String(path[.. [String: String] { + [ + RunfilesEnv.runfilesManifestFile: manifestPath.path, + ] + } + + static func loadRunfiles(from manifestPath: URL) throws -> [String: String] { + guard let fileHandle = try? FileHandle(forReadingFrom: manifestPath) else { + throw RunfilesError.missingManifest + } + defer { + try? fileHandle.close() + } + + var pathMapping = [String: String]() + if let data = try? fileHandle.readToEnd(), let content = String(data: data, encoding: .utf8) { + let lines = content.split(separator: "\n") + for line in lines { + let fields = line.split(separator: " ", maxSplits: 1) + if fields.count == 1 { + pathMapping[String(fields[0])] = String(fields[0]) + } else { + pathMapping[String(fields[0])] = String(fields[1]) + } + } + } + + return pathMapping + } +} + +struct RepoMappingKey: Hashable { + let sourceRepoCanonicalName: String + let targetRepoApparentName: String +} + +public enum RunfilesError: Error { + case invalidRunfilePath + case invalidRunfilesLocations + case missingRunfilesLocations + case missingRunfileEntryFromManifest + case invalidRepoMappingEntry(line: String) + case missingManifest +} + +public final class Runfiles { + private let strategy: LookupStrategy + // Value is the runfiles directory of target repository + private let repoMapping: [RepoMappingKey: String] + private let sourceRepository: String + + init(strategy: LookupStrategy, repoMapping: [RepoMappingKey: String], sourceRepository: String) { + self.strategy = strategy + self.repoMapping = repoMapping + self.sourceRepository = sourceRepository + } + + public func rlocation(_ path: String, sourceRepository: String? = nil) throws -> URL { + guard + !path.hasPrefix("../"), + !path.contains("/.."), + !path.hasPrefix("./"), + !path.contains("/./"), + !path.hasSuffix("/."), + !path.contains("//") + else { + throw RunfilesError.invalidRunfilePath + } + guard path.first != "\\" else { + throw RunfilesError.invalidRunfilePath + } + guard path.first != "/" else { + return URL(fileURLWithPath: path) + } + + let sourceRepository = sourceRepository ?? self.sourceRepository + + // Split off the first path component, which contains the repository + // name (apparent or canonical). + let components = path.split(separator: "/", maxSplits: 1) + let targetRepository = String(components[0]) + let key = RepoMappingKey(sourceRepoCanonicalName: sourceRepository, targetRepoApparentName: targetRepository) + + if components.count == 1 || repoMapping[key] == nil { + // One of the following is the case: + // - not using Bzlmod, so the repository mapping is empty and + // apparent and canonical repository names are the same + // - target_repo is already a canonical repository name and does not + // have to be mapped. + // - path did not contain a slash and referred to a root symlink, + // which also should not be mapped. + return try strategy.rlocationChecked(path: path) + } + + let remainingPath = String(components[1]) + + // target_repo is an apparent repository name. Look up the corresponding + // canonical repository name with respect to the current repository, + // identified by its canonical name. + if let targetCanonical = repoMapping[key] { + return try strategy.rlocationChecked(path: targetCanonical + "/" + remainingPath) + } else { + return try strategy.rlocationChecked(path: path) + } + } + + public func envVars() -> [String: String] { + strategy.envVars() + } + + // MARK: Factory method + + public static func create(sourceRepository: String? = nil, environment: [String: String]? = nil, _ callerFilePath: String = #filePath) throws -> Runfiles { + + let environment = environment ?? ProcessInfo.processInfo.environment + + let runfilesPath = try computeRunfilesPath( + argv0: CommandLine.arguments[0], + manifestFile: environment[RunfilesEnv.runfilesManifestFile], + runfilesDir: environment[RunfilesEnv.runfilesDir], + isRunfilesManifest: { file in FileManager.default.fileExists(atPath: file) }, + isRunfilesDirectory: { file in + var isDir: ObjCBool = false + return FileManager.default.fileExists(atPath: file, isDirectory: &isDir) && isDir.boolValue + } + ) + + let strategy: LookupStrategy = switch (runfilesPath) { + case .manifest(let path): + try ManifestBased(manifestPath: URL(fileURLWithPath: path)) + case .directory(let path): + DirectoryBased(path: URL(fileURLWithPath: path)) + } + + // If the repository mapping file can't be found, that is not an error: We + // might be running without Bzlmod enabled or there may not be any runfiles. + // In this case, just apply an empty repo mapping. + let repoMapping: [RepoMappingKey : String] = if let path = try? strategy.rlocationChecked(path: "_repo_mapping") { + try parseRepoMapping(path: path) + } else { + [:] + } + + return Runfiles(strategy: strategy, repoMapping: repoMapping, sourceRepository: sourceRepository ?? repository(from: callerFilePath)) + } +} + + // https://github.com/bazel-contrib/rules_go/blob/6505cf2e4f0a768497b123a74363f47b711e1d02/go/runfiles/global.go#L53-L54 + private let legacyExternalGeneratedFile = /bazel-out\/[^\/]+\/bin\/external\/([^\/]+)/ + private let legacyExternalFile = /external\/([^\/]+)/ + + // Extracts the canonical name of the repository containing the file + // located at `path`. + private func repository(from path: String) -> String { + if let match = path.prefixMatch(of: legacyExternalGeneratedFile) { + return String(match.1) + } + if let match = path.prefixMatch(of: legacyExternalFile) { + return String(match.1) + } + // If a file is not in an external repository, return an empty string + return "" + } + +// MARK: Runfiles Paths Computation + +enum RunfilesPath: Equatable { + case manifest(String) + case directory(String) +} + +func computeRunfilesPath( + argv0: String, + manifestFile: String?, + runfilesDir: String?, + isRunfilesManifest: (String) -> Bool, + isRunfilesDirectory: (String) -> Bool +) throws -> RunfilesPath { + // if a manifest or a runfiles dir was provided, try to use whichever + // was valid or else error. + if (manifestFile != nil || runfilesDir != nil) { + if let manifestFile, isRunfilesManifest(manifestFile) { + return RunfilesPath.manifest(manifestFile) + } else if let runfilesDir, isRunfilesDirectory(runfilesDir) { + return RunfilesPath.directory(runfilesDir) + } else { + throw RunfilesError.invalidRunfilesLocations + } + } + + // If a manifest exists in one of the well known location, use it. + for wellKnownManifestFileSuffixes in [".runfiles/MANIFEST", ".runfiles_manifest"] { + let manifestFileCandidate = "\(argv0)\(wellKnownManifestFileSuffixes)" + if isRunfilesManifest(manifestFileCandidate) { + return RunfilesPath.manifest(manifestFileCandidate) + } + } + + // If a runfiles dir exists in the well known location, use it. + let runfilesDirCandidate = "\(argv0).runfiles" + if isRunfilesDirectory(runfilesDirCandidate) { + return RunfilesPath.directory(runfilesDirCandidate) + } + + throw RunfilesError.missingRunfilesLocations +} + +// MARK: Parsing Repo Mapping + +func parseRepoMapping(path: URL) throws -> [RepoMappingKey: String] { + guard let fileHandle = try? FileHandle(forReadingFrom: path) else { + // If the repository mapping file can't be found, that is not an error: We + // might be running without Bzlmod enabled or there may not be any runfiles. + // In this case, just apply an empty repo mapping. + return [:] + } + defer { + try? fileHandle.close() + } + + var repoMapping = [RepoMappingKey: String]() + if let data = try fileHandle.readToEnd(), let content = String(data: data, encoding: .utf8) { + let lines = content.split(separator: "\n") + for line in lines { + let fields = line.components(separatedBy: ",") + if fields.count != 3 { + throw RunfilesError.invalidRepoMappingEntry(line: String(line)) + } + let key = RepoMappingKey( + sourceRepoCanonicalName: fields[0], + targetRepoApparentName: fields[1] + ) + repoMapping[key] = fields[2] // mapping + } + } + + return repoMapping +} diff --git a/test/runfiles/BUILD b/test/runfiles/BUILD new file mode 100644 index 000000000..be733625d --- /dev/null +++ b/test/runfiles/BUILD @@ -0,0 +1,9 @@ +load("//swift:swift_test.bzl", "swift_test") + +swift_test( + name = "RunfilesTests", + srcs = ["RunfilesTests.swift"], + deps = [ + "@build_bazel_rules_swift//swift/runfiles", + ], +) diff --git a/test/runfiles/RunfilesTests.swift b/test/runfiles/RunfilesTests.swift new file mode 100644 index 000000000..d5fb1ced8 --- /dev/null +++ b/test/runfiles/RunfilesTests.swift @@ -0,0 +1,540 @@ +// Copyright 2025 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. + +@testable import BazelRunfiles +import Foundation +import XCTest + +// Mainly adapted from https://github.com/bazelbuild/rules_python/blob/main/tests/runfiles/runfiles_test.py +final class RunfilesTests: XCTestCase { + func testRlocationArgumentValidation() throws { + let (fileURL, clean) = try createMockFile(name: "MANIFEST", contents: "a/b /c/d"); defer { try? clean() } + + let runfiles = try Runfiles.create( + environment: [ + "RUNFILES_MANIFEST_FILE": fileURL.path, + "RUNFILES_DIR": "ignored when RUNFILES_MANIFEST_FILE has a value", + "TEST_SRCDIR": "always ignored", + ] + ) + XCTAssertEqual(try runfiles.rlocation("a/b").path, "/c/d") + XCTAssertNil(try? runfiles.rlocation("foo")) + } + + func testManifestBasedRunfilesEnvVarsFromArbitraryManifest() throws { + let (manifest, clean) = try createMockFile(name: "x_manifest", contents: "a/b /c/d"); defer { try? clean() } + + let runfiles = try Runfiles.create( + environment: [ + "RUNFILES_MANIFEST_FILE": manifest.path, + "TEST_SRCDIR": "always ignored", + ] + ) + + XCTAssertEqual(runfiles.envVars(), [ + "RUNFILES_MANIFEST_FILE": manifest.path, + ]) + } + + func testCreatesDirectoryBasedRunfiles() throws { + let (runfilesDir, clean) = try createMockDirectory(name: "my_custom_runfiles"); defer { try? clean() } + let runfiles = try Runfiles.create( + environment: [ + "RUNFILES_DIR": runfilesDir.path, + "TEST_SRCDIR": "always ignored", + ] + ) + + XCTAssertEqual(try runfiles.rlocation("a/b").path, runfilesDir.path + "/" + "a/b") + XCTAssertEqual(try runfiles.rlocation("foo").path, runfilesDir.path + "/" + "foo") + } + + func testCreatesDirectoryBasedRunfilesEnvVars() throws { + let (runfilesDir, clean) = try createMockDirectory(name: "my_custom_runfiles"); defer { try? clean() } + let runfiles = try Runfiles.create( + environment: [ + "RUNFILES_DIR": runfilesDir.path, + "TEST_SRCDIR": "always ignored", + ] + ) + + XCTAssertEqual(runfiles.envVars(), [ + "RUNFILES_DIR": runfilesDir.path, + ]) + } + + func testFailsToCreateManifestBasedBecauseManifestDoesNotExist() { + XCTAssertNil(try? Runfiles.create( + environment: ["RUNFILES_MANIFEST_FILE": "non-existing path"] + )) + } + + func testManifestBasedRlocation() throws { + let manifestContents = """ + /Foo/runfile1 + Foo/runfile2 /Actual Path/runfile2 + Foo/Bar/runfile3 /the path/run file 3.txt + Foo/Bar/Dir /Actual Path/Directory + """ + let (manifest, clean) = try createMockFile(name: "MANIFEST", contents: manifestContents) + defer { try? clean() } + + let runfiles = try Runfiles.create( + environment: [ + "RUNFILES_MANIFEST_FILE": manifest.path, + "TEST_SRCDIR": "always ignored", + ] + ) + + XCTAssertEqual(try runfiles.rlocation("/Foo/runfile1").path, "/Foo/runfile1") + XCTAssertEqual(try runfiles.rlocation("Foo/runfile2").path, "/Actual Path/runfile2") + XCTAssertEqual(try runfiles.rlocation("Foo/Bar/runfile3").path, "/the path/run file 3.txt") + XCTAssertEqual(try runfiles.rlocation("Foo/Bar/Dir/runfile4").path, "/Actual Path/Directory/runfile4") + XCTAssertEqual( + try runfiles.rlocation("Foo/Bar/Dir/Deeply/Nested/runfile4").path, + "/Actual Path/Directory/Deeply/Nested/runfile4" + ) + XCTAssertNil(try? runfiles.rlocation("unknown")) + + XCTAssertEqual(try runfiles.rlocation("/foo").path, "/foo") + } + + func testManifestBasedRlocationWithRepoMappingFromMain() throws { + let repoMappingContents = """ + ,config.json,config.json~1.2.3 + ,my_module,_main + ,my_protobuf,protobuf~3.19.2 + ,my_workspace,_main + protobuf~3.19.2,config.json,config.json~1.2.3 + protobuf~3.19.2,protobuf,protobuf~3.19.2 + """ + let (repoMapping, cleanRepoMapping) = try createMockFile(name: "_repo_mapping", contents: repoMappingContents) + defer { try? cleanRepoMapping() } + + let manifestContents = """ + _repo_mapping \(repoMapping.path) + config.json /etc/config.json + protobuf~3.19.2/foo/runfile /Actual Path/protobuf/runfile + _main/bar/runfile /the/path/./to/other//other runfile.txt + protobuf~3.19.2/bar/dir /Actual Path/Directory + """ + let (manifest, cleanManifest) = try createMockFile(name: "MANIFEST", contents: manifestContents) + defer { try? cleanManifest() } + + let runfiles = try Runfiles.create( + environment: [ + "RUNFILES_MANIFEST_FILE": manifest.path, + "TEST_SRCDIR": "always ignored", + ] + ) + + XCTAssertEqual( + try runfiles.rlocation("my_module/bar/runfile", sourceRepository: "").path, + "/the/path/./to/other//other runfile.txt" + ) + XCTAssertEqual( + try runfiles.rlocation("my_workspace/bar/runfile", sourceRepository: "").path, + "/the/path/./to/other//other runfile.txt" + ) + XCTAssertEqual( + try runfiles.rlocation("my_protobuf/foo/runfile", sourceRepository: "").path, + "/Actual Path/protobuf/runfile" + ) + XCTAssertEqual(try runfiles.rlocation("my_protobuf/bar/dir", sourceRepository: "").path, "/Actual Path/Directory") + XCTAssertEqual( + try runfiles.rlocation("my_protobuf/bar/dir/file", sourceRepository: "").path, + "/Actual Path/Directory/file" + ) + XCTAssertEqual( + try runfiles.rlocation("my_protobuf/bar/dir/de eply/nes ted/fi~le", sourceRepository: "").path, + "/Actual Path/Directory/de eply/nes ted/fi~le" + ) + + XCTAssertNil(try? runfiles.rlocation("protobuf/foo/runfile")) + XCTAssertNil(try? runfiles.rlocation("protobuf/bar/dir")) + XCTAssertNil(try? runfiles.rlocation("protobuf/bar/dir/file")) + XCTAssertNil(try? runfiles.rlocation("protobuf/bar/dir/dir/de eply/nes ted/fi~le")) + + XCTAssertEqual(try runfiles.rlocation("_main/bar/runfile").path, "/the/path/./to/other//other runfile.txt") + XCTAssertEqual(try runfiles.rlocation("protobuf~3.19.2/foo/runfile").path, "/Actual Path/protobuf/runfile") + XCTAssertEqual(try runfiles.rlocation("protobuf~3.19.2/bar/dir").path, "/Actual Path/Directory") + XCTAssertEqual(try runfiles.rlocation("protobuf~3.19.2/bar/dir/file").path, "/Actual Path/Directory/file") + XCTAssertEqual( + try runfiles.rlocation("protobuf~3.19.2/bar/dir/de eply/nes ted/fi~le").path, + "/Actual Path/Directory/de eply/nes ted/fi~le" + ) + + XCTAssertEqual(try runfiles.rlocation("config.json").path, "/etc/config.json") + XCTAssertNil(try? runfiles.rlocation("_main")) + XCTAssertNil(try? runfiles.rlocation("my_module")) + XCTAssertNil(try? runfiles.rlocation("protobuf")) + } + + func testManifestBasedRlocationWithRepoMappingFromOtherRepo() throws { + let repoMappingContents = """ + ,config.json,config.json~1.2.3 + ,my_module,_main + ,my_protobuf,protobuf~3.19.2 + ,my_workspace,_main + protobuf~3.19.2,config.json,config.json~1.2.3 + protobuf~3.19.2,protobuf,protobuf~3.19.2 + """ + + let (repoMapping, cleanRepoMapping) = try createMockFile(name: "_repo_mapping", contents: repoMappingContents) + defer { try? cleanRepoMapping() } + + let manifestContents = """ + _repo_mapping \(repoMapping.path) + config.json /etc/config.json + protobuf~3.19.2/foo/runfile /Actual Path/protobuf/runfile + _main/bar/runfile /the/path/./to/other//other runfile.txt + protobuf~3.19.2/bar/dir /Actual Path/Directory + """ + let (manifest, cleanManifest) = try createMockFile(name: "mock_manifest", contents: manifestContents) + defer { try? cleanManifest() } + + let runfiles = try Runfiles.create( + sourceRepository: "protobuf~3.19.2", + environment: [ + "RUNFILES_MANIFEST_FILE": manifest.path, + "TEST_SRCDIR": "always ignored", + ] + ) + + XCTAssertEqual(try runfiles.rlocation("protobuf/foo/runfile").path, "/Actual Path/protobuf/runfile") + XCTAssertEqual(try runfiles.rlocation("protobuf/bar/dir").path, "/Actual Path/Directory") + XCTAssertEqual(try runfiles.rlocation("protobuf/bar/dir/file").path, "/Actual Path/Directory/file") + XCTAssertEqual( + try runfiles.rlocation("protobuf/bar/dir/de eply/nes ted/fi~le").path, + "/Actual Path/Directory/de eply/nes ted/fi~le" + ) + + XCTAssertNil(try? runfiles.rlocation("my_module/bar/runfile")) + XCTAssertNil(try? runfiles.rlocation("my_protobuf/foo/runfile")) + XCTAssertNil(try? runfiles.rlocation("my_protobuf/bar/dir")) + XCTAssertNil(try? runfiles.rlocation("my_protobuf/bar/dir/file")) + XCTAssertNil(try? runfiles.rlocation("my_protobuf/bar/dir/de eply/nes ted/fi~le")) + + XCTAssertEqual(try runfiles.rlocation("_main/bar/runfile").path, "/the/path/./to/other//other runfile.txt") + XCTAssertEqual(try runfiles.rlocation("protobuf~3.19.2/foo/runfile").path, "/Actual Path/protobuf/runfile") + XCTAssertEqual(try runfiles.rlocation("protobuf~3.19.2/bar/dir").path, "/Actual Path/Directory") + XCTAssertEqual(try runfiles.rlocation("protobuf~3.19.2/bar/dir/file").path, "/Actual Path/Directory/file") + XCTAssertEqual( + try runfiles.rlocation("protobuf~3.19.2/bar/dir/de eply/nes ted/fi~le").path, + "/Actual Path/Directory/de eply/nes ted/fi~le" + ) + + XCTAssertEqual(try runfiles.rlocation("config.json").path, "/etc/config.json") + XCTAssertNil(try? runfiles.rlocation("_main")) + XCTAssertNil(try? runfiles.rlocation("my_module")) + XCTAssertNil(try? runfiles.rlocation("protobuf")) + } + + func testDirectoryBasedRlocation() throws { + let (runfilesDir, clean) = try createMockDirectory(name: "runfiles_dir") + defer { try? clean() } + + let runfiles = try Runfiles.create( + environment: [ + "RUNFILES_DIR": runfilesDir.path, + ] + ) + + XCTAssertEqual(try runfiles.rlocation("arg").path, runfilesDir.appendingPathComponent("arg").path) + XCTAssertEqual(try runfiles.rlocation("/foo").path, "/foo") + } + + func testDirectoryBasedRlocationWithRepoMappingFromMain() throws { + let repoMappingContents = """ + _,config.json,config.json~1.2.3 + ,my_module,_main + ,my_protobuf,protobuf~3.19.2 + ,my_workspace,_main + protobuf~3.19.2,config.json,config.json~1.2.3 + protobuf~3.19.2,protobuf,protobuf~3.19.2 + """ + let (runfilesDir, clean) = try createMockDirectory(name: "runfiles_dir") + defer { try? clean() } + + let repoMappingFile = runfilesDir.appendingPathComponent("_repo_mapping") + try repoMappingContents.write(to: repoMappingFile, atomically: true, encoding: .utf8) + defer { try? FileManager.default.removeItem(at: repoMappingFile) } + + let runfiles = try Runfiles.create( + environment: [ + "RUNFILES_DIR": runfilesDir.path, + ] + ) + + XCTAssertEqual( + try runfiles.rlocation("my_module/bar/runfile").path, + runfilesDir.appendingPathComponent("_main/bar/runfile").path + ) + XCTAssertEqual( + try runfiles.rlocation("my_workspace/bar/runfile").path, + runfilesDir.appendingPathComponent("_main/bar/runfile").path + ) + XCTAssertEqual( + try runfiles.rlocation("my_protobuf/foo/runfile").path, + runfilesDir.appendingPathComponent("protobuf~3.19.2/foo/runfile").path + ) + XCTAssertEqual( + try runfiles.rlocation("my_protobuf/bar/dir").path, + runfilesDir.appendingPathComponent("protobuf~3.19.2/bar/dir").path + ) + XCTAssertEqual( + try runfiles.rlocation("my_protobuf/bar/dir/file").path, + runfilesDir.appendingPathComponent("protobuf~3.19.2/bar/dir/file").path + ) + XCTAssertEqual( + try runfiles.rlocation("my_protobuf/bar/dir/de eply/nes ted/fi~le").path, + runfilesDir.appendingPathComponent("protobuf~3.19.2/bar/dir/de eply/nes ted/fi~le").path + ) + + XCTAssertEqual( + try runfiles.rlocation("protobuf/foo/runfile").path, + runfilesDir.appendingPathComponent("protobuf/foo/runfile").path + ) + XCTAssertEqual( + try runfiles.rlocation("protobuf/bar/dir/dir/de eply/nes ted/fi~le").path, + runfilesDir.appendingPathComponent("protobuf/bar/dir/dir/de eply/nes ted/fi~le").path + ) + + XCTAssertEqual( + try runfiles.rlocation("_main/bar/runfile").path, + runfilesDir.appendingPathComponent("_main/bar/runfile").path + ) + XCTAssertEqual( + try runfiles.rlocation("protobuf~3.19.2/foo/runfile").path, + runfilesDir.appendingPathComponent("protobuf~3.19.2/foo/runfile").path + ) + XCTAssertEqual( + try runfiles.rlocation("protobuf~3.19.2/bar/dir").path, + runfilesDir.appendingPathComponent("protobuf~3.19.2/bar/dir").path + ) + XCTAssertEqual( + try runfiles.rlocation("protobuf~3.19.2/bar/dir/file").path, + runfilesDir.appendingPathComponent("protobuf~3.19.2/bar/dir/file").path + ) + XCTAssertEqual( + try runfiles.rlocation("protobuf~3.19.2/bar/dir/de eply/nes ted/fi~le").path, + runfilesDir.appendingPathComponent("protobuf~3.19.2/bar/dir/de eply/nes ted/fi~le").path + ) + + XCTAssertEqual(try runfiles.rlocation("config.json").path, runfilesDir.appendingPathComponent("config.json").path) + } + + func testDirectoryBasedRlocationWithRepoMappingFromOtherRepo() throws { + let repoMappingContents = """ + _,config.json,config.json~1.2.3 + ,my_module,_main + ,my_protobuf,protobuf~3.19.2 + ,my_workspace,_main + protobuf~3.19.2,config.json,config.json~1.2.3 + protobuf~3.19.2,protobuf,protobuf~3.19.2 + """ + let (runfilesDir, clean) = try createMockDirectory(name: "runfiles_dir") + defer { try? clean() } + + let repoMappingFile = runfilesDir.appendingPathComponent("_repo_mapping") + try repoMappingContents.write(to: repoMappingFile, atomically: true, encoding: .utf8) + defer { try? FileManager.default.removeItem(at: repoMappingFile) } + + let runfiles = try Runfiles.create( + sourceRepository: "protobuf~3.19.2", + environment: [ + "RUNFILES_DIR": runfilesDir.path, + ] + ) + + XCTAssertEqual( + try runfiles.rlocation("protobuf/foo/runfile").path, + runfilesDir.appendingPathComponent("protobuf~3.19.2/foo/runfile").path + ) + XCTAssertEqual( + try runfiles.rlocation("protobuf/bar/dir").path, + runfilesDir.appendingPathComponent("protobuf~3.19.2/bar/dir").path + ) + XCTAssertEqual( + try runfiles.rlocation("protobuf/bar/dir/file").path, + runfilesDir.appendingPathComponent("protobuf~3.19.2/bar/dir/file").path + ) + XCTAssertEqual( + try runfiles.rlocation("protobuf/bar/dir/de eply/nes ted/fi~le").path, + runfilesDir.appendingPathComponent("protobuf~3.19.2/bar/dir/de eply/nes ted/fi~le").path + ) + + XCTAssertEqual( + try runfiles.rlocation("my_module/bar/runfile").path, + runfilesDir.appendingPathComponent("my_module/bar/runfile").path + ) + XCTAssertEqual( + try runfiles.rlocation("my_protobuf/bar/dir/de eply/nes ted/fi~le").path, + runfilesDir.appendingPathComponent("my_protobuf/bar/dir/de eply/nes ted/fi~le").path + ) + + XCTAssertEqual( + try runfiles.rlocation("_main/bar/runfile").path, + runfilesDir.appendingPathComponent("_main/bar/runfile").path + ) + XCTAssertEqual( + try runfiles.rlocation("protobuf~3.19.2/foo/runfile").path, + runfilesDir.appendingPathComponent("protobuf~3.19.2/foo/runfile").path + ) + XCTAssertEqual( + try runfiles.rlocation("protobuf~3.19.2/bar/dir").path, + runfilesDir.appendingPathComponent("protobuf~3.19.2/bar/dir").path + ) + XCTAssertEqual( + try runfiles.rlocation("protobuf~3.19.2/bar/dir/file").path, + runfilesDir.appendingPathComponent("protobuf~3.19.2/bar/dir/file").path + ) + XCTAssertEqual( + try runfiles.rlocation("protobuf~3.19.2/bar/dir/de eply/nes ted/fi~le").path, + runfilesDir.appendingPathComponent("protobuf~3.19.2/bar/dir/de eply/nes ted/fi~le").path + ) + + XCTAssertEqual(try runfiles.rlocation("config.json").path, runfilesDir.appendingPathComponent("config.json").path) + } + + func testComputeRunfilesPath_withValidManifestFile() throws { + let mockManifestFile = "/path/to/manifest" + let isRunfilesManifest: (String) -> Bool = { $0 == mockManifestFile } + let isRunfilesDirectory: (String) -> Bool = { _ in false } + + let path = try computeRunfilesPath( + argv0: "/path/to/argv0", + manifestFile: mockManifestFile, + runfilesDir: nil, + isRunfilesManifest: isRunfilesManifest, + isRunfilesDirectory: isRunfilesDirectory + ) + XCTAssertEqual(path, RunfilesPath.manifest(mockManifestFile)) + } + + func testComputeRunfilesPath_withValidRunfilesDirectory() throws { + let mockRunfilesDir = "/path/to/runfiles" + let isRunfilesManifest: (String) -> Bool = { _ in false } + let isRunfilesDirectory: (String) -> Bool = { $0 == mockRunfilesDir } + + let path = try computeRunfilesPath( + argv0: "/path/to/argv0", + manifestFile: nil, + runfilesDir: mockRunfilesDir, + isRunfilesManifest: isRunfilesManifest, + isRunfilesDirectory: isRunfilesDirectory + ) + + XCTAssertEqual(path, RunfilesPath.directory(mockRunfilesDir)) + } + + func testComputeRunfilesPath_withInvalidManifestAndDirectory() { + let isRunfilesManifest: (String) -> Bool = { _ in false } + let isRunfilesDirectory: (String) -> Bool = { _ in false } + + XCTAssertThrowsError(try computeRunfilesPath( + argv0: "/path/to/argv0", + manifestFile: "/invalid/manifest", + runfilesDir: "/invalid/runfiles", + isRunfilesManifest: isRunfilesManifest, + isRunfilesDirectory: isRunfilesDirectory + )) + } + + func testComputeRunfilesPath_withWellKnownManifestFile() throws { + let argv0 = "/path/to/argv0" + let isRunfilesManifest: (String) -> Bool = { $0 == "/path/to/argv0.runfiles/MANIFEST" } + let isRunfilesDirectory: (String) -> Bool = { _ in false } + + let path = try computeRunfilesPath( + argv0: argv0, + manifestFile: nil, + runfilesDir: nil, + isRunfilesManifest: isRunfilesManifest, + isRunfilesDirectory: isRunfilesDirectory + ) + + XCTAssertEqual(path, RunfilesPath.manifest("/path/to/argv0.runfiles/MANIFEST")) + } + + func testComputeRunfilesPath_withWellKnownRunfilesDir() throws { + let argv0 = "/path/to/argv0" + let isRunfilesManifest: (String) -> Bool = { _ in false } + let isRunfilesDirectory: (String) -> Bool = { $0 == "/path/to/argv0.runfiles" } + + let path = try computeRunfilesPath( + argv0: argv0, + manifestFile: nil, + runfilesDir: nil, + isRunfilesManifest: isRunfilesManifest, + isRunfilesDirectory: isRunfilesDirectory + ) + + XCTAssertEqual(path, RunfilesPath.directory("/path/to/argv0.runfiles")) + } + + func testComputeRunfilesPath_missingRunfilesLocations() { + let argv0 = "/path/to/argv0" + let isRunfilesManifest: (String) -> Bool = { _ in false } + let isRunfilesDirectory: (String) -> Bool = { _ in false } + + XCTAssertThrowsError(try computeRunfilesPath( + argv0: argv0, + manifestFile: nil, + runfilesDir: nil, + isRunfilesManifest: isRunfilesManifest, + isRunfilesDirectory: isRunfilesDirectory + )) + } +} + +enum RunfilesTestError: Error { + case missingTestTmpDir +} + +func createMockFile(name: String, contents: String) throws -> (URL, () throws -> Void) { + + guard let tmpBaseDirectory = ProcessInfo.processInfo.environment["TEST_TMPDIR"] else { + XCTFail() + throw RunfilesTestError.missingTestTmpDir + } + + let fallbackTempDirectory = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(UUID().uuidString) + let tempDirectory = URL(fileURLWithPath: tmpBaseDirectory).appendingPathComponent(fallbackTempDirectory.lastPathComponent) + let tempFile = tempDirectory.appendingPathComponent(name) + + try FileManager.default.createDirectory(at: tempDirectory, withIntermediateDirectories: true) + try contents.write(to: tempFile, atomically: true, encoding: .utf8) + + return (tempFile, { + try FileManager.default.removeItem(at: tempFile) + try FileManager.default.removeItem(at: tempDirectory) + }) +} + +func createMockDirectory(name _: String) throws -> (URL, () throws -> Void) { + guard let tmpBaseDirectory = ProcessInfo.processInfo.environment["TEST_TMPDIR"] else { + XCTFail() + throw RunfilesTestError.missingTestTmpDir + } + + let fallbackTempDirectory = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(UUID().uuidString) + let tempDirectory = URL(fileURLWithPath: tmpBaseDirectory).appendingPathComponent(fallbackTempDirectory.lastPathComponent) + + try FileManager.default.createDirectory(at: tempDirectory, withIntermediateDirectories: true) + + return (tempDirectory, { + try FileManager.default.removeItem(at: tempDirectory) + }) +} From 343f35ebef603b92eb458b929a94f4ef97338d78 Mon Sep 17 00:00:00 2001 From: Matt Robinson Date: Tue, 11 Mar 2025 11:42:46 -0600 Subject: [PATCH 69/92] Update `swift.suppress_warnings` comment (#1499) --- swift/internal/feature_names.bzl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/swift/internal/feature_names.bzl b/swift/internal/feature_names.bzl index 655324eac..ecd166f8f 100644 --- a/swift/internal/feature_names.bzl +++ b/swift/internal/feature_names.bzl @@ -374,7 +374,7 @@ SWIFT_FEATURE__SUPPORTS_V6 = "swift._supports_v6" # Enabled by default for Swift 5.10+ on macOS. SWIFT_FEATURE_DISABLE_SWIFT_SANDBOX = "swift.disable_swift_sandbox" -# Pass -warnings-as-errors to the compiler. +# Pass -suppress-warnings to the compiler. SWIFT_FEATURE_SUPPRESS_WARNINGS = "swift.suppress_warnings" # Pass -warnings-as-errors to the compiler. From bcd8b999e275fbcfc53a957e6003e7d08bd82398 Mon Sep 17 00:00:00 2001 From: Andrei <1589385+thelvis4@users.noreply.github.com> Date: Mon, 31 Mar 2025 13:37:03 -0700 Subject: [PATCH 70/92] Fix expected path in `//test:private_deps_client_cc_deps_modulemaps` (#1503) --- test/private_deps_tests.bzl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/private_deps_tests.bzl b/test/private_deps_tests.bzl index 88909e1fc..d4856d872 100644 --- a/test/private_deps_tests.bzl +++ b/test/private_deps_tests.bzl @@ -104,7 +104,7 @@ def private_deps_test_suite(name, tags = []): name = "{}_client_cc_deps_modulemaps".format(name), expected_files = [ "/test/fixtures/private_deps/public_cc_modulemap/_/module.modulemap", - "-/test/fixtures/private_deps/private_cc_modulemap_/module.modulemap", + "-/test/fixtures/private_deps/private_cc_modulemap/_/module.modulemap", ], field = "transitive_modules.clang!.module_map!", provider = "SwiftInfo", From 43d5265e607445f75da6858f7b683b4f59ba5fb6 Mon Sep 17 00:00:00 2001 From: Luis Padron Date: Thu, 3 Apr 2025 14:09:54 -0400 Subject: [PATCH 71/92] Update apple_support to latest (#1506) Fixes CI --- MODULE.bazel | 2 +- swift/repositories.bzl | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/MODULE.bazel b/MODULE.bazel index bce62ac41..83c1f295f 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -8,7 +8,7 @@ module( bazel_dep(name = "bazel_features", version = "1.9.0") bazel_dep(name = "bazel_skylib", version = "1.3.0") -bazel_dep(name = "apple_support", version = "1.15.1", repo_name = "build_bazel_apple_support") +bazel_dep(name = "apple_support", version = "1.21.0", repo_name = "build_bazel_apple_support") bazel_dep(name = "rules_cc", version = "0.0.2") bazel_dep(name = "platforms", version = "0.0.9") bazel_dep(name = "protobuf", version = "21.7", repo_name = "com_google_protobuf") diff --git a/swift/repositories.bzl b/swift/repositories.bzl index bcbad3974..c4df993ac 100644 --- a/swift/repositories.bzl +++ b/swift/repositories.bzl @@ -57,8 +57,8 @@ def swift_rules_dependencies(include_bzlmod_ready_dependencies = True): _maybe( http_archive, name = "build_bazel_apple_support", - url = "https://github.com/bazelbuild/apple_support/releases/download/1.15.1/apple_support.1.15.1.tar.gz", - sha256 = "c4bb2b7367c484382300aee75be598b92f847896fb31bbd22f3a2346adf66a80", + url = "https://github.com/bazelbuild/apple_support/releases/download/1.21.0/apple_support.1.21.0.tar.gz", + sha256 = "293f5fe430787f3a995b2703440d27498523df119de00b84002deac9525bea55", ) _maybe( From 8d0de9973e7812b5d7ecefef88a133feece38023 Mon Sep 17 00:00:00 2001 From: Luis Padron Date: Thu, 3 Apr 2025 14:18:09 -0400 Subject: [PATCH 72/92] Support index-import on all Xcode 16.x versions (#1504) This PR updates the index-import dependency to include both version 5.8 and 6.1 as the hash algorithm changed in Swift 6.1. To make this change backwards compatible we switch to the 5.8 version on Xcode 16.2 and under. --- MODULE.bazel | 3 ++- swift/repositories.bzl | 18 ++++++++++++++---- swift/toolchains/xcode_swift_toolchain.bzl | 8 ++++++++ tools/worker/BUILD | 3 ++- tools/worker/worker_main.cc | 15 +++++++++++---- 5 files changed, 37 insertions(+), 10 deletions(-) diff --git a/MODULE.bazel b/MODULE.bazel index 83c1f295f..492d38a72 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -23,7 +23,8 @@ bazel_dep( non_module_deps = use_extension("//swift:extensions.bzl", "non_module_deps") use_repo( non_module_deps, - "build_bazel_rules_swift_index_import", + "build_bazel_rules_swift_index_import_5_8", + "build_bazel_rules_swift_index_import_6_1", "build_bazel_rules_swift_local_config", "com_github_apple_swift_docc_symbolkit", "com_github_apple_swift_log", diff --git a/swift/repositories.bzl b/swift/repositories.bzl index c4df993ac..038f0058b 100644 --- a/swift/repositories.bzl +++ b/swift/repositories.bzl @@ -224,17 +224,27 @@ def swift_rules_dependencies(include_bzlmod_ready_dependencies = True): ), ) - # 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 + # When using the "global index store" feature we rely on `index-import` to allow + # using a global index. + # TODO: we must depend on two versions of index-import to support backwards + # compatibility between Xcode 16.3+ and older versions, we can remove the older + # version once we drop support for Xcode 16.x. _maybe( http_archive, - name = "build_bazel_rules_swift_index_import", + name = "build_bazel_rules_swift_index_import_5_8", build_file = Label("//third_party:build_bazel_rules_swift_index_import/BUILD.overlay"), canonical_id = "index-import-5.8", urls = ["https://github.com/MobileNativeFoundation/index-import/releases/download/5.8.0.1/index-import.tar.gz"], sha256 = "28c1ffa39d99e74ed70623899b207b41f79214c498c603915aef55972a851a15", ) + _maybe( + http_archive, + name = "build_bazel_rules_swift_index_import_6_1", + build_file = Label("//third_party:build_bazel_rules_swift_index_import/BUILD.overlay"), + canonical_id = "index-import-6.1", + urls = ["https://github.com/MobileNativeFoundation/index-import/releases/download/6.1.0/index-import.tar.gz"], + sha256 = "54d0477526bba0dc1560189dfc4f02d90aea536e9cb329e911f32b2a564b66f1", + ) _maybe( swift_autoconfiguration, diff --git a/swift/toolchains/xcode_swift_toolchain.bzl b/swift/toolchains/xcode_swift_toolchain.bzl index 9ca530def..46d260de5 100644 --- a/swift/toolchains/xcode_swift_toolchain.bzl +++ b/swift/toolchains/xcode_swift_toolchain.bzl @@ -688,6 +688,14 @@ def _xcode_swift_toolchain_impl(ctx): requested_features.append(SWIFT_FEATURE__SUPPORTS_V6) env = _xcode_env(target_triple = target_triple, xcode_config = xcode_config) + + # TODO: Remove once we drop support for Xcode 16.x. + # We set a private environment variable when using a version older than Xcode 16.3 + # which comes with Swift 6.1 which changes the hash algorithm for the index-import tool. + # When using an older version we switch to the older version of index-import. + if not _is_xcode_at_least_version(xcode_config, "16.3"): + env["__RULES_SWIFT_USE_LEGACY_INDEX_IMPORT"] = "1" + execution_requirements = xcode_config.execution_info() generated_header_rewriter = ctx.executable.generated_header_rewriter diff --git a/tools/worker/BUILD b/tools/worker/BUILD index 7198958c0..c2c743b63 100644 --- a/tools/worker/BUILD +++ b/tools/worker/BUILD @@ -76,7 +76,8 @@ cc_library( }), data = select({ "@build_bazel_apple_support//configs:apple": [ - "@build_bazel_rules_swift_index_import//:index_import", + "@build_bazel_rules_swift_index_import_5_8//:index_import", + "@build_bazel_rules_swift_index_import_6_1//:index_import", ], "//conditions:default": [], }), diff --git a/tools/worker/worker_main.cc b/tools/worker/worker_main.cc index 59b0a4fb2..004ac6807 100644 --- a/tools/worker/worker_main.cc +++ b/tools/worker/worker_main.cc @@ -17,6 +17,7 @@ #include #include +#include "tools/common/process.h" #include "tools/cpp/runfiles/runfiles.h" #include "tools/worker/compile_with_worker.h" #include "tools/worker/compile_without_worker.h" @@ -31,10 +32,16 @@ int main(int argc, char *argv[]) { std::unique_ptr runfiles(Runfiles::Create(argv[0])); #endif // BAZEL_CURRENT_REPOSITORY if (runfiles != nullptr) { - // We silently ignore errors here, we will report an error later if this - // path is accessed - index_import_path = - runfiles->Rlocation("build_bazel_rules_swift_index_import/index-import"); + // TODO: Remove once we drop support for Xcode 16.x. + // Determine which version of index-import to use based on the environment + auto env = GetCurrentEnvironment(); + if (env.find("__RULES_SWIFT_USE_LEGACY_INDEX_IMPORT") != env.end()) { + index_import_path = runfiles->Rlocation( + "build_bazel_rules_swift_index_import_5_8/index-import"); + } else { + index_import_path = runfiles->Rlocation( + "build_bazel_rules_swift_index_import_6_1/index-import"); + } } auto args = std::vector(argv + 1, argv + argc); From 9785845f6c34bee4fd3479f300cad62fddcab372 Mon Sep 17 00:00:00 2001 From: Jonathan Schear Date: Thu, 10 Apr 2025 14:27:16 -0600 Subject: [PATCH 73/92] Fix documentation of per_module_swiftcopt flag (#1508) --- swift/internal/build_settings.bzl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/swift/internal/build_settings.bzl b/swift/internal/build_settings.bzl index b42affa91..532d94266 100644 --- a/swift/internal/build_settings.bzl +++ b/swift/internal/build_settings.bzl @@ -98,7 +98,7 @@ per_module_swiftcopt_flag = rule( 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. +copts is a comma separated list of Swift copts. """, implementation = _per_module_swiftcopt_flag_impl, ) From 60a34dfba27259432261579f6e96501aeea58f16 Mon Sep 17 00:00:00 2001 From: Brentley Jones Date: Fri, 11 Apr 2025 12:57:02 -0500 Subject: [PATCH 74/92] Use `exec_group` for `SWIFT_ACTION_DERIVE_FILES` and `SWIFT_ACTION_DUMP_AST` (#1510) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Missed when cherry-picking from upstream as they don’t have these actions. Signed-off-by: Brentley Jones --- swift/internal/compiling.bzl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/swift/internal/compiling.bzl b/swift/internal/compiling.bzl index 576e6e7f5..688218273 100644 --- a/swift/internal/compiling.bzl +++ b/swift/internal/compiling.bzl @@ -664,6 +664,7 @@ to use swift_common.compile(include_dev_srch_paths = ...) instead.\ run_toolchain_action( actions = actions, action_name = SWIFT_ACTION_DERIVE_FILES, + exec_group = exec_group, feature_configuration = feature_configuration, outputs = all_derived_outputs, prerequisites = prerequisites, @@ -691,6 +692,7 @@ to use swift_common.compile(include_dev_srch_paths = ...) instead.\ run_toolchain_action( actions = actions, action_name = SWIFT_ACTION_DUMP_AST, + exec_group = exec_group, feature_configuration = feature_configuration, outputs = compile_outputs.ast_files, prerequisites = prerequisites, From d296e13ab1832ac739c4da99f2dc1d69a36789b9 Mon Sep 17 00:00:00 2001 From: Maxwell Elliott Date: Fri, 11 Apr 2025 14:14:48 -0400 Subject: [PATCH 75/92] Add test for swift_binary transitive runfiles (#1507) Add coverage that makes sure that transitive runfiles from swift_libraries are pulled into swift_binary rules --------- Signed-off-by: Maxwell Elliott --- test/BUILD | 3 ++ test/fixtures/runtime_deps/BUILD | 31 +++++++++++++ .../runtime_deps/direct_library.swift | 3 ++ test/fixtures/runtime_deps/main.swift | 1 + .../fixtures/runtime_deps/transitive_data.txt | 1 + .../runtime_deps/transitive_library.swift | 3 ++ test/runtime_deps_tests.bzl | 46 +++++++++++++++++++ 7 files changed, 88 insertions(+) create mode 100644 test/fixtures/runtime_deps/BUILD create mode 100644 test/fixtures/runtime_deps/direct_library.swift create mode 100644 test/fixtures/runtime_deps/main.swift create mode 100644 test/fixtures/runtime_deps/transitive_data.txt create mode 100644 test/fixtures/runtime_deps/transitive_library.swift create mode 100644 test/runtime_deps_tests.bzl diff --git a/test/BUILD b/test/BUILD index adb5f80de..cad3602b8 100644 --- a/test/BUILD +++ b/test/BUILD @@ -18,6 +18,7 @@ load(":output_file_map_tests.bzl", "output_file_map_test_suite") load(":pch_output_dir_tests.bzl", "pch_output_dir_test_suite") load(":private_deps_tests.bzl", "private_deps_test_suite") load(":private_swiftinterface_tests.bzl", "private_swiftinterface_test_suite") +load(":runtime_deps_tests.bzl", "runtime_deps_test_suite") load(":split_derived_files_tests.bzl", "split_derived_files_test_suite") load(":swift_binary_linking_tests.bzl", "swift_binary_linking_test_suite") load(":swift_through_non_swift_tests.bzl", "swift_through_non_swift_test_suite") @@ -75,6 +76,8 @@ utils_test_suite(name = "utils") xctest_runner_test_suite(name = "xctest_runner") +runtime_deps_test_suite(name = "runtime_deps") + test_suite( name = "all_tests", ) diff --git a/test/fixtures/runtime_deps/BUILD b/test/fixtures/runtime_deps/BUILD new file mode 100644 index 000000000..10a5234eb --- /dev/null +++ b/test/fixtures/runtime_deps/BUILD @@ -0,0 +1,31 @@ +load("//swift:swift_binary.bzl", "swift_binary") +load("//swift:swift_library.bzl", "swift_library") +load("//test/fixtures:common.bzl", "FIXTURE_TAGS") + +package( + default_visibility = ["//test:__subpackages__"], +) + +exports_files(["transitive_data.txt"]) + +swift_library( + name = "transitive_library", + srcs = ["transitive_library.swift"], + data = [":transitive_data.txt"], + tags = FIXTURE_TAGS, +) + +swift_library( + name = "direct_library", + srcs = ["direct_library.swift"], + tags = FIXTURE_TAGS, + deps = [":transitive_library"], +) + +swift_binary( + name = "runtime_deps", + srcs = ["main.swift"], + module_name = "binary", + tags = FIXTURE_TAGS, + deps = [":direct_library"], +) diff --git a/test/fixtures/runtime_deps/direct_library.swift b/test/fixtures/runtime_deps/direct_library.swift new file mode 100644 index 000000000..00c7568f8 --- /dev/null +++ b/test/fixtures/runtime_deps/direct_library.swift @@ -0,0 +1,3 @@ +public func foo() -> String { + return "foo" +} diff --git a/test/fixtures/runtime_deps/main.swift b/test/fixtures/runtime_deps/main.swift new file mode 100644 index 000000000..b80e3222a --- /dev/null +++ b/test/fixtures/runtime_deps/main.swift @@ -0,0 +1 @@ +print("hi") diff --git a/test/fixtures/runtime_deps/transitive_data.txt b/test/fixtures/runtime_deps/transitive_data.txt new file mode 100644 index 000000000..312b673e3 --- /dev/null +++ b/test/fixtures/runtime_deps/transitive_data.txt @@ -0,0 +1 @@ +Its some data diff --git a/test/fixtures/runtime_deps/transitive_library.swift b/test/fixtures/runtime_deps/transitive_library.swift new file mode 100644 index 000000000..00c7568f8 --- /dev/null +++ b/test/fixtures/runtime_deps/transitive_library.swift @@ -0,0 +1,3 @@ +public func foo() -> String { + return "foo" +} diff --git a/test/runtime_deps_tests.bzl b/test/runtime_deps_tests.bzl new file mode 100644 index 000000000..45116f031 --- /dev/null +++ b/test/runtime_deps_tests.bzl @@ -0,0 +1,46 @@ +# Copyright 2020 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 `swift_library.generated_header`.""" + +load( + "//test/rules:provider_test.bzl", + "provider_test", +) + +def runtime_deps_test_suite(name, tags = []): + """Test suite for `swift_binary` runtime deps. + + Args: + name: The base name to be used in targets created by this macro. + tags: Additional tags to apply to each test. + """ + all_tags = [name] + tags + + provider_test( + name = "{}_swift_binary_runtime_deps".format(name), + expected_files = [ + "test/fixtures/runtime_deps/transitive_data.txt", + "*", + ], + field = "default_runfiles.files", + provider = "DefaultInfo", + tags = all_tags, + target_under_test = "//test/fixtures/runtime_deps", + ) + + native.test_suite( + name = name, + tags = all_tags, + ) From 8be413e0d9f2a0cb36c2dab53056cc5938b8664f Mon Sep 17 00:00:00 2001 From: Sebastian Shanus Date: Fri, 25 Apr 2025 13:59:04 -0400 Subject: [PATCH 76/92] Add instrumented files info to `swift_binary` and `swift_compiler_plugin` (#1512) When using a `macos_unit_test` target whose test sources depend on a `swift_compiler_plugin` or `swift_binary` target, its sources are missing from the coverage manifest `COVERAGE_MANIFEST` env var that's passed to lcov. This change mirrors what the other targets do to add coverage instrumentation info so that they are available in the resulting coverage file. --- swift/swift_binary.bzl | 6 ++++++ swift/swift_compiler_plugin.bzl | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/swift/swift_binary.bzl b/swift/swift_binary.bzl index effd85006..4c5890025 100644 --- a/swift/swift_binary.bzl +++ b/swift/swift_binary.bzl @@ -204,6 +204,12 @@ def _swift_binary_impl(ctx): files = ctx.files.data, ), ), + coverage_common.instrumented_files_info( + ctx, + dependency_attributes = ["deps"], + extensions = ["swift"], + source_attributes = ["srcs"], + ), OutputGroupInfo(**output_groups), SwiftInfo( modules = [ diff --git a/swift/swift_compiler_plugin.bzl b/swift/swift_compiler_plugin.bzl index d68fbc60b..64915abf4 100644 --- a/swift/swift_compiler_plugin.bzl +++ b/swift/swift_compiler_plugin.bzl @@ -199,6 +199,12 @@ def _swift_compiler_plugin_impl(ctx): OutputGroupInfo( **supplemental_compilation_output_groups(supplemental_outputs) ), + coverage_common.instrumented_files_info( + ctx, + dependency_attributes = ["deps"], + extensions = ["swift"], + source_attributes = ["srcs"], + ), SwiftBinaryInfo( cc_info = CcInfo( compilation_context = module_context.clang.compilation_context, From c9c9d561281fcc5a79c5d07ea99f65e609a34510 Mon Sep 17 00:00:00 2001 From: Luis Padron Date: Tue, 6 May 2025 15:32:05 -0400 Subject: [PATCH 77/92] Update index-import for zlib brew dep fix (#1513) --- swift/repositories.bzl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/swift/repositories.bzl b/swift/repositories.bzl index 038f0058b..51d649f6d 100644 --- a/swift/repositories.bzl +++ b/swift/repositories.bzl @@ -242,8 +242,8 @@ def swift_rules_dependencies(include_bzlmod_ready_dependencies = True): name = "build_bazel_rules_swift_index_import_6_1", build_file = Label("//third_party:build_bazel_rules_swift_index_import/BUILD.overlay"), canonical_id = "index-import-6.1", - urls = ["https://github.com/MobileNativeFoundation/index-import/releases/download/6.1.0/index-import.tar.gz"], - sha256 = "54d0477526bba0dc1560189dfc4f02d90aea536e9cb329e911f32b2a564b66f1", + urls = ["https://github.com/MobileNativeFoundation/index-import/releases/download/6.1.0.1/index-import.tar.gz"], + sha256 = "9a54fc1674af6031125a9884480a1e31e1bcf48b8f558b3e8bcc6b6fcd6e8b61", ) _maybe( From 72ca4a64e5e2db27e903cfe86cb7155b507b1f1a Mon Sep 17 00:00:00 2001 From: Luis Padron Date: Sun, 18 May 2025 06:26:27 -0400 Subject: [PATCH 78/92] Use rules_shell to fix Bazel head CI (#1515) Required explicit dependency in Bazel HEAD. Similar to: https://github.com/bazelbuild/rules_apple/pull/2712 --- MODULE.bazel | 1 + doc/BUILD | 1 + examples/xplatform/custom_swift_proto_compiler/rules/BUILD | 1 + tools/dump_toolchains/BUILD | 2 ++ tools/mixed_language_module_map_extender/BUILD | 2 ++ 5 files changed, 7 insertions(+) diff --git a/MODULE.bazel b/MODULE.bazel index 492d38a72..7288ee8e4 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -10,6 +10,7 @@ bazel_dep(name = "bazel_features", version = "1.9.0") bazel_dep(name = "bazel_skylib", version = "1.3.0") bazel_dep(name = "apple_support", version = "1.21.0", repo_name = "build_bazel_apple_support") bazel_dep(name = "rules_cc", version = "0.0.2") +bazel_dep(name = "rules_shell", version = "0.3.0") bazel_dep(name = "platforms", version = "0.0.9") bazel_dep(name = "protobuf", version = "21.7", repo_name = "com_google_protobuf") bazel_dep(name = "rules_proto", version = "5.3.0-21.7") diff --git a/doc/BUILD b/doc/BUILD index ce78caab3..db0f4b4d8 100644 --- a/doc/BUILD +++ b/doc/BUILD @@ -3,6 +3,7 @@ load("@bazel_skylib//:bzl_library.bzl", "bzl_library") load("@bazel_skylib//rules:diff_test.bzl", "diff_test") load("@bazel_skylib//rules:write_file.bzl", "write_file") load("@io_bazel_stardoc//stardoc:stardoc.bzl", "stardoc") +load("@rules_shell//shell:sh_binary.bzl", "sh_binary") _DOC_SRCS = { "api": [ diff --git a/examples/xplatform/custom_swift_proto_compiler/rules/BUILD b/examples/xplatform/custom_swift_proto_compiler/rules/BUILD index 1c5d53bef..23b262bc5 100644 --- a/examples/xplatform/custom_swift_proto_compiler/rules/BUILD +++ b/examples/xplatform/custom_swift_proto_compiler/rules/BUILD @@ -1,4 +1,5 @@ load("@bazel_skylib//:bzl_library.bzl", "bzl_library") +load("@rules_shell//shell:sh_binary.bzl", "sh_binary") licenses(["notice"]) diff --git a/tools/dump_toolchains/BUILD b/tools/dump_toolchains/BUILD index 595e2c9f6..d5a716b40 100644 --- a/tools/dump_toolchains/BUILD +++ b/tools/dump_toolchains/BUILD @@ -1,3 +1,5 @@ +load("@rules_shell//shell:sh_binary.bzl", "sh_binary") + licenses(["notice"]) sh_binary( diff --git a/tools/mixed_language_module_map_extender/BUILD b/tools/mixed_language_module_map_extender/BUILD index 081fb4754..4b9c6c8ad 100644 --- a/tools/mixed_language_module_map_extender/BUILD +++ b/tools/mixed_language_module_map_extender/BUILD @@ -1,3 +1,5 @@ +load("@rules_shell//shell:sh_binary.bzl", "sh_binary") + licenses(["notice"]) sh_binary( From 291e375a1bcf701548680ef57dfdd5b938b6887c Mon Sep 17 00:00:00 2001 From: Luis Padron Date: Sun, 18 May 2025 13:14:09 -0400 Subject: [PATCH 79/92] Upstream: Only pass the strict Obj-C include paths from _direct_ dependencies to compilation actions (#1516) The `compilation_context_for_explicit_module_compilation` function handles collecting the strict includes from `SwiftInfo`s and we're already using that function when compiling the header module for a mixed-language `swift_library`, so we can simply reuse that logic here. Upstreams: https://github.com/bazelbuild/rules_swift/commit/f08dc0cbe302f907935b71a50d1263c29b8bec08 --- swift/internal/compiling.bzl | 5 ++++- swift/toolchains/config/compile_config.bzl | 16 ---------------- 2 files changed, 4 insertions(+), 17 deletions(-) diff --git a/swift/internal/compiling.bzl b/swift/internal/compiling.bzl index 688218273..3bd00736b 100644 --- a/swift/internal/compiling.bzl +++ b/swift/internal/compiling.bzl @@ -975,7 +975,10 @@ def _precompile_clang_module( prerequisites = struct( bin_dir = feature_configuration._bin_dir, - cc_compilation_context = cc_compilation_context, + cc_compilation_context = compilation_context_for_explicit_module_compilation( + compilation_contexts = [cc_compilation_context], + swift_infos = swift_infos, + ), genfiles_dir = feature_configuration._genfiles_dir, include_dev_srch_paths = False, indexstore_directory = indexstore_directory, diff --git a/swift/toolchains/config/compile_config.bzl b/swift/toolchains/config/compile_config.bzl index c752ad53b..04b64426c 100644 --- a/swift/toolchains/config/compile_config.bzl +++ b/swift/toolchains/config/compile_config.bzl @@ -1486,15 +1486,6 @@ def _c_layering_check_configurator(prerequisites, args): args.add("-Xcc", "-fmodules-strict-decluse") return None -def _clang_module_strict_includes(module_context): - """Returns the strict Clang include paths for a module context.""" - if not module_context.clang: - return None - strict_includes = module_context.clang.strict_includes - if not strict_includes: - return None - return strict_includes.to_list() - def _clang_search_paths_configurator(prerequisites, args): """Adds Clang search paths to the command line.""" args.add_all( @@ -1502,13 +1493,6 @@ def _clang_search_paths_configurator(prerequisites, args): before_each = "-Xcc", format_each = "-I%s", ) - args.add_all( - prerequisites.transitive_modules, - before_each = "-Xcc", - format_each = "-I%s", - map_each = _clang_module_strict_includes, - uniquify = True, - ) # Add Clang search paths for the workspace root and Bazel output roots. The # first allows ClangImporter to find headers included using From 2e812b56565aa8215a458cdc548100e3475c46b6 Mon Sep 17 00:00:00 2001 From: Luis Padron Date: Sun, 18 May 2025 13:15:42 -0400 Subject: [PATCH 80/92] Upstream: Add env attribute to swift_{binary,test} and env_inherit to swift_test (#1517) These attributes provide the same functionality as the same-named attributes on other language rules, like `sh_{binary,test}`. Upstreams: https://github.com/bazelbuild/rules_swift/commit/64c410faf754b05cf78258eb7028e63d3837a9cc --- doc/rules.md | 10 ++-- swift/internal/binary_attrs.bzl | 19 +++++++- swift/internal/utils.bzl | 15 +++++- swift/swift_binary.bzl | 7 +++ swift/swift_compiler_plugin.bzl | 7 ++- swift/swift_test.bzl | 16 +++++- test/BUILD | 3 ++ test/environment_tests.bzl | 70 +++++++++++++++++++++++++++ test/fixtures/environment/BUILD | 37 ++++++++++++++ test/rules/expected_files.bzl | 86 +++++++++++++++++++++++++++++++++ test/rules/provider_test.bzl | 32 ++++++++++++ 11 files changed, 293 insertions(+), 9 deletions(-) create mode 100644 test/environment_tests.bzl create mode 100644 test/fixtures/environment/BUILD diff --git a/doc/rules.md b/doc/rules.md index ddbba2091..0a76612b3 100644 --- a/doc/rules.md +++ b/doc/rules.md @@ -41,8 +41,8 @@ On this page: ## swift_binary
-swift_binary(name, deps, srcs, data, copts, defines, linkopts, malloc, module_name, package_name,
-             plugins, stamp, swiftc_inputs)
+swift_binary(name, deps, srcs, data, copts, defines, env, linkopts, malloc, module_name,
+             package_name, plugins, stamp, swiftc_inputs)
 
Compiles and links Swift code into an executable binary. @@ -70,6 +70,7 @@ please use one of the platform-specific application rules in | 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 | `[]` | | 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 | `[]` | | 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 | `[]` | +| env | Specifies additional environment variables to set when the test is executed by `bazel run` or `bazel test`.

The values of these environment variables are subject to `$(location)` and "Make variable" substitution.

NOTE: The environment variables are not set when you run the target outside of Bazel (for example, by manually executing the binary in `bazel-bin/`). | Dictionary: String -> String | 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 | `""` | @@ -920,8 +921,8 @@ swift_binary( ## swift_test
-swift_test(name, deps, srcs, data, copts, defines, discover_tests, env, linkopts, malloc,
-           module_name, package_name, plugins, stamp, swiftc_inputs)
+swift_test(name, deps, srcs, data, copts, defines, discover_tests, env, env_inherit, linkopts,
+           malloc, module_name, package_name, plugins, stamp, swiftc_inputs)
 
Compiles and links Swift code into an executable test target. @@ -999,6 +1000,7 @@ root of your workspace (i.e. `$(SRCROOT)`). | 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 | `[]` | | discover_tests | Determines whether or not tests are automatically discovered in the binary. The default value is `True`.

Tests are discovered in a platform-specific manner. On Apple platforms, they are found using the XCTest framework's `XCTestSuite.default` accessor, which uses the Objective-C runtime to dynamically discover tests. On non-Apple platforms, discovery uses symbol graphs generated from dependencies to find classes and methods written in XCTest's style.

If tests are discovered, then you should not provide your own `main` entry point in the `swift_test` binary; the test runtime provides the entry point for you. If you set this attribute to `False`, then you are responsible for providing your own `main`. This allows you to write tests that use a framework other than Apple's `XCTest`. The only requirement of such a test is that it terminate with a zero exit code for success or a non-zero exit code for failure. | Boolean | optional | `True` | | env | Dictionary of environment variables that should be set during the test execution. | Dictionary: String -> String | optional | `{}` | +| env_inherit | Specifies additional environment variables to inherit from the external environment when the test is executed by `bazel test`. | List of strings | 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 | `""` | diff --git a/swift/internal/binary_attrs.bzl b/swift/internal/binary_attrs.bzl index 425ae804f..7377b640a 100644 --- a/swift/internal/binary_attrs.bzl +++ b/swift/internal/binary_attrs.bzl @@ -33,6 +33,7 @@ def binary_rule_attrs( *, additional_deps_aspects = [], additional_deps_providers = [], + exclude_env = False, stamp_default): """Returns attributes common to both `swift_binary` and `swift_test`. @@ -42,12 +43,13 @@ def binary_rule_attrs( additional_deps_providers: A list of lists representing additional providers that should be allowed by the `deps` attribute of the rule. + exclude_env: Whether to exclude the `env` attribute. stamp_default: The default value of the `stamp` attribute. Returns: A `dict` of attributes for a binary or test rule. """ - return dicts.add( + result = dicts.add( swift_compilation_attrs( additional_deps_aspects = [ swift_clang_module_aspect, @@ -103,3 +105,18 @@ into the binary. Possible values are: ), }, ) + if not exclude_env: + result["env"] = attr.string_dict( + doc = """\ +Specifies additional environment variables to set when the test is executed by +`bazel run` or `bazel test`. + +The values of these environment variables are subject to `$(location)` and +"Make variable" substitution. + +NOTE: The environment variables are not set when you run the target outside of +Bazel (for example, by manually executing the binary in `bazel-bin/`). +""", + ) + + return result diff --git a/swift/internal/utils.bzl b/swift/internal/utils.bzl index 6a54c3ef9..270637376 100644 --- a/swift/internal/utils.bzl +++ b/swift/internal/utils.bzl @@ -102,13 +102,24 @@ def expand_locations(ctx, values, targets = []): Args: ctx: The rule context. - values: A list of strings, which may contain `$(location)` placeholders. + values: A list or dictionary of strings. If it is a list, each element + is assumed to be a string that may contain `$(location)` + placeholders. If it is a dictionary, each value is assumed to be a + string that may contain `$(location)` placeholders. (The keys of a + dictionary are not substituted.) targets: A list of additional targets (other than the calling rule's `deps`) that should be searched for substitutable labels. Returns: - A list of strings with any `$(location)` placeholders filled in. + A list or dictionary of strings with any `$(location)` placeholders + filled in. """ + if type(values) == "dict": + return { + key: ctx.expand_location(value, targets) + for key, value in values.items() + } + return [ctx.expand_location(value, targets) for value in values] def expand_make_variables(ctx, values, attribute_name): diff --git a/swift/swift_binary.bzl b/swift/swift_binary.bzl index 4c5890025..b67589b1e 100644 --- a/swift/swift_binary.bzl +++ b/swift/swift_binary.bzl @@ -222,6 +222,13 @@ def _swift_binary_impl(ctx): for module_context in module_contexts ], ), + RunEnvironmentInfo( + environment = expand_locations( + ctx, + ctx.attr.env, + ctx.attr.swiftc_inputs, + ), + ), ] # Only create a linking context and propagate `SwiftBinaryInfo` if this rule diff --git a/swift/swift_compiler_plugin.bzl b/swift/swift_compiler_plugin.bzl index 64915abf4..765a6dcbc 100644 --- a/swift/swift_compiler_plugin.bzl +++ b/swift/swift_compiler_plugin.bzl @@ -221,7 +221,12 @@ def _swift_compiler_plugin_impl(ctx): swift_compiler_plugin = rule( attrs = dicts.add( # Do not stamp macro binaries by default to prevent frequent rebuilds. - binary_rule_attrs(stamp_default = 0), + binary_rule_attrs( + exclude_env = True, + # Do not stamp macro binaries by default to prevent frequent + # rebuilds. + stamp_default = 0, + ), ), doc = """\ Compiles and links a Swift compiler plugin (for example, a macro). diff --git a/swift/swift_test.bzl b/swift/swift_test.bzl index 66332ecce..16359f6cf 100644 --- a/swift/swift_test.bzl +++ b/swift/swift_test.bzl @@ -487,6 +487,7 @@ def _swift_test_impl(ctx): ) test_environment = dicts.add( + ctx.attr.env, swift_toolchain.test_configuration.env, {"TEST_BINARIES_FOR_LLVM_COV": linking_outputs.executable.short_path}, expanded_env.get_expanded_env(ctx, {}), @@ -528,7 +529,14 @@ def _swift_test_impl(ctx): testing.ExecutionInfo( swift_toolchain.test_configuration.execution_requirements, ), - testing.TestEnvironment(test_environment), + RunEnvironmentInfo( + environment = expand_locations( + ctx, + test_environment, + ctx.attr.swiftc_inputs, + ), + inherited_environment = ctx.attr.env_inherit, + ), ] swift_test = rule( @@ -565,6 +573,12 @@ a zero exit code for success or a non-zero exit code for failure. Dictionary of environment variables that should be set during the test execution. """, ), + "env_inherit": attr.string_list( + doc = """\ +Specifies additional environment variables to inherit from the external +environment when the test is executed by `bazel test`. +""", + ), "_apple_coverage_support": attr.label( cfg = "exec", default = Label( diff --git a/test/BUILD b/test/BUILD index cad3602b8..ecc8a61ff 100644 --- a/test/BUILD +++ b/test/BUILD @@ -6,6 +6,7 @@ load(":compiler_arguments_tests.bzl", "compiler_arguments_test_suite") load(":const_values_tests.bzl", "const_values_test_suite") load(":coverage_settings_tests.bzl", "coverage_settings_test_suite") load(":debug_settings_tests.bzl", "debug_settings_test_suite") +load(":environment_tests.bzl", "environment_test_suite") load(":features_tests.bzl", "features_test_suite") load(":generated_header_tests.bzl", "generated_header_test_suite") load(":imported_framework_tests.bzl", "imported_framework_test_suite") @@ -40,6 +41,8 @@ coverage_settings_test_suite(name = "coverage_settings") debug_settings_test_suite(name = "debug_settings") +environment_test_suite(name = "environment") + features_test_suite(name = "features") generated_header_test_suite(name = "generated_header") diff --git a/test/environment_tests.bzl b/test/environment_tests.bzl new file mode 100644 index 000000000..e59404559 --- /dev/null +++ b/test/environment_tests.bzl @@ -0,0 +1,70 @@ +# Copyright 2025 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 environment attributes on `swift_{binary,test}.""" + +load( + "@build_bazel_rules_swift//test/rules:provider_test.bzl", + "provider_test", +) + +visibility("private") + +def environment_test_suite(name, tags = []): + """Test suite for environment attributes on `swift_{binary,test}`. + + Args: + name: The base name to be used in targets created by this macro. + tags: Additional tags to apply to each test. + """ + all_tags = [name] + tags + + provider_test( + name = "{}_binary_environment_is_set".format(name), + expected_values = [ + "TEST_ENV_VAR=test-value", + ], + field = "environment", + provider = "RunEnvironmentInfo", + tags = all_tags, + target_under_test = "@build_bazel_rules_swift//test/fixtures/environment:binary", + ) + + provider_test( + name = "{}_test_environment_is_set".format(name), + expected_values = [ + "TEST_ENV_VAR=test-value", + "*", # Will include TEST_BINARIES_FOR_LLVM_COV + ], + field = "environment", + provider = "RunEnvironmentInfo", + tags = all_tags, + target_under_test = "@build_bazel_rules_swift//test/fixtures/environment:test", + ) + + provider_test( + name = "{}_test_env_inherit_is_set".format(name), + expected_values = [ + "HOME", + ], + field = "inherited_environment", + provider = "RunEnvironmentInfo", + tags = all_tags, + target_under_test = "@build_bazel_rules_swift//test/fixtures/environment:test", + ) + + native.test_suite( + name = name, + tags = all_tags, + ) diff --git a/test/fixtures/environment/BUILD b/test/fixtures/environment/BUILD new file mode 100644 index 000000000..37e542e0a --- /dev/null +++ b/test/fixtures/environment/BUILD @@ -0,0 +1,37 @@ +load( + "//swift:swift_binary.bzl", + "swift_binary", +) +load( + "//swift:swift_test.bzl", + "swift_test", +) +load("//test/fixtures:common.bzl", "FIXTURE_TAGS") + +package( + default_visibility = ["//test:__subpackages__"], +) + +licenses(["notice"]) + +############################################################################### +# Fixtures for testing environment propagation of binaries and tests. + +swift_binary( + name = "binary", + srcs = ["Binary.swift"], + env = { + "TEST_ENV_VAR": "test-value", + }, + tags = FIXTURE_TAGS, +) + +swift_test( + name = "test", + srcs = ["Test.swift"], + env = { + "TEST_ENV_VAR": "test-value", + }, + env_inherit = ["HOME"], + tags = FIXTURE_TAGS, +) diff --git a/test/rules/expected_files.bzl b/test/rules/expected_files.bzl index bac973ec2..13d991e29 100644 --- a/test/rules/expected_files.bzl +++ b/test/rules/expected_files.bzl @@ -57,6 +57,8 @@ def normalize_collection(object): """ if types.is_depset(object): return object.to_list() + elif types.is_dict(object): + return ["{}={}".format(k, v) for k, v in object.items()] else: return object @@ -153,3 +155,87 @@ def compare_expected_files(env, access_description, expected, actual): ]), ), ) + +def compare_expected_strings(env, access_description, expected, actual): + """Implements the `expected_strings` comparison. + + This compares a list or dictionary of strings retrieved from a provider + field against a list of expected strings, as well as excluded strings and a + wildcard. See the documentation of the `expected_values` attribute on the + rule definition below for specifics. + + Args: + env: The analysis test environment. + access_description: A target/provider/field access description string + printed in assertion failure messages. + expected: The list of expected string inclusions/exclusions. + actual: The collection of strings obtained from the provider. + """ + if not (types.is_list(actual) or types.is_dict(actual)): + unittest.fail( + env, + ("Expected '{}' to be a list or dict of strings, " + + "but got a {}: {}.").format( + access_description, + type(actual), + _prettify(actual), + ), + ) + return + + actual = normalize_collection(actual) + remaining = list(actual) + + expected_is_subset = "*" in expected + expected_include = [ + s + for s in expected + if not s.startswith("-") and s != "*" + ] + expected_exclude = [s[1:] for s in expected if s.startswith("-")] + + # For every expected string, pick off the first actual that we find that is + # equal to it. + failed = False + for current in expected_include: + if not remaining: + # It's a failure if we are still expecting strings but there are no + # more actual strings. + failed = True + break + + found_expected_string = False + for i in range(len(remaining)): + actual_string = remaining[i] + if actual_string == current: + found_expected_string = True + remaining.pop(i) + break + + # It's a failure if we never found a string we expected. + if not found_expected_string: + failed = True + break + + # For every string expected to *not* be present, check the list of remaining + # strings and fail if we find a match. + for current in expected_exclude: + for r in remaining: + if r == current: + failed = True + break + + # If we found all the expected strings, the remaining list should be empty. + # Fail if the list is not empty and we're not looking for a subset. + if not expected_is_subset and remaining: + failed = True + + asserts.false( + env, + failed, + "Expected '{}' to match {}, but got {}.".format( + access_description, + _prettify(expected), + _prettify([repr(v) for v in actual]), + ), + ) diff --git a/test/rules/provider_test.bzl b/test/rules/provider_test.bzl index ac6152e8f..37a528b66 100644 --- a/test/rules/provider_test.bzl +++ b/test/rules/provider_test.bzl @@ -24,6 +24,7 @@ load("//swift:providers.bzl", "SwiftInfo") load( "//test/rules:expected_files.bzl", "compare_expected_files", + "compare_expected_strings", "normalize_collection", ) @@ -161,6 +162,8 @@ def _lookup_provider_by_name(env, target, provider_name): provider = DefaultInfo elif provider_name == "OutputGroupInfo": provider = OutputGroupInfo + elif provider_name == "RunEnvironmentInfo": + provider = RunEnvironmentInfo elif provider_name == "SwiftInfo": provider = SwiftInfo @@ -256,6 +259,13 @@ def _provider_test_impl(ctx): ctx.attr.expected_files, actual, ) + elif ctx.attr.expected_values: + compare_expected_strings( + env, + access_description, + ctx.attr.expected_values, + actual, + ) return analysistest.end(env) @@ -284,6 +294,7 @@ Currently, only the following providers are recognized: * `CcInfo` * `DefaultInfo` * `OutputGroupInfo` +* `RunEnvironmentInfo` * `SwiftInfo` """, ), @@ -305,6 +316,26 @@ This list can contain three types of strings: The use of path suffixes allows the test to be unconcerned about specific configuration details, such as output directories for generated files. +""", + ), + "expected_values": attr.string_list( + mandatory = False, + doc = """\ +The expected list of values when evaluating the given provider's field. + +This list can contain three types of strings: + +* A string (e.g., `foo`), which indicates a value that must be present. +* A negated string (`-foo`), denoting that a string that must *not* be + present. +* A wildcard (`*`), denoting that the expected list of strings can be a + *subset* of the actual list. If the wildcard is omitted, the expected list + of strings must match the actual list completely; unmatched strings will + result in a test failure. + +This attribute also supports string dictionary attributes. In this case, each +key and value will be concatenated into a single string of the form `key=value` +and this form is what should be specified in the `expected_values` list. """, ), "field": attr.string( @@ -336,6 +367,7 @@ Currently, only the following providers are recognized: * `CcInfo` * `DefaultInfo` * `OutputGroupInfo` +* `RunEnvironmentInfo` * `SwiftInfo` """, ), From 9079e7224e067603cab0415a59e821bad752a015 Mon Sep 17 00:00:00 2001 From: Luis Padron Date: Mon, 19 May 2025 22:38:46 -0400 Subject: [PATCH 81/92] Upstream: Add a feature to allow certain targets to ignore the implicit dependencies from the toolchain. (#1518) If enabled, the toolchain's implicit dependencies will not be used when compiling Swift or Clang modules. This should only be used when building the toolchain itself. Upstreams: https://github.com/bazelbuild/rules_swift/commit/c29e0ab6b49d3812a07bede821f8f746878eb7d0 --- swift/BUILD | 3 +- swift/internal/BUILD | 1 + swift/internal/compiling.bzl | 31 ++++++++++++-------- swift/internal/feature_names.bzl | 5 ++++ swift/internal/linking.bzl | 13 +++++++-- swift/internal/utils.bzl | 50 ++++++++++++++++++++++++++++++++ 6 files changed, 88 insertions(+), 15 deletions(-) diff --git a/swift/BUILD b/swift/BUILD index 348c80e67..75610303f 100644 --- a/swift/BUILD +++ b/swift/BUILD @@ -161,10 +161,11 @@ bzl_library( srcs = ["swift_import.bzl"], deps = [ ":providers", + ":swift_clang_module_aspect", "//swift/internal:attrs", "//swift/internal:compiling", "//swift/internal:features", - "//swift/internal:linking", + "//swift/internal:providers", "//swift/internal:toolchain_utils", "//swift/internal:utils", "@bazel_skylib//lib:dicts", diff --git a/swift/internal/BUILD b/swift/internal/BUILD index 4806e901c..d795d1086 100644 --- a/swift/internal/BUILD +++ b/swift/internal/BUILD @@ -177,6 +177,7 @@ bzl_library( ":developer_dirs", ":feature_names", ":features", + ":utils", "//swift:providers", ], ) diff --git a/swift/internal/compiling.bzl b/swift/internal/compiling.bzl index 3bd00736b..935c97c47 100644 --- a/swift/internal/compiling.bzl +++ b/swift/internal/compiling.bzl @@ -71,6 +71,8 @@ load( ":utils.bzl", "compact", "compilation_context_for_explicit_module_compilation", + "get_clang_implicit_deps", + "get_swift_implicit_deps", "merge_compilation_contexts", "owner_relative_path", "struct_fields", @@ -182,16 +184,18 @@ def compile_module_interface( """ swiftmodule_file = actions.declare_file("{}.swiftmodule".format(module_name)) + implicit_swift_infos, implicit_cc_infos = get_swift_implicit_deps( + feature_configuration = feature_configuration, + swift_toolchain = swift_toolchain, + ) merged_compilation_context = merge_compilation_contexts( transitive_compilation_contexts = compilation_contexts + [ cc_info.compilation_context - for cc_info in swift_toolchain.implicit_deps_providers.cc_infos + for cc_info in implicit_cc_infos ], ) merged_swift_info = SwiftInfo( - swift_infos = ( - swift_infos + swift_toolchain.implicit_deps_providers.swift_infos - ), + swift_infos = swift_infos + implicit_swift_infos, ) # Flattening this `depset` is necessary because we need to extract the @@ -461,10 +465,12 @@ def compile( user_swift_infos = swift_infos + private_swift_infos, ) + implicit_swift_infos, implicit_cc_infos = get_swift_implicit_deps( + feature_configuration = feature_configuration, + swift_toolchain = swift_toolchain, + ) all_swift_infos = ( - swift_infos_to_propagate + - private_swift_infos + - swift_toolchain.implicit_deps_providers.swift_infos + swift_infos_to_propagate + private_swift_infos + implicit_swift_infos ) merged_swift_info = SwiftInfo(swift_infos = all_swift_infos) @@ -542,7 +548,7 @@ def compile( ] merged_cc_info = cc_common.merge_cc_infos( cc_infos = cc_infos + private_cc_infos + - swift_toolchain.implicit_deps_providers.cc_infos, + implicit_cc_infos, ) transitive_swiftmodules = [] @@ -932,18 +938,19 @@ def _precompile_clang_module( ) if not is_swift_generated_header: - implicit_swift_infos = ( - swift_toolchain.clang_implicit_deps_providers.swift_infos + implicit_swift_infos, implicit_cc_infos = get_clang_implicit_deps( + feature_configuration = feature_configuration, + swift_toolchain = swift_toolchain, ) cc_compilation_context = merge_compilation_contexts( direct_compilation_contexts = [cc_compilation_context], transitive_compilation_contexts = [ cc_info.compilation_context - for cc_info in swift_toolchain.clang_implicit_deps_providers.cc_infos + for cc_info in implicit_cc_infos ], ) else: - implicit_swift_infos = [] + implicit_swift_infos, _ = [], [] if not is_swift_generated_header and implicit_swift_infos: swift_infos = list(swift_infos) diff --git a/swift/internal/feature_names.bzl b/swift/internal/feature_names.bzl index ecd166f8f..208ccd114 100644 --- a/swift/internal/feature_names.bzl +++ b/swift/internal/feature_names.bzl @@ -38,6 +38,11 @@ SWIFT_FEATURE_OPT = "swift.opt" # Swift 6 features even before switching to a Swift 6 compiler. SWIFT_FEATURE_ENABLE_V6 = "swift.enable_v6" +# If enabled, the toolchain's implicit dependencies will not be used when +# compiling Swift or Clang modules. This should only be used when building the +# toolchain itself. +SWIFT_FEATURE_NO_IMPLICIT_DEPS = "swift.no_implicit_deps" + # If True, transitive C headers will be always be passed as inputs to Swift # compilation actions, even when building with explicit modules. SWIFT_FEATURE_HEADERS_ALWAYS_ACTION_INPUTS = "swift.headers_always_action_inputs" diff --git a/swift/internal/linking.bzl b/swift/internal/linking.bzl index 84195912f..d486b3bdd 100644 --- a/swift/internal/linking.bzl +++ b/swift/internal/linking.bzl @@ -35,6 +35,7 @@ load( "get_cc_feature_configuration", "is_feature_enabled", ) +load(":utils.bzl", "get_swift_implicit_deps") def configure_features_for_binary( *, @@ -244,9 +245,13 @@ def create_linking_context_from_compilation_outputs( context to be propagated by the caller's `CcInfo` provider and the artifact representing the library that was linked, respectively. """ + _, implicit_cc_infos = get_swift_implicit_deps( + feature_configuration = feature_configuration, + swift_toolchain = swift_toolchain, + ) extra_linking_contexts = [ cc_info.linking_context - for cc_info in swift_toolchain.implicit_deps_providers.cc_infos + for cc_info in implicit_cc_infos ] debugging_linking_context = _create_embedded_debugging_linking_context( @@ -481,9 +486,13 @@ def register_link_binary_action( # Collect linking contexts from any of the toolchain's implicit # dependencies. + _, implicit_cc_infos = get_swift_implicit_deps( + feature_configuration = feature_configuration, + swift_toolchain = swift_toolchain, + ) linking_contexts.extend([ cc_info.linking_context - for cc_info in swift_toolchain.implicit_deps_providers.cc_infos + for cc_info in implicit_cc_infos ]) return cc_common.link( diff --git a/swift/internal/utils.bzl b/swift/internal/utils.bzl index 270637376..bd17315d5 100644 --- a/swift/internal/utils.bzl +++ b/swift/internal/utils.bzl @@ -16,6 +16,8 @@ load("@bazel_skylib//lib:paths.bzl", "paths") load("//swift:providers.bzl", "SwiftInfo") +load(":feature_names.bzl", "SWIFT_FEATURE_NO_IMPLICIT_DEPS") +load(":features.bzl", "is_feature_enabled") def collect_implicit_deps_providers(targets, additional_cc_infos = []): """Returns a struct with important providers from a list of implicit deps. @@ -393,3 +395,51 @@ def include_developer_search_paths(attr): "always_include_developer_search_paths", False, ) + +def get_swift_implicit_deps(*, feature_configuration, swift_toolchain): + """Returns the Swift and C++ providers for implicit Swift dependencies. + + Args: + feature_configuration: A feature configuration obtained from + `swift_common.configure_features`. If this feature configuration is + such that implicit dependencies should be ignored, this function + returns an empty list for both providers. + swift_toolchain: The `SwiftToolchainInfo` provider of the toolchain. + + Returns: + A tuple `(list[SwiftInfo], list[CcInfo])`. + """ + if is_feature_enabled( + feature_configuration = feature_configuration, + feature_name = SWIFT_FEATURE_NO_IMPLICIT_DEPS, + ): + return [], [] + else: + return ( + swift_toolchain.implicit_deps_providers.swift_infos, + swift_toolchain.implicit_deps_providers.cc_infos, + ) + +def get_clang_implicit_deps(*, feature_configuration, swift_toolchain): + """Returns the Swift and C++ providers for implicit Clang dependencies. + + Args: + feature_configuration: A feature configuration obtained from + `swift_common.configure_features`. If this feature configuration is + such that implicit dependencies should be ignored, this function + returns an empty list for both providers. + swift_toolchain: The `SwiftToolchainInfo` provider of the toolchain. + + Returns: + A tuple `(list[SwiftInfo], list[CcInfo])`. + """ + if is_feature_enabled( + feature_configuration = feature_configuration, + feature_name = SWIFT_FEATURE_NO_IMPLICIT_DEPS, + ): + return [], [] + else: + return ( + swift_toolchain.clang_implicit_deps_providers.swift_infos, + swift_toolchain.clang_implicit_deps_providers.cc_infos, + ) From ec9283b5d4357ff42f2340ec3908509a0a3ffb56 Mon Sep 17 00:00:00 2001 From: Luis Padron Date: Thu, 22 May 2025 11:58:35 -0400 Subject: [PATCH 82/92] Upstream: use a different attribute than features on toolchain rules to specify which features should be enabled/disabled by default for all targets built with that toolchain (#1519) Using `ctx.features` has meant that features specified at the *package* level in the package that contains a toolchain (and may also contain other targets) would have those features applied to any feature configuration created with the toolchain. In other words, features specified on a toolchain's package would apply to all Swift targets anywhere. Avoid that by using attributes that don't collide with the existing feature algebra. Upstream: - https://github.com/bazelbuild/rules_swift/commit/f7a6fb4326d949d14db0be1cd51c4e55b4b77f46 - https://github.com/bazelbuild/rules_swift/commit/58b6f2123d983a47c4a766c2564627213ce03c75 --- doc/api.md | 6 ++++-- swift/internal/toolchain_utils.bzl | 20 ++++++++++++++------ swift/swift_import.bzl | 7 ++++--- swift/toolchains/xcode_swift_toolchain.bzl | 21 ++++++++++++++++++--- 4 files changed, 40 insertions(+), 14 deletions(-) diff --git a/doc/api.md b/doc/api.md index 655ed1deb..c25a7aa37 100644 --- a/doc/api.md +++ b/doc/api.md @@ -434,7 +434,7 @@ Extracts the symbol graph from a Swift module. ## swift_common.get_toolchain
-swift_common.get_toolchain(ctx, exec_group, mandatory, attr)
+swift_common.get_toolchain(ctx, exec_group, mandatory, toolchain_type, attr)
 
Gets the Swift toolchain associated with the rule or aspect. @@ -447,6 +447,7 @@ Gets the Swift toolchain associated with the rule or aspect. | ctx | The rule or aspect context. | none | | exec_group | The name of the execution group that should contain the toolchain. If this is provided and the toolchain is not declared in that execution group, it will be looked up from `ctx` as a fallback instead. If this argument is `None` (the default), then the toolchain will only be looked up from `ctx.` | `None` | | mandatory | If `False`, this function will return `None` instead of failing if no toolchain is found. Defaults to `True`. | `True` | +| toolchain_type | The toolchain type to use. Defaults to the standard Swift toolchain type. | `"@build_bazel_rules_swift//toolchains:toolchain_type"` | | attr | The name of the attribute on the calling rule or aspect that should be used to retrieve the toolchain if it is not provided by the `toolchains` argument of the rule/aspect. Note that this is only supported for legacy/migration purposes and will be removed once migration to toolchains is complete. | `"_toolchain"` | **RETURNS** @@ -521,7 +522,7 @@ A struct containing the precompiled module and optional indexstore directory, ## swift_common.use_toolchain
-swift_common.use_toolchain(mandatory)
+swift_common.use_toolchain(mandatory, toolchain_type)
 
Returns a list of toolchain types needed to use the Swift toolchain. @@ -541,6 +542,7 @@ toolchains = use_swift_toolchain() + [other toolchains...] | Name | Description | Default Value | | :------------- | :------------- | :------------- | | mandatory | Whether or not it should be an error if the toolchain cannot be resolved. Defaults to True. | `True` | +| toolchain_type | The toolchain type to use. Defaults to the standard Swift toolchain type. | `"@build_bazel_rules_swift//toolchains:toolchain_type"` | **RETURNS** diff --git a/swift/internal/toolchain_utils.bzl b/swift/internal/toolchain_utils.bzl index 448789c09..2b0fbe375 100644 --- a/swift/internal/toolchain_utils.bzl +++ b/swift/internal/toolchain_utils.bzl @@ -21,6 +21,7 @@ def get_swift_toolchain( *, exec_group = None, mandatory = True, + toolchain_type = SWIFT_TOOLCHAIN_TYPE, attr = "_toolchain"): """Gets the Swift toolchain associated with the rule or aspect. @@ -33,6 +34,8 @@ def get_swift_toolchain( toolchain will only be looked up from `ctx.` mandatory: If `False`, this function will return `None` instead of failing if no toolchain is found. Defaults to `True`. + toolchain_type: The toolchain type to use. Defaults to the standard + Swift toolchain type. attr: The name of the attribute on the calling rule or aspect that should be used to retrieve the toolchain if it is not provided by the `toolchains` argument of the rule/aspect. Note that this is only @@ -45,11 +48,11 @@ def get_swift_toolchain( """ if exec_group: group = ctx.exec_groups[exec_group] - if group and SWIFT_TOOLCHAIN_TYPE in group.toolchains: - return group.toolchains[SWIFT_TOOLCHAIN_TYPE].swift_toolchain + if group and toolchain_type in group.toolchains: + return group.toolchains[toolchain_type].swift_toolchain - if SWIFT_TOOLCHAIN_TYPE in ctx.toolchains: - return ctx.toolchains[SWIFT_TOOLCHAIN_TYPE].swift_toolchain + if toolchain_type in ctx.toolchains: + return ctx.toolchains[toolchain_type].swift_toolchain # TODO(b/205018581): Delete this code path when migration to the new # toolchain APIs is complete. @@ -64,7 +67,10 @@ def get_swift_toolchain( return None -def use_swift_toolchain(*, mandatory = True): +def use_swift_toolchain( + *, + mandatory = True, + toolchain_type = SWIFT_TOOLCHAIN_TYPE): """Returns a list of toolchain types needed to use the Swift toolchain. This function returns a list so that it can be easily composed with other @@ -78,6 +84,8 @@ def use_swift_toolchain(*, mandatory = True): Args: mandatory: Whether or not it should be an error if the toolchain cannot be resolved. Defaults to True. + toolchain_type: The toolchain type to use. Defaults to the standard + Swift toolchain type. Returns: A list of @@ -86,7 +94,7 @@ def use_swift_toolchain(*, mandatory = True): """ return [ config_common.toolchain_type( - SWIFT_TOOLCHAIN_TYPE, + toolchain_type, mandatory = mandatory, ), ] diff --git a/swift/swift_import.bzl b/swift/swift_import.bzl index 02e5e798a..b939b83c5 100644 --- a/swift/swift_import.bzl +++ b/swift/swift_import.bzl @@ -44,6 +44,7 @@ load( "create_swift_module_context", "create_swift_module_inputs", ) +load(":swift_clang_module_aspect.bzl", "swift_clang_module_aspect") def _swift_import_impl(ctx): archives = ctx.files.archives @@ -146,7 +147,9 @@ def _swift_import_impl(ctx): swift_import = rule( attrs = dicts.add( - swift_common_rule_attrs(), + swift_common_rule_attrs( + additional_deps_aspects = [swift_clang_module_aspect], + ), { "archives": attr.label_list( allow_empty = True, @@ -192,8 +195,6 @@ May not be specified if `swiftinterface` is specified. """, mandatory = False, ), - # TODO(b/301253335): Enable AEGs and add `toolchain` param once this rule starts using toolchain resolution. - "_use_auto_exec_groups": attr.bool(default = False), }, ), doc = """\ diff --git a/swift/toolchains/xcode_swift_toolchain.bzl b/swift/toolchains/xcode_swift_toolchain.bzl index 46d260de5..145b4ed6a 100644 --- a/swift/toolchains/xcode_swift_toolchain.bzl +++ b/swift/toolchains/xcode_swift_toolchain.bzl @@ -666,6 +666,7 @@ def _xcode_swift_toolchain_impl(ctx): cpp_fragment = cpp_fragment, ) + wmo_features_from_swiftcopts(swiftcopts = swiftcopts) requested_features.extend(ctx.features) + requested_features.extend(ctx.attr.default_enabled_features) requested_features.extend(default_features_for_toolchain( target_triple = target_triple, )) @@ -687,6 +688,11 @@ def _xcode_swift_toolchain_impl(ctx): if _is_xcode_at_least_version(xcode_config, "16.0"): requested_features.append(SWIFT_FEATURE__SUPPORTS_V6) + unsupported_features = ctx.disabled_features + [ + SWIFT_FEATURE_MODULE_MAP_HOME_IS_CWD, + ] + unsupported_features.extend(ctx.attr.default_unsupported_features) + env = _xcode_env(target_triple = target_triple, xcode_config = xcode_config) # TODO: Remove once we drop support for Xcode 16.x. @@ -792,9 +798,7 @@ def _xcode_swift_toolchain_impl(ctx): test_linking_contexts = [test_linking_context], ), tool_configs = all_tool_configs, - unsupported_features = ctx.disabled_features + [ - SWIFT_FEATURE_MODULE_MAP_HOME_IS_CWD, - ], + unsupported_features = unsupported_features, ) return [ @@ -837,6 +841,17 @@ and bystanding module are both already declared as dependencies. mandatory = False, providers = [[SwiftCrossImportOverlayInfo]], ), + "default_enabled_features": attr.string_list( + doc = """\ +A list of features that are enabled by default for all targets build with this +toolchain. +""", + ), + "default_unsupported_features": attr.string_list( + doc = """\ +A list of features that are unsupported by this toolchain. +""", + ), "feature_allowlists": attr.label_list( doc = """\ A list of `swift_feature_allowlist` targets that allow or prohibit packages from From f1a3e9082dbaa9475145ae0fa92092fa006c637e Mon Sep 17 00:00:00 2001 From: Luis Padron Date: Tue, 27 May 2025 13:33:52 -0400 Subject: [PATCH 83/92] Upstream: Implement `swift_synthesize_interface_aspect`. (#1522) This aspect uses the new `swift-synthesize-interface` driver mode that will be available in Swift 6.1 to generate a file that contains the synthesized Swift interface for a module (such as a C/Obj-C module), similar to the functionality provided by SourceKit. This can be used by IDEs and other clients to generate those interfaces without the complexity of a SourceKit invocation, which often do not play nicely with advanced build scenarios. PiperOrigin-RevId: 731345457 Upstreams: https://github.com/bazelbuild/rules_swift/commit/51ce176054ebcf816d0c738009df075f727412ae Co-authored-by: Tony Allevato --- doc/api.md | 25 ++++ swift/BUILD | 17 ++- swift/internal/BUILD | 12 ++ swift/internal/action_names.bzl | 4 + swift/internal/interface_synthesizing.bzl | 95 +++++++++++++ swift/providers.bzl | 25 ++++ swift/swift.bzl | 7 + swift/swift_common.bzl | 5 + swift/swift_synthesize_interface_aspect.bzl | 126 ++++++++++++++++++ swift/toolchains/BUILD | 2 + swift/toolchains/config/BUILD | 9 ++ swift/toolchains/config/compile_config.bzl | 19 ++- .../config/synthesize_interface_config.bzl | 49 +++++++ swift/toolchains/xcode_swift_toolchain.bzl | 23 +++- test/BUILD | 9 ++ test/environment_tests.bzl | 6 +- test/fixtures/BUILD | 1 + test/fixtures/synthesize_interface/BUILD | 33 +++++ test/fixtures/synthesize_interface/header.h | 12 ++ test/rules/synthesize_interface_applier.bzl | 41 ++++++ test/synthesize_interface_tests.bzl | 50 +++++++ 21 files changed, 563 insertions(+), 7 deletions(-) create mode 100644 swift/internal/interface_synthesizing.bzl create mode 100644 swift/swift_synthesize_interface_aspect.bzl create mode 100644 swift/toolchains/config/synthesize_interface_config.bzl create mode 100644 test/fixtures/synthesize_interface/BUILD create mode 100644 test/fixtures/synthesize_interface/header.h create mode 100644 test/rules/synthesize_interface_applier.bzl create mode 100644 test/synthesize_interface_tests.bzl diff --git a/doc/api.md b/doc/api.md index c25a7aa37..2c529a51d 100644 --- a/doc/api.md +++ b/doc/api.md @@ -517,6 +517,31 @@ A struct containing the precompiled module and optional indexstore directory, or `None` if the toolchain or target does not support precompiled modules. + + +## swift_common.synthesize_interface + +
+swift_common.synthesize_interface(actions, compilation_contexts, feature_configuration, module_name,
+                                  output_file, swift_infos, swift_toolchain)
+
+ +Extracts the symbol graph from a Swift module. + +**PARAMETERS** + + +| Name | Description | Default Value | +| :------------- | :------------- | :------------- | +| actions | The object used to register actions. | none | +| compilation_contexts | A list of `CcCompilationContext`s that represent C/Objective-C requirements of the target being compiled, such as Swift-compatible preprocessor defines, header search paths, and so forth. These are typically retrieved from the `CcInfo` providers of a target's dependencies. | none | +| feature_configuration | The Swift feature configuration. | none | +| module_name | The name of the module whose symbol graph should be extracted. | none | +| output_file | A `File` into which the synthesized interface will be written. | none | +| swift_infos | A list of `SwiftInfo` providers from dependencies of the target being compiled. This should include both propagated and non-propagated (implementation-only) dependencies. | none | +| swift_toolchain | The `SwiftToolchainInfo` provider of the toolchain. | none | + + ## swift_common.use_toolchain diff --git a/swift/BUILD b/swift/BUILD index 75610303f..b0a262d00 100644 --- a/swift/BUILD +++ b/swift/BUILD @@ -96,6 +96,7 @@ bzl_library( deps = [ "//swift/internal:compiling", "//swift/internal:features", + "//swift/internal:interface_synthesizing", "//swift/internal:linking", "//swift/internal:symbol_graph_extracting", "//swift/internal:toolchain_utils", @@ -207,7 +208,6 @@ bzl_library( "//swift/internal:utils", "@bazel_skylib//lib:dicts", "@bazel_skylib//lib:sets", - "@bazel_skylib//rules:common_settings", ], ) @@ -249,6 +249,7 @@ bzl_library( "//swift/internal:attrs", "//swift/internal:feature_names", "//swift/internal:providers", + "//swift/internal:toolchain_utils", "//swift/internal:utils", ], ) @@ -280,6 +281,18 @@ bzl_library( ], ) +bzl_library( + name = "swift_synthesize_interface_aspect", + srcs = ["swift_synthesize_interface_aspect.bzl"], + deps = [ + ":providers", + ":swift_clang_module_aspect", + "//swift/internal:features", + "//swift/internal:interface_synthesizing", + "//swift/internal:toolchain_utils", + ], +) + bzl_library( name = "swift_test", srcs = ["swift_test.bzl"], @@ -290,6 +303,7 @@ bzl_library( "//swift/internal:compiling", "//swift/internal:env_expansion", "//swift/internal:feature_names", + "//swift/internal:interface_synthesizing", "//swift/internal:linking", "//swift/internal:output_groups", "//swift/internal:providers", @@ -326,6 +340,7 @@ bzl_library( ":swift_overlay_helpers", ":swift_package_configuration", ":swift_symbol_graph_aspect", + ":swift_synthesize_interface_aspect", ":swift_test", ], ) diff --git a/swift/internal/BUILD b/swift/internal/BUILD index d795d1086..0547adc64 100644 --- a/swift/internal/BUILD +++ b/swift/internal/BUILD @@ -162,6 +162,18 @@ bzl_library( ], ) +bzl_library( + name = "interface_synthesizing", + srcs = ["interface_synthesizing.bzl"], + visibility = ["//swift:__subpackages__"], + deps = [ + ":action_names", + ":actions", + ":utils", + "//swift:providers", + ], +) + bzl_library( name = "linking", srcs = ["linking.bzl"], diff --git a/swift/internal/action_names.bzl b/swift/internal/action_names.bzl index d87b7068c..35c1de354 100644 --- a/swift/internal/action_names.bzl +++ b/swift/internal/action_names.bzl @@ -46,6 +46,9 @@ SWIFT_ACTION_PRECOMPILE_C_MODULE = "SwiftPrecompileCModule" # other tooling. SWIFT_ACTION_SYMBOL_GRAPH_EXTRACT = "SwiftSymbolGraphExtract" +# Synthesizes the Swift interface from a compiled module. +SWIFT_ACTION_SYNTHESIZE_INTERFACE = "SwiftSynthesizeInterface" + def all_action_names(): """A convenience function to return all actions defined by this rule set.""" return ( @@ -57,4 +60,5 @@ def all_action_names(): SWIFT_ACTION_MODULEWRAP, SWIFT_ACTION_PRECOMPILE_C_MODULE, SWIFT_ACTION_SYMBOL_GRAPH_EXTRACT, + SWIFT_ACTION_SYNTHESIZE_INTERFACE, ) diff --git a/swift/internal/interface_synthesizing.bzl b/swift/internal/interface_synthesizing.bzl new file mode 100644 index 000000000..09be55890 --- /dev/null +++ b/swift/internal/interface_synthesizing.bzl @@ -0,0 +1,95 @@ +# Copyright 2025 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. + +"""Functions relating to synthesizing Swift interfaces from non-Swift targets.""" + +load("//swift:providers.bzl", "SwiftInfo") +load(":action_names.bzl", "SWIFT_ACTION_SYNTHESIZE_INTERFACE") +load(":actions.bzl", "run_toolchain_action") +load(":utils.bzl", "merge_compilation_contexts") + +def synthesize_interface( + *, + actions, + compilation_contexts, + feature_configuration, + module_name, + output_file, + swift_infos, + swift_toolchain): + """Extracts the symbol graph from a Swift module. + + Args: + actions: The object used to register actions. + compilation_contexts: A list of `CcCompilationContext`s that represent + C/Objective-C requirements of the target being compiled, such as + Swift-compatible preprocessor defines, header search paths, and so + forth. These are typically retrieved from the `CcInfo` providers of + a target's dependencies. + feature_configuration: The Swift feature configuration. + module_name: The name of the module whose symbol graph should be + extracted. + output_file: A `File` into which the synthesized interface will be + written. + swift_infos: A list of `SwiftInfo` providers from dependencies of the + target being compiled. This should include both propagated and + non-propagated (implementation-only) dependencies. + swift_toolchain: The `SwiftToolchainInfo` provider of the toolchain. + """ + merged_compilation_context = merge_compilation_contexts( + transitive_compilation_contexts = compilation_contexts + [ + cc_info.compilation_context + for cc_info in swift_toolchain.implicit_deps_providers.cc_infos + ], + ) + merged_swift_info = SwiftInfo( + swift_infos = ( + swift_infos + swift_toolchain.implicit_deps_providers.swift_infos + ), + ) + + # Flattening this `depset` is necessary because we need to extract the + # module maps or precompiled modules out of structured values and do so + # conditionally. + transitive_modules = merged_swift_info.transitive_modules.to_list() + + transitive_swiftmodules = [] + for module in transitive_modules: + swift_module = module.swift + if swift_module: + transitive_swiftmodules.append(swift_module.swiftmodule) + + prerequisites = struct( + bin_dir = feature_configuration._bin_dir, + cc_compilation_context = merged_compilation_context, + genfiles_dir = feature_configuration._genfiles_dir, + is_swift = True, + module_name = module_name, + output_file = output_file, + target_label = feature_configuration._label, + transitive_modules = transitive_modules, + transitive_swiftmodules = transitive_swiftmodules, + ) + + run_toolchain_action( + actions = actions, + action_name = SWIFT_ACTION_SYNTHESIZE_INTERFACE, + feature_configuration = feature_configuration, + outputs = [output_file], + prerequisites = prerequisites, + progress_message = ( + "Synthesizing Swift interface for {}".format(module_name) + ), + swift_toolchain = swift_toolchain, + ) diff --git a/swift/providers.bzl b/swift/providers.bzl index e4548db37..e0e069ba1 100644 --- a/swift/providers.bzl +++ b/swift/providers.bzl @@ -269,6 +269,31 @@ has the same fields as documented in `direct_symbol_graphs`. }, ) +SwiftSynthesizedInterfaceInfo = provider( + doc = "Propagates synthesized Swift interfaces for modules.", + fields = { + "direct_modules": """\ +`List` of `struct`s representing the synthesized interfaces for the modules in +the target that propagated this provider. This list will be empty if propagated +by a target that does not contain any modules that Swift can synthesize an +interface for (i.e., C, or Swift itself), but its `transitive_modules` may be +non-empty if it has dependencies for which interfaces can be synthesized. + +Each `struct` has the following fields: + +* `module_name`: A string denoting the name of the module whose interface is + synthesized. + +* `synthesized_interface`: A `File` containing the synthesized interface. +""", + "transitive_modules": """\ +`Depset` of `struct`s representing the synthesized interfaces for the modules in +the target that propagated this provider and all of its dependencies. Each +`struct` has the same fields as documented in `direct_modules`. +""", + }, +) + SwiftToolchainInfo = provider( doc = """ Propagates information about a Swift toolchain to compilation and linking rules diff --git a/swift/swift.bzl b/swift/swift.bzl index 14bb7f81c..9353f140f 100644 --- a/swift/swift.bzl +++ b/swift/swift.bzl @@ -33,6 +33,7 @@ load( _SwiftProtoCompilerInfo = "SwiftProtoCompilerInfo", _SwiftProtoInfo = "SwiftProtoInfo", _SwiftSymbolGraphInfo = "SwiftSymbolGraphInfo", + _SwiftSynthesizedInterfaceInfo = "SwiftSynthesizedInterfaceInfo", _SwiftToolchainInfo = "SwiftToolchainInfo", ) load(":swift_binary.bzl", _swift_binary = "swift_binary") @@ -61,6 +62,10 @@ load( ":swift_symbol_graph_aspect.bzl", _swift_symbol_graph_aspect = "swift_symbol_graph_aspect", ) +load( + ":swift_synthesize_interface_aspect.bzl", + _swift_synthesize_interface_aspect = "swift_synthesize_interface_aspect", +) load(":swift_test.bzl", _swift_test = "swift_test") # Re-export providers. @@ -68,6 +73,7 @@ SwiftInfo = _SwiftInfo SwiftProtoCompilerInfo = _SwiftProtoCompilerInfo SwiftProtoInfo = _SwiftProtoInfo SwiftSymbolGraphInfo = _SwiftSymbolGraphInfo +SwiftSynthesizedInterfaceInfo = _SwiftSynthesizedInterfaceInfo SwiftToolchainInfo = _SwiftToolchainInfo # Re-export public API module. @@ -89,3 +95,4 @@ swift_test = _swift_test # Re-export public aspects. swift_clang_module_aspect = _swift_clang_module_aspect swift_symbol_graph_aspect = _swift_symbol_graph_aspect +swift_synthesize_interface_aspect = _swift_synthesize_interface_aspect diff --git a/swift/swift_common.bzl b/swift/swift_common.bzl index c50eb37c7..107754deb 100644 --- a/swift/swift_common.bzl +++ b/swift/swift_common.bzl @@ -33,6 +33,10 @@ load( "get_cc_feature_configuration", "is_feature_enabled", ) +load( + "//swift/internal:interface_synthesizing.bzl", + "synthesize_interface", +) load( "//swift/internal:linking.bzl", "create_linking_context_from_compilation_outputs", @@ -60,5 +64,6 @@ swift_common = struct( get_toolchain = get_swift_toolchain, is_enabled = is_feature_enabled, precompile_clang_module = precompile_clang_module, + synthesize_interface = synthesize_interface, use_toolchain = use_swift_toolchain, ) diff --git a/swift/swift_synthesize_interface_aspect.bzl b/swift/swift_synthesize_interface_aspect.bzl new file mode 100644 index 000000000..c38aeccdd --- /dev/null +++ b/swift/swift_synthesize_interface_aspect.bzl @@ -0,0 +1,126 @@ +# Copyright 2025 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. + +"""Implementation of the `swift_synthesize_interface_aspect` aspect.""" + +load( + "//swift/internal:features.bzl", + "configure_features", +) +load( + "//swift/internal:interface_synthesizing.bzl", + "synthesize_interface", +) +load( + "//swift/internal:toolchain_utils.bzl", + "get_swift_toolchain", + "use_swift_toolchain", +) +load(":providers.bzl", "SwiftInfo", "SwiftSynthesizedInterfaceInfo") +load(":swift_clang_module_aspect.bzl", "swift_clang_module_aspect") + +visibility("public") + +def _get_swift_info(target): + """Returns the SwiftInfo provider or None if it is not present.""" + if SwiftInfo in target: + return target[SwiftInfo] + else: + return None + +def _swift_synthesize_interface_aspect_impl(target, aspect_ctx): + direct_outputs = [] + synthesized_modules = [] + + swift_info = _get_swift_info(target) + if swift_info and swift_info.direct_modules: + swift_toolchain = get_swift_toolchain(aspect_ctx) + feature_configuration = configure_features( + ctx = aspect_ctx, + swift_toolchain = swift_toolchain, + requested_features = aspect_ctx.features, + unsupported_features = aspect_ctx.disabled_features, + ) + + if CcInfo in target: + compilation_context = target[CcInfo].compilation_context + else: + compilation_context = cc_common.create_compilation_context() + + for module in swift_info.direct_modules: + output_file = aspect_ctx.actions.declare_file( + "{}.synthesized.swift".format(target.label.name), + ) + direct_outputs.append(output_file) + synthesize_interface( + actions = aspect_ctx.actions, + compilation_contexts = [compilation_context], + feature_configuration = feature_configuration, + module_name = module.name, + output_file = output_file, + swift_infos = [swift_info], + swift_toolchain = swift_toolchain, + ) + synthesized_modules.append( + struct( + module_name = module.name, + synthesized_interface = output_file, + ), + ) + + transitive_synthesized_modules = [] + for dep in getattr(aspect_ctx.rule.attr, "deps", []): + if SwiftSynthesizedInterfaceInfo in dep: + synth_info = dep[SwiftSynthesizedInterfaceInfo] + transitive_synthesized_modules.append( + synth_info.transitive_modules, + ) + + return [ + OutputGroupInfo( + swift_synthesized_interface = depset(direct_outputs), + ), + SwiftSynthesizedInterfaceInfo( + direct_modules = synthesized_modules, + transitive_modules = depset( + synthesized_modules, + transitive = transitive_synthesized_modules, + ), + ), + ] + +swift_synthesize_interface_aspect = aspect( + attr_aspects = ["deps"], + doc = """\ + Synthesizes the Swift interface for the target to which it is applied. + + This aspect invokes `swift-synthesize-interface` on the target to which + it is applied and produces the output in a file located at + `bazel-bin//.synthesized.swift`. This output + file can be obtained by requesting the output group named + `swift_synthesized_interface` during the build. + + The output group only contains the synthesized interface for the target + to which the aspect is *directly* applied; it does not contain the + synthesized interfaces for any transitive dependencies. If the full + transitive closure of synthesized interfaces is needed, then clients + should read the `SwiftSynthesizedInterfaceInfo` provider, which contains + a `depset` with the full transitive closure. + """, + fragments = ["cpp"], + implementation = _swift_synthesize_interface_aspect_impl, + provides = [SwiftSynthesizedInterfaceInfo], + requires = [swift_clang_module_aspect], + toolchains = use_swift_toolchain(), +) diff --git a/swift/toolchains/BUILD b/swift/toolchains/BUILD index 25921f4b9..1da9128ff 100644 --- a/swift/toolchains/BUILD +++ b/swift/toolchains/BUILD @@ -22,6 +22,7 @@ bzl_library( "//swift/toolchains/config:all_actions_config", "//swift/toolchains/config:compile_config", "//swift/toolchains/config:symbol_graph_config", + "//swift/toolchains/config:synthesize_interface_config", "//swift/toolchains/config:tool_config", "@bazel_skylib//lib:dicts", "@bazel_skylib//lib:paths", @@ -49,6 +50,7 @@ bzl_library( "//swift/toolchains/config:compile_config", "//swift/toolchains/config:compile_module_interface_config", "//swift/toolchains/config:symbol_graph_config", + "//swift/toolchains/config:synthesize_interface_config", "//swift/toolchains/config:tool_config", "@bazel_skylib//lib:dicts", "@bazel_skylib//lib:paths", diff --git a/swift/toolchains/config/BUILD b/swift/toolchains/config/BUILD index 3c72a0911..5738d6c04 100644 --- a/swift/toolchains/config/BUILD +++ b/swift/toolchains/config/BUILD @@ -66,6 +66,15 @@ bzl_library( ], ) +bzl_library( + name = "synthesize_interface_config", + srcs = ["synthesize_interface_config.bzl"], + deps = [ + ":action_config", + "//swift/internal:action_names", + ], +) + bzl_library( name = "tool_config", srcs = ["tool_config.bzl"], diff --git a/swift/toolchains/config/compile_config.bzl b/swift/toolchains/config/compile_config.bzl index 04b64426c..0aca88ebf 100644 --- a/swift/toolchains/config/compile_config.bzl +++ b/swift/toolchains/config/compile_config.bzl @@ -25,6 +25,7 @@ load( "SWIFT_ACTION_DUMP_AST", "SWIFT_ACTION_PRECOMPILE_C_MODULE", "SWIFT_ACTION_SYMBOL_GRAPH_EXTRACT", + "SWIFT_ACTION_SYNTHESIZE_INTERFACE", ) load( "//swift/internal:developer_dirs.bzl", @@ -641,6 +642,7 @@ def compile_action_configs( SWIFT_ACTION_DUMP_AST, SWIFT_ACTION_PRECOMPILE_C_MODULE, SWIFT_ACTION_SYMBOL_GRAPH_EXTRACT, + SWIFT_ACTION_SYNTHESIZE_INTERFACE, ], configurators = [ add_arg("-Xcc", "-Xclang"), @@ -718,6 +720,7 @@ def compile_action_configs( SWIFT_ACTION_DUMP_AST, SWIFT_ACTION_PRECOMPILE_C_MODULE, SWIFT_ACTION_SYMBOL_GRAPH_EXTRACT, + SWIFT_ACTION_SYNTHESIZE_INTERFACE, ], configurators = [add_arg("-Xcc", "-fno-implicit-module-maps")], features = [SWIFT_FEATURE_USE_C_MODULES], @@ -729,6 +732,7 @@ def compile_action_configs( SWIFT_ACTION_COMPILE_MODULE_INTERFACE, SWIFT_ACTION_PRECOMPILE_C_MODULE, SWIFT_ACTION_SYMBOL_GRAPH_EXTRACT, + SWIFT_ACTION_SYNTHESIZE_INTERFACE, ], configurators = [add_arg("-Xcc", "-fno-implicit-modules")], features = [SWIFT_FEATURE_USE_C_MODULES], @@ -851,13 +855,19 @@ def compile_action_configs( not_features = [SWIFT_FEATURE_OPT], ), - # swift-symbolgraph-extract doesn't yet support explicit Swift module - # maps. + # swift-symbolgraph-extract doesn't yet support explicit Swift module maps. ActionConfigInfo( actions = [SWIFT_ACTION_SYMBOL_GRAPH_EXTRACT], configurators = [_dependencies_swiftmodules_and_swiftdocs_configurator], features = [SWIFT_FEATURE_EMIT_SWIFTDOC], ), + + # swift-synthesize-interface doesn't yet support explicit Swift module maps. + ActionConfigInfo( + actions = [SWIFT_ACTION_SYNTHESIZE_INTERFACE], + configurators = [_dependencies_swiftmodules_configurator], + features = [SWIFT_FEATURE_EMIT_SWIFTDOC], + ), ActionConfigInfo( actions = [SWIFT_ACTION_SYMBOL_GRAPH_EXTRACT], configurators = [_dependencies_swiftmodules_configurator], @@ -874,6 +884,7 @@ def compile_action_configs( SWIFT_ACTION_DERIVE_FILES, SWIFT_ACTION_DUMP_AST, SWIFT_ACTION_SYMBOL_GRAPH_EXTRACT, + SWIFT_ACTION_SYNTHESIZE_INTERFACE, ], configurators = [ lambda prereqs, args: _framework_search_paths_configurator( @@ -906,6 +917,7 @@ def compile_action_configs( SWIFT_ACTION_DUMP_AST, SWIFT_ACTION_PRECOMPILE_C_MODULE, SWIFT_ACTION_SYMBOL_GRAPH_EXTRACT, + SWIFT_ACTION_SYNTHESIZE_INTERFACE, ], configurators = [ _clang_search_paths_configurator, @@ -922,6 +934,7 @@ def compile_action_configs( SWIFT_ACTION_DERIVE_FILES, SWIFT_ACTION_DUMP_AST, SWIFT_ACTION_SYMBOL_GRAPH_EXTRACT, + SWIFT_ACTION_SYNTHESIZE_INTERFACE, ], configurators = [_dependencies_clang_modules_configurator], features = [SWIFT_FEATURE_USE_C_MODULES], @@ -934,6 +947,7 @@ def compile_action_configs( SWIFT_ACTION_DUMP_AST, SWIFT_ACTION_PRECOMPILE_C_MODULE, SWIFT_ACTION_SYMBOL_GRAPH_EXTRACT, + SWIFT_ACTION_SYNTHESIZE_INTERFACE, ], configurators = [_dependencies_clang_modulemaps_configurator], not_features = [SWIFT_FEATURE_USE_C_MODULES], @@ -1038,6 +1052,7 @@ def compile_action_configs( SWIFT_ACTION_DUMP_AST, SWIFT_ACTION_PRECOMPILE_C_MODULE, SWIFT_ACTION_SYMBOL_GRAPH_EXTRACT, + SWIFT_ACTION_SYNTHESIZE_INTERFACE, ], configurators = [_module_name_configurator], ), diff --git a/swift/toolchains/config/synthesize_interface_config.bzl b/swift/toolchains/config/synthesize_interface_config.bzl new file mode 100644 index 000000000..ec71f08be --- /dev/null +++ b/swift/toolchains/config/synthesize_interface_config.bzl @@ -0,0 +1,49 @@ +# Copyright 2025 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. + +"""Common configuration for interface synthesis actions.""" + +load( + "//swift/internal:action_names.bzl", + "SWIFT_ACTION_SYNTHESIZE_INTERFACE", +) +load(":action_config.bzl", "ActionConfigInfo", "add_arg") + +def synthesize_interface_action_configs(): + """Returns the list of action configs needed to synthesize Swift interfaces. + + If a toolchain supports interface synthesis, it should add these to its + list of action configs so that those actions will be correctly configured. + (Other required configuration is provided by `compile_action_configs`.) + + Returns: + The list of action configs needed to synthesize Swift interfaces. + """ + return [ + ActionConfigInfo( + actions = [SWIFT_ACTION_SYNTHESIZE_INTERFACE], + configurators = [ + add_arg("-include-submodules"), + add_arg("-print-fully-qualified-types"), + ], + ), + ActionConfigInfo( + actions = [SWIFT_ACTION_SYNTHESIZE_INTERFACE], + configurators = [_synthesized_interface_output_configurator], + ), + ] + +def _synthesized_interface_output_configurator(prerequisites, args): + """Configures the outputs of the synthesize interface action.""" + args.add("-o", prerequisites.output_file) diff --git a/swift/toolchains/xcode_swift_toolchain.bzl b/swift/toolchains/xcode_swift_toolchain.bzl index 145b4ed6a..a6075b949 100644 --- a/swift/toolchains/xcode_swift_toolchain.bzl +++ b/swift/toolchains/xcode_swift_toolchain.bzl @@ -43,6 +43,7 @@ load( "SWIFT_ACTION_DUMP_AST", "SWIFT_ACTION_PRECOMPILE_C_MODULE", "SWIFT_ACTION_SYMBOL_GRAPH_EXTRACT", + "SWIFT_ACTION_SYNTHESIZE_INTERFACE", ) load("//swift/internal:attrs.bzl", "swift_toolchain_driver_attrs") load("//swift/internal:developer_dirs.bzl", "swift_developer_lib_dir") @@ -98,6 +99,10 @@ load( "//swift/toolchains/config:symbol_graph_config.bzl", "symbol_graph_action_configs", ) +load( + "//swift/toolchains/config:synthesize_interface_config.bzl", + "synthesize_interface_action_configs", +) load("//swift/toolchains/config:tool_config.bzl", "ToolConfigInfo") def _platform_developer_framework_dir( @@ -327,6 +332,7 @@ def _all_action_configs( SWIFT_ACTION_DUMP_AST, SWIFT_ACTION_PRECOMPILE_C_MODULE, SWIFT_ACTION_SYMBOL_GRAPH_EXTRACT, + SWIFT_ACTION_SYNTHESIZE_INTERFACE, ], configurators = [ add_arg("-target", target_triples.str(target_triple)), @@ -404,6 +410,7 @@ def _all_action_configs( SWIFT_ACTION_DUMP_AST, SWIFT_ACTION_PRECOMPILE_C_MODULE, SWIFT_ACTION_SYMBOL_GRAPH_EXTRACT, + SWIFT_ACTION_SYNTHESIZE_INTERFACE, ], configurators = [ _make_resource_directory_configurator( @@ -442,6 +449,7 @@ def _all_action_configs( generated_header_rewriter = generated_header_rewriter, )) action_configs.extend(symbol_graph_action_configs()) + action_configs.extend(synthesize_interface_action_configs()) action_configs.extend(compile_module_interface_action_configs()) return action_configs @@ -456,7 +464,8 @@ def _all_tool_configs( env, execution_requirements, swift_executable, - toolchain_root): + toolchain_root, + xcode_config): """Returns the tool configurations for the Swift toolchain. Args: @@ -467,6 +476,7 @@ def _all_tool_configs( 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. + xcode_config: The Xcode configuration. Returns: A dictionary mapping action name to tool configuration. @@ -532,6 +542,16 @@ def _all_tool_configs( ), } + # swift-synthesize-interface is only available in Xcode 16.3 and later. + if _is_xcode_at_least_version(xcode_config, "16.3"): + tool_configs[SWIFT_ACTION_SYNTHESIZE_INTERFACE] = ToolConfigInfo( + driver_config = _driver_config(mode = "swift-synthesize-interface"), + env = env, + execution_requirements = execution_requirements, + use_param_file = True, + worker_mode = "wrap", + ) + return tool_configs def _is_xcode_at_least_version(xcode_config, desired_version): @@ -711,6 +731,7 @@ def _xcode_swift_toolchain_impl(ctx): execution_requirements = execution_requirements, swift_executable = swift_executable, toolchain_root = toolchain_root, + xcode_config = xcode_config, ) all_action_configs = _all_action_configs( additional_objc_copts = command_line_objc_copts( diff --git a/test/BUILD b/test/BUILD index ecc8a61ff..35deeeb31 100644 --- a/test/BUILD +++ b/test/BUILD @@ -24,6 +24,7 @@ load(":split_derived_files_tests.bzl", "split_derived_files_test_suite") load(":swift_binary_linking_tests.bzl", "swift_binary_linking_test_suite") load(":swift_through_non_swift_tests.bzl", "swift_through_non_swift_test_suite") load(":symbol_graphs_tests.bzl", "symbol_graphs_test_suite") +load(":synthesize_interface_tests.bzl", "synthesize_interface_test_suite") load(":utils_tests.bzl", "utils_test_suite") load(":xctest_runner_tests.bzl", "xctest_runner_test_suite") @@ -71,6 +72,14 @@ swift_through_non_swift_test_suite(name = "swift_through_non_swift") symbol_graphs_test_suite(name = "symbol_graphs") +synthesize_interface_test_suite( + name = "synthesize_interface", + # Don't run this on CI until Xcode 16.3 is available. + tags = [ + "manual", + ], +) + pch_output_dir_test_suite(name = "pch_output_dir_settings") private_swiftinterface_test_suite(name = "private_swiftinterface") diff --git a/test/environment_tests.bzl b/test/environment_tests.bzl index e59404559..ffe2c0577 100644 --- a/test/environment_tests.bzl +++ b/test/environment_tests.bzl @@ -38,7 +38,7 @@ def environment_test_suite(name, tags = []): field = "environment", provider = "RunEnvironmentInfo", tags = all_tags, - target_under_test = "@build_bazel_rules_swift//test/fixtures/environment:binary", + target_under_test = "//test/fixtures/environment:binary", ) provider_test( @@ -50,7 +50,7 @@ def environment_test_suite(name, tags = []): field = "environment", provider = "RunEnvironmentInfo", tags = all_tags, - target_under_test = "@build_bazel_rules_swift//test/fixtures/environment:test", + target_under_test = "//test/fixtures/environment:test", ) provider_test( @@ -61,7 +61,7 @@ def environment_test_suite(name, tags = []): field = "inherited_environment", provider = "RunEnvironmentInfo", tags = all_tags, - target_under_test = "@build_bazel_rules_swift//test/fixtures/environment:test", + target_under_test = "//test/fixtures/environment:test", ) native.test_suite( diff --git a/test/fixtures/BUILD b/test/fixtures/BUILD index 1d71c446e..d406d2cb8 100644 --- a/test/fixtures/BUILD +++ b/test/fixtures/BUILD @@ -8,6 +8,7 @@ bzl_library( deps = [ "//swift:providers", "//swift:swift_clang_module_aspect", + "//swift:swift_synthesize_interface_aspect", ], ) diff --git a/test/fixtures/synthesize_interface/BUILD b/test/fixtures/synthesize_interface/BUILD new file mode 100644 index 000000000..2cecdc141 --- /dev/null +++ b/test/fixtures/synthesize_interface/BUILD @@ -0,0 +1,33 @@ +load( + "//swift:swift_interop_hint.bzl", + "swift_interop_hint", +) +load("//test/fixtures:common.bzl", "FIXTURE_TAGS") +load( + "//test/rules:synthesize_interface_applier.bzl", + "synthesize_interface_applier", +) + +package( + default_visibility = ["//test:__subpackages__"], +) + +licenses(["notice"]) + +cc_library( + name = "c_module", + hdrs = ["header.h"], + aspect_hints = [":c_module_swift_hint"], + tags = FIXTURE_TAGS, +) + +swift_interop_hint( + name = "c_module_swift_hint", + tags = FIXTURE_TAGS, +) + +synthesize_interface_applier( + name = "synthesized_interface", + tags = FIXTURE_TAGS, + target = ":c_module", +) diff --git a/test/fixtures/synthesize_interface/header.h b/test/fixtures/synthesize_interface/header.h new file mode 100644 index 000000000..e1668460c --- /dev/null +++ b/test/fixtures/synthesize_interface/header.h @@ -0,0 +1,12 @@ +#ifndef RULES_SWIFT_TEST_FIXTURES_SYNTHESIZE_INTERFACE_HEADER_H_ +#define RULES_SWIFT_TEST_FIXTURES_SYNTHESIZE_INTERFACE_HEADER_H_ + +#define SIMPLE_CONSTANT 123 + +typedef struct MyStruct { + int my_field; +} MyStruct; + +void my_function(MyStruct *p); + +#endif // RULES_SWIFT_TEST_FIXTURES_SYNTHESIZE_INTERFACE_HEADER_H_ diff --git a/test/rules/synthesize_interface_applier.bzl b/test/rules/synthesize_interface_applier.bzl new file mode 100644 index 000000000..b808515a8 --- /dev/null +++ b/test/rules/synthesize_interface_applier.bzl @@ -0,0 +1,41 @@ +# Copyright 2025 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. + +"""A rule to collect the outputs of `swift_synthesize_interface_aspect`.""" + +load( + "//swift:providers.bzl", + "SwiftSynthesizedInterfaceInfo", +) +load( + "//swift:swift_synthesize_interface_aspect.bzl", + "swift_synthesize_interface_aspect", +) + +def _synthesize_interface_applier_impl(ctx): + target = ctx.attr.target + info = target[SwiftSynthesizedInterfaceInfo] + return [DefaultInfo( + files = depset([ + module.synthesized_interface + for module in info.direct_modules + ]), + )] + +synthesize_interface_applier = rule( + attrs = { + "target": attr.label(aspects = [swift_synthesize_interface_aspect]), + }, + implementation = _synthesize_interface_applier_impl, +) diff --git a/test/synthesize_interface_tests.bzl b/test/synthesize_interface_tests.bzl new file mode 100644 index 000000000..05c0e4de9 --- /dev/null +++ b/test/synthesize_interface_tests.bzl @@ -0,0 +1,50 @@ +# Copyright 2022 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 synthesizing Swift interfaces for modules.""" + +load( + "//test/rules:provider_test.bzl", + "provider_test", +) + +visibility("private") + +def synthesize_interface_test_suite(name, tags = []): + """Test suite for synthesizing Swift interfaces for modules. + + Args: + name: The base name to be used in targets created by this macro. + tags: Additional tags to apply to each test. + """ + all_tags = [name] + tags + + # Verify that the `swift_extract_symbol_graph` rule produces a directory + # output containing the correct files when the requested target is a leaf + # module. + provider_test( + name = "{}_expected_files".format(name), + expected_files = [ + "test/fixtures/synthesize_interface/c_module.synthesized.swift", + ], + field = "files", + provider = "DefaultInfo", + tags = all_tags, + target_under_test = "//test/fixtures/synthesize_interface:synthesized_interface", + ) + + native.test_suite( + name = name, + tags = all_tags, + ) From 46c6f7e4f069abb88eaa72bc4c20cb3a00e76cfe Mon Sep 17 00:00:00 2001 From: Luis Padron Date: Tue, 27 May 2025 13:33:59 -0400 Subject: [PATCH 84/92] Upstream: Use a new provider to wrap symbol graph extraction used for test discovery (#1521) Upstreams: - https://github.com/bazelbuild/rules_swift/commit/be9220c8feb6fc5f107e4054588c86cc172e7f24 - https://github.com/bazelbuild/rules_swift/commit/a2b9db5dbd56a77327f175b674aee1935cd43f82 >Since symbol graph extraction is an implementation detail of `swift_test`, it shouldn't squat on the provider exclusively. Any other aspect invocation trying to extract symbol graphs on the same target will run into the "provider was provided twice" error. > To remedy this, introduce the ability for someone creating their own symbol graph aspect to automatically wrap the `SwiftSymbolGraphInfo` provider in one of their own choosing, and to add a distinguishing string to the output directory name so that outputs likewise don't collide. (In the future, we may want to do this automatically by hashing the active aspect IDs, but at this time I prefer not to introduce that complexity here.) --------- Co-authored-by: Tony Allevato --- swift/internal/swift_symbol_graph_aspect.bzl | 238 +++++++++++++------ swift/swift_test.bzl | 8 +- 2 files changed, 171 insertions(+), 75 deletions(-) diff --git a/swift/internal/swift_symbol_graph_aspect.bzl b/swift/internal/swift_symbol_graph_aspect.bzl index 5cd1ce220..4465e130f 100644 --- a/swift/internal/swift_symbol_graph_aspect.bzl +++ b/swift/internal/swift_symbol_graph_aspect.bzl @@ -34,79 +34,152 @@ load( ) load("//swift/internal:utils.bzl", "include_developer_search_paths") -def _swift_symbol_graph_aspect_impl(target, aspect_ctx): - symbol_graphs = [] - - if SwiftInfo in target: - swift_toolchain = get_swift_toolchain(aspect_ctx) - feature_configuration = configure_features( - ctx = aspect_ctx, - swift_toolchain = swift_toolchain, - requested_features = aspect_ctx.features, - unsupported_features = aspect_ctx.disabled_features, - ) +def _make_swift_symbol_graph_aspect_impl( + output_distinguisher = None, + wrapper_provider = None): + """Creates the implementation function for a symbol graph aspect. - swift_info = target[SwiftInfo] - if CcInfo in target: - compilation_context = target[CcInfo].compilation_context - else: - compilation_context = cc_common.create_compilation_context() + Args: + output_distinguisher: If set, a string that will be appended to the + output directory name for each target. This allows multiple + instances of the aspect to be used on the same target without their + outputs colliding. + wrapper_provider: If set, the provider to use to wrap the symbol graph + provider. If not set, the symbol graph provider will be used + directly. If set, the provider must have a single field named + `symbol_graph_info` that will be set to the underlying symbol graph + provider. + Returns: + The aspect implementation function. + """ - emit_extension_block_symbols = aspect_ctx.attr.emit_extension_block_symbols - minimum_access_level = aspect_ctx.attr.minimum_access_level + def _wrap(provider): + """Wraps the symbol graph provider if necessary.""" + if wrapper_provider: + return wrapper_provider(symbol_graph_info = provider) + return provider - # Only extract the symbol graphs of modules when the target compiles Swift or ObjC code - compiles_code = [ - action - for action in target.actions - if action.mnemonic in ("SwiftCompile", "SwiftCompileModule", "SwiftPrecompileCModule") - ] - if compiles_code: - for module in swift_info.direct_modules: - output_dir = aspect_ctx.actions.declare_directory( - "{}.symbolgraphs".format(target.label.name), - ) - extract_symbol_graph( - actions = aspect_ctx.actions, - compilation_contexts = [compilation_context], - emit_extension_block_symbols = emit_extension_block_symbols, - feature_configuration = feature_configuration, - include_dev_srch_paths = include_developer_search_paths( - aspect_ctx.rule.attr, - ), - minimum_access_level = minimum_access_level, - module_name = module.name, - output_dir = output_dir, - swift_infos = [swift_info], - swift_toolchain = swift_toolchain, + def _unwrap(target): + """Unwraps the symbol graph provider if necessary.""" + if wrapper_provider: + if wrapper_provider in target: + return target[wrapper_provider].symbol_graph_info + return None + + if SwiftSymbolGraphInfo in target: + return target[SwiftSymbolGraphInfo] + return None + + def _impl(target, aspect_ctx): + """The actual aspect implementation function that will be returned.""" + symbol_graphs = [] + + if SwiftInfo in target: + swift_toolchain = get_swift_toolchain(aspect_ctx) + feature_configuration = configure_features( + ctx = aspect_ctx, + swift_toolchain = swift_toolchain, + requested_features = aspect_ctx.features, + unsupported_features = aspect_ctx.disabled_features, + ) + + swift_info = target[SwiftInfo] + if CcInfo in target: + compilation_context = target[CcInfo].compilation_context + else: + compilation_context = cc_common.create_compilation_context() + + emit_extension_block_symbols = aspect_ctx.attr.emit_extension_block_symbols + minimum_access_level = aspect_ctx.attr.minimum_access_level + + # Only extract the symbol graphs of modules when the target compiles + # Swift or Objective-C code. + compiles_code = [ + action + for action in target.actions + if action.mnemonic in ( + "SwiftCompile", + "SwiftCompileModule", + "SwiftPrecompileCModule", ) - symbol_graphs.append( - struct( + ] + if compiles_code: + for module in swift_info.direct_modules: + if module.is_system: + continue + + if output_distinguisher: + output_name = "{}.{}.symbolgraphs".format( + target.label.name, + output_distinguisher, + ) + else: + output_name = "{}.symbolgraphs".format( + target.label.name, + ) + output_dir = aspect_ctx.actions.declare_directory( + output_name, + ) + extract_symbol_graph( + actions = aspect_ctx.actions, + compilation_contexts = [compilation_context], + emit_extension_block_symbols = emit_extension_block_symbols, + feature_configuration = feature_configuration, + include_dev_srch_paths = include_developer_search_paths( + aspect_ctx.rule.attr, + ), + minimum_access_level = minimum_access_level, module_name = module.name, - symbol_graph_dir = output_dir, - ), - ) + output_dir = output_dir, + swift_infos = [swift_info], + swift_toolchain = swift_toolchain, + ) + symbol_graphs.append( + struct( + module_name = module.name, + symbol_graph_dir = output_dir, + ), + ) - # TODO(b/204480390): We intentionally don't propagate symbol graphs from - # private deps at this time, since the main use case for them is - # documentation. Are there use cases where we should consider this? - transitive_symbol_graphs = [] - for dep in getattr(aspect_ctx.rule.attr, "deps", []): - if SwiftSymbolGraphInfo in dep: - symbol_graph_info = dep[SwiftSymbolGraphInfo] - transitive_symbol_graphs.append( - symbol_graph_info.transitive_symbol_graphs, - ) + # TODO(b/204480390): We intentionally don't propagate symbol graphs from + # private deps at this time, since the main use case for them is + # documentation. Are there use cases where we should consider this? + transitive_symbol_graphs = [] + for dep in getattr(aspect_ctx.rule.attr, "deps", []): + symbol_graph_info = _unwrap(dep) + if symbol_graph_info: + transitive_symbol_graphs.append( + symbol_graph_info.transitive_symbol_graphs, + ) - return [ - SwiftSymbolGraphInfo( - direct_symbol_graphs = symbol_graphs, - transitive_symbol_graphs = depset( - symbol_graphs, - transitive = transitive_symbol_graphs, + return [ + _wrap( + SwiftSymbolGraphInfo( + direct_symbol_graphs = symbol_graphs, + transitive_symbol_graphs = depset( + symbol_graphs, + transitive = transitive_symbol_graphs, + ), + ), ), - ), - ] + ] + + return _impl + +SwiftTestDiscoverySymbolGraphInfo = provider( + doc = "Wraps the symbol graph info for test discovery.", + fields = { + "symbol_graph_info": """\ +The underlying `SwiftSymbolGraphInfo` provider for the target. +""", + }, +) + +# Used only by `_testonly_symbol_graph_aspect_impl` below. +_test_discovery_swift_symbol_graph_aspect_impl = _make_swift_symbol_graph_aspect_impl( + output_distinguisher = "test_discovery", + wrapper_provider = SwiftTestDiscoverySymbolGraphInfo, +) def _testonly_symbol_graph_aspect_impl(target, aspect_ctx): ignored = not getattr(aspect_ctx.rule.attr, "testonly", False) @@ -116,20 +189,24 @@ def _testonly_symbol_graph_aspect_impl(target, aspect_ctx): # a non-`testonly` target can't depend on a `testonly` target, so there # is no possibility of losing anything we'd want to keep. return [ - SwiftSymbolGraphInfo( - direct_symbol_graphs = [], - transitive_symbol_graphs = depset(), + SwiftTestDiscoverySymbolGraphInfo( + symbol_graph_info = SwiftSymbolGraphInfo( + direct_symbol_graphs = [], + transitive_symbol_graphs = depset(), + ), ), ] - return _swift_symbol_graph_aspect_impl(target, aspect_ctx) + return _test_discovery_swift_symbol_graph_aspect_impl(target, aspect_ctx) def make_swift_symbol_graph_aspect( *, default_emit_extension_block_symbols, default_minimum_access_level, doc = "", - testonly_targets): + testonly_targets, + output_distinguisher = None, + wrapper_provider = None): """Creates an aspect that extracts Swift symbol graphs from dependencies. Args: @@ -141,16 +218,33 @@ def make_swift_symbol_graph_aspect( that applies this aspect can let users override this value if it also provides an attribute named `minimum_access_level`. doc: The documentation string for the aspect. + output_distinguisher: If set, a string that will be appended to the + output directory name for each target. This allows multiple + instances of the aspect to be used on the same target without their + outputs colliding. testonly_targets: If True, symbol graphs will only be extracted from targets that have the `testonly` attribute set. + wrapper_provider: If set, the provider to use to wrap the symbol graph + provider. If not set, the symbol graph provider will be used + directly. If set, the provider must have a single field named + `symbol_graph_info` that will be set to the underlying symbol graph + provider. Returns: An `aspect` that can be applied to a rule's dependencies. """ if testonly_targets: aspect_impl = _testonly_symbol_graph_aspect_impl + provides = [SwiftTestDiscoverySymbolGraphInfo] else: - aspect_impl = _swift_symbol_graph_aspect_impl + aspect_impl = _make_swift_symbol_graph_aspect_impl( + output_distinguisher = output_distinguisher, + wrapper_provider = wrapper_provider, + ) + if wrapper_provider: + provides = [wrapper_provider] + else: + provides = [SwiftSymbolGraphInfo] return aspect( attr_aspects = ["deps"], @@ -197,7 +291,7 @@ default value is {default_value}. doc = doc, fragments = ["cpp"], implementation = aspect_impl, - provides = [SwiftSymbolGraphInfo], + provides = provides, requires = [swift_clang_module_aspect], toolchains = use_swift_toolchain(), ) diff --git a/swift/swift_test.bzl b/swift/swift_test.bzl index 16359f6cf..f789e1b19 100644 --- a/swift/swift_test.bzl +++ b/swift/swift_test.bzl @@ -37,6 +37,7 @@ load( load("//swift/internal:providers.bzl", "SwiftCompilerPluginInfo") load( "//swift/internal:swift_symbol_graph_aspect.bzl", + "SwiftTestDiscoverySymbolGraphInfo", "make_swift_symbol_graph_aspect", ) load("//swift/internal:symbol_graph_extracting.bzl", "extract_symbol_graph") @@ -56,7 +57,6 @@ load( ":providers.bzl", "SwiftBinaryInfo", "SwiftInfo", - "SwiftSymbolGraphInfo", "create_swift_module_context", ) @@ -135,10 +135,12 @@ def _generate_test_discovery_srcs( modules_to_scan.append(owner_module_name) for dep in deps: - if SwiftSymbolGraphInfo not in dep: + if SwiftTestDiscoverySymbolGraphInfo not in dep: continue - symbol_graph_info = dep[SwiftSymbolGraphInfo] + symbol_graph_info = ( + dep[SwiftTestDiscoverySymbolGraphInfo].symbol_graph_info + ) # Only include the direct symbol graphs if the owner didn't have any # sources. From da07016a15fc2776e0da556f7dfea744e5f84789 Mon Sep 17 00:00:00 2001 From: Luis Padron Date: Wed, 28 May 2025 16:09:42 -0400 Subject: [PATCH 85/92] Update CI to use Xcode 16.2 (#1525) > Your selected Xcode version 15.4 was not available on the machine. Bazel CI automatically picked a fallback version: 16.2. Available versions are: 16.2. --- .bazelci/presubmit.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.bazelci/presubmit.yml b/.bazelci/presubmit.yml index 342e6803b..e1601eae7 100644 --- a/.bazelci/presubmit.yml +++ b/.bazelci/presubmit.yml @@ -4,7 +4,7 @@ x_defaults: # it doesn't know about; so that is used to avoid repeating common subparts. mac_common: &mac_common platform: macos_arm64 - xcode_version: "15.4" + xcode_version: "16.2" build_targets: - "//examples/..." test_targets: From d1f05441363f41087e8dbed7800efa5a52660595 Mon Sep 17 00:00:00 2001 From: Luis Padron Date: Wed, 28 May 2025 21:33:47 -0400 Subject: [PATCH 86/92] Upstream: Add an advanced API to create a custom instance of swift_clang_module_aspect with a different toolchain type than the default (#1524) Cherry-picks: - https://github.com/bazelbuild/rules_swift/commit/1532dfc73a98adfe22eaf592b9dee565762167e3 - https://github.com/bazelbuild/rules_swift/commit/7f28991db8d2b44dd41b2b0ad7436014bae9f715 --------- Co-authored-by: Tony Allevato --- doc/api.md | 21 +++++--- swift/internal/BUILD | 3 ++ swift/internal/actions.bzl | 5 ++ swift/internal/autolinking.bzl | 8 ++- swift/internal/compiling.bzl | 27 +++++++++- swift/internal/debugging.bzl | 7 ++- swift/internal/interface_synthesizing.bzl | 8 ++- swift/internal/linking.bzl | 18 ++++++- swift/internal/symbol_graph_extracting.bzl | 8 ++- swift/swift_clang_module_aspect.bzl | 60 +++++++++++++++++----- 10 files changed, 139 insertions(+), 26 deletions(-) diff --git a/doc/api.md b/doc/api.md index 2c529a51d..fa78c9e0f 100644 --- a/doc/api.md +++ b/doc/api.md @@ -168,7 +168,7 @@ swift_common.compile(actions, extra_swift_infos, feature_configuration, generated_header_name, is_test, include_dev_srch_paths, module_name, package_name, plugins, private_cc_infos, private_swift_infos, srcs, swift_infos, swift_toolchain, target_name, - workspace_name) + toolchain_type, workspace_name) Compiles a Swift module. @@ -198,6 +198,7 @@ Compiles a Swift module. | swift_infos | A list of `SwiftInfo` providers from non-private dependencies of the target being compiled. The modules defined by these providers are used as dependencies of both the Swift module being compiled and the Clang module for the generated header. | 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 | +| toolchain_type | The toolchain type of the `swift_toolchain` which is used for the proper selection of the execution platform inside `run_toolchain_action`. | `"@build_bazel_rules_swift//toolchains:toolchain_type"` | | 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 | **RETURNS** @@ -251,7 +252,8 @@ A `struct` with the following fields:
 swift_common.compile_module_interface(actions, clang_module, compilation_contexts, copts,
                                       exec_group, feature_configuration, is_framework, module_name,
-                                      swiftinterface_file, swift_infos, swift_toolchain, target_name)
+                                      swiftinterface_file, swift_infos, swift_toolchain, target_name,
+                                      toolchain_type)
 
Compiles a Swift module interface. @@ -273,6 +275,7 @@ Compiles a Swift module interface. | swift_infos | A list of `SwiftInfo` providers from dependencies of the target being 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 | +| toolchain_type | The toolchain type of the `swift_toolchain` which is used for the proper selection of the execution platform inside `run_toolchain_action`. | `"@build_bazel_rules_swift//toolchains:toolchain_type"` | **RETURNS** @@ -361,7 +364,8 @@ swift_common.create_linking_context_from_compilation_outputs(feature_configuration, is_test, include_dev_srch_paths, label, linking_contexts, module_context, name, - swift_toolchain, user_link_flags) + swift_toolchain, toolchain_type, + user_link_flags) Creates a linking context from the outputs of a Swift compilation. @@ -391,6 +395,7 @@ command line parameters file, those actions will be created here. | module_context | The module context returned by `compile` containing information about the Swift module that was compiled. Typically, this is the first tuple element in the value returned by `compile`. | none | | name | A string that is used to derive the name of the library or libraries linked by this function. If this is not provided or is a falsy value, the name component of the `label` argument is used. | `None` | | swift_toolchain | The `SwiftToolchainInfo` provider of the toolchain. | none | +| toolchain_type | The toolchain type of the `swift_toolchain` which is used for the proper selection of the execution platform inside `run_toolchain_action`. | `"@build_bazel_rules_swift//toolchains:toolchain_type"` | | user_link_flags | A `list` of strings containing additional flags that will be passed to the linker for any binary that links with the returned linking context. | `[]` | **RETURNS** @@ -407,7 +412,8 @@ A tuple of `(CcLinkingContext, CcLinkingOutputs)` containing the linking
 swift_common.extract_symbol_graph(actions, compilation_contexts, emit_extension_block_symbols,
                                   feature_configuration, include_dev_srch_paths, minimum_access_level,
-                                  module_name, output_dir, swift_infos, swift_toolchain)
+                                  module_name, output_dir, swift_infos, swift_toolchain,
+                                  toolchain_type)
 
Extracts the symbol graph from a Swift module. @@ -427,6 +433,7 @@ Extracts the symbol graph from a Swift module. | output_dir | A directory-type `File` into which `.symbols.json` files representing the module's symbol graph will be extracted. If extraction is successful, this directory will contain a file named `${MODULE_NAME}.symbols.json`. Optionally, if the module contains extensions to types in other modules, then there will also be files named `${MODULE_NAME}@${EXTENDED_MODULE}.symbols.json`. | none | | swift_infos | A list of `SwiftInfo` providers from dependencies of the target being compiled. This should include both propagated and non-propagated (implementation-only) dependencies. | none | | swift_toolchain | The `SwiftToolchainInfo` provider of the toolchain. | none | +| toolchain_type | The toolchain type of the `swift_toolchain` which is used for the proper selection of the execution platform inside `run_toolchain_action`. | `"@build_bazel_rules_swift//toolchains:toolchain_type"` | @@ -491,7 +498,7 @@ check it.
 swift_common.precompile_clang_module(actions, cc_compilation_context, exec_group,
                                      feature_configuration, module_map_file, module_name,
-                                     swift_toolchain, target_name, swift_infos)
+                                     swift_toolchain, target_name, toolchain_type, swift_infos)
 
Precompiles an explicit Clang module that is compatible with Swift. @@ -509,6 +516,7 @@ Precompiles an explicit Clang module that is compatible with Swift. | 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 | +| toolchain_type | The toolchain type of the Swift toolchain. | none | | swift_infos | A list of `SwiftInfo` providers representing dependencies required to compile this module. | `[]` | **RETURNS** @@ -523,7 +531,7 @@ A struct containing the precompiled module and optional indexstore directory,
 swift_common.synthesize_interface(actions, compilation_contexts, feature_configuration, module_name,
-                                  output_file, swift_infos, swift_toolchain)
+                                  output_file, swift_infos, swift_toolchain, toolchain_type)
 
Extracts the symbol graph from a Swift module. @@ -540,6 +548,7 @@ Extracts the symbol graph from a Swift module. | output_file | A `File` into which the synthesized interface will be written. | none | | swift_infos | A list of `SwiftInfo` providers from dependencies of the target being compiled. This should include both propagated and non-propagated (implementation-only) dependencies. | none | | swift_toolchain | The `SwiftToolchainInfo` provider of the toolchain. | none | +| toolchain_type | The toolchain type of the `swift_toolchain` which is used for the proper selection of the execution platform inside `run_toolchain_action`. | `"@build_bazel_rules_swift//toolchains:toolchain_type"` | diff --git a/swift/internal/BUILD b/swift/internal/BUILD index 0547adc64..89dc87669 100644 --- a/swift/internal/BUILD +++ b/swift/internal/BUILD @@ -169,6 +169,7 @@ bzl_library( deps = [ ":action_names", ":actions", + ":toolchain_utils", ":utils", "//swift:providers", ], @@ -189,6 +190,7 @@ bzl_library( ":developer_dirs", ":feature_names", ":features", + ":toolchain_utils", ":utils", "//swift:providers", ], @@ -272,6 +274,7 @@ bzl_library( deps = [ ":action_names", ":actions", + ":toolchain_utils", ":utils", "//swift:providers", ], diff --git a/swift/internal/actions.bzl b/swift/internal/actions.bzl index cae8468b8..0843c543a 100644 --- a/swift/internal/actions.bzl +++ b/swift/internal/actions.bzl @@ -135,6 +135,7 @@ def run_toolchain_action( prerequisites, swift_toolchain, mnemonic = None, + toolchain_type, **kwargs): """Runs an action using the toolchain's tool and action configurations. @@ -152,6 +153,9 @@ def run_toolchain_action( by the action configurators to add files and other dependent data to the command line. swift_toolchain: The Swift toolchain being used to build. + toolchain_type: A toolchain type of the `swift_toolchain` which is used + for the proper selection of the execution platform inside + `run_toolchain_action`. **kwargs: Additional arguments passed directly to `actions.run`. """ tool_config = swift_toolchain.tool_configs.get(action_name) @@ -219,6 +223,7 @@ def run_toolchain_action( env = tool_config.env, exec_group = exec_group, executable = executable, + toolchain = toolchain_type, execution_requirements = execution_requirements, inputs = depset( action_inputs.inputs, diff --git a/swift/internal/autolinking.bzl b/swift/internal/autolinking.bzl index 70794cd1c..f79a758c2 100644 --- a/swift/internal/autolinking.bzl +++ b/swift/internal/autolinking.bzl @@ -21,6 +21,7 @@ load( ) load(":action_names.bzl", "SWIFT_ACTION_AUTOLINK_EXTRACT") load(":actions.bzl", "run_toolchain_action") +load(":toolchain_utils.bzl", "SWIFT_TOOLCHAIN_TYPE") def _autolink_extract_input_configurator(prerequisites, args): """Configures the inputs of the autolink-extract action.""" @@ -57,7 +58,8 @@ def register_autolink_extract_action( autolink_file, feature_configuration, object_files, - swift_toolchain): + swift_toolchain, + toolchain_type = SWIFT_TOOLCHAIN_TYPE): """Extracts autolink information from Swift `.o` files. For some platforms (such as Linux), autolinking of imported frameworks is @@ -74,6 +76,9 @@ def register_autolink_extract_action( object_files: The list of object files whose autolink information will be extracted. swift_toolchain: The `SwiftToolchainInfo` provider of the toolchain. + toolchain_type: The toolchain type of the `swift_toolchain` which is + used for the proper selection of the execution platform inside + `run_toolchain_action`. """ prerequisites = struct( autolink_file = autolink_file, @@ -88,4 +93,5 @@ def register_autolink_extract_action( prerequisites = prerequisites, progress_message = "Extracting autolink data for Swift module %{label}", swift_toolchain = swift_toolchain, + toolchain_type = toolchain_type, ) diff --git a/swift/internal/compiling.bzl b/swift/internal/compiling.bzl index 935c97c47..0382e6c41 100644 --- a/swift/internal/compiling.bzl +++ b/swift/internal/compiling.bzl @@ -67,6 +67,7 @@ load( "upcoming_and_experimental_features", ) load(":module_maps.bzl", "write_module_map") +load(":toolchain_utils.bzl", "SWIFT_TOOLCHAIN_TYPE") load( ":utils.bzl", "compact", @@ -145,7 +146,8 @@ def compile_module_interface( swiftinterface_file, swift_infos, swift_toolchain, - target_name): + target_name, + toolchain_type = SWIFT_TOOLCHAIN_TYPE): """Compiles a Swift module interface. Args: @@ -173,6 +175,9 @@ def compile_module_interface( 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. + toolchain_type: The toolchain type of the `swift_toolchain` which is + used for the proper selection of the execution platform inside + `run_toolchain_action`. Returns: A Swift module context (as returned by `create_swift_module_context`) @@ -282,6 +287,7 @@ def compile_module_interface( prerequisites = prerequisites, progress_message = "Compiling Swift module {} from textual interface".format(module_name), swift_toolchain = swift_toolchain, + toolchain_type = toolchain_type, ) module_context = create_swift_module_context( @@ -326,6 +332,7 @@ def compile( swift_infos, swift_toolchain, target_name, + toolchain_type = SWIFT_TOOLCHAIN_TYPE, workspace_name): """Compiles a Swift module. @@ -382,9 +389,15 @@ def compile( these providers are used as dependencies of both the Swift module being compiled and the Clang module for the generated header. swift_toolchain: The `SwiftToolchainInfo` provider of the toolchain. + toolchain_type: A toolchain type of the `swift_toolchain` which is used + for the proper selection of the execution platform inside + `run_toolchain_action`. 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. + toolchain_type: The toolchain type of the `swift_toolchain` which is + used for the proper selection of the execution platform inside + `run_toolchain_action`. 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. @@ -676,6 +689,7 @@ to use swift_common.compile(include_dev_srch_paths = ...) instead.\ prerequisites = prerequisites, progress_message = "Generating derived files for Swift module %{label}", swift_toolchain = swift_toolchain, + toolchain_type = toolchain_type, ) run_toolchain_action( @@ -687,6 +701,7 @@ to use swift_common.compile(include_dev_srch_paths = ...) instead.\ prerequisites = prerequisites, progress_message = "Compiling Swift module %{label}", swift_toolchain = swift_toolchain, + toolchain_type = toolchain_type, ) # Dump AST has to run in its own action because `-dump-ast` is incompatible @@ -704,6 +719,7 @@ to use swift_common.compile(include_dev_srch_paths = ...) instead.\ prerequisites = prerequisites, progress_message = "Dumping Swift AST for %{label}", swift_toolchain = swift_toolchain, + toolchain_type = toolchain_type, ) # If a header and module map were generated for this Swift module, attempt @@ -734,6 +750,7 @@ to use swift_common.compile(include_dev_srch_paths = ...) instead.\ swift_infos = generated_module_deps_swift_infos, swift_toolchain = swift_toolchain, target_name = target_name, + toolchain_type = toolchain_type, ) if pcm_outputs: precompiled_module = pcm_outputs.pcm_file @@ -826,6 +843,7 @@ def precompile_clang_module( module_name, swift_toolchain, target_name, + toolchain_type, swift_infos = []): """Precompiles an explicit Clang module that is compatible with Swift. @@ -852,6 +870,7 @@ def precompile_clang_module( 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. + toolchain_type: The toolchain type of the Swift toolchain. swift_infos: A list of `SwiftInfo` providers representing dependencies required to compile this module. @@ -870,6 +889,7 @@ def precompile_clang_module( swift_infos = swift_infos, swift_toolchain = swift_toolchain, target_name = target_name, + toolchain_type = toolchain_type, ) def _precompile_clang_module( @@ -883,7 +903,8 @@ def _precompile_clang_module( module_name, swift_infos = [], swift_toolchain, - target_name): + target_name, + toolchain_type): """Precompiles an explicit Clang module that is compatible with Swift. Args: @@ -913,6 +934,7 @@ def _precompile_clang_module( 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. + toolchain_type: The toolchain type of the Swift toolchain. Returns: A struct containing the precompiled module and optional indexstore directory, @@ -1009,6 +1031,7 @@ def _precompile_clang_module( prerequisites = prerequisites, progress_message = "Precompiling C module %{label}", swift_toolchain = swift_toolchain, + toolchain_type = toolchain_type, ) return struct( diff --git a/swift/internal/debugging.bzl b/swift/internal/debugging.bzl index c1a9d40b1..cd2cf7ab4 100644 --- a/swift/internal/debugging.bzl +++ b/swift/internal/debugging.bzl @@ -34,7 +34,8 @@ def ensure_swiftmodule_is_embedded( feature_configuration, label, swiftmodule, - swift_toolchain): + swift_toolchain, + toolchain_type): """Ensures that a `.swiftmodule` file is embedded in a library or binary. This function handles the distinctions between how different object file @@ -47,6 +48,9 @@ def ensure_swiftmodule_is_embedded( label: The `Label` of the target being built. swiftmodule: The `.swiftmodule` file to be wrapped. swift_toolchain: The `SwiftToolchainInfo` provider of the toolchain. + toolchain_type: The toolchain type of the `swift_toolchain` which is + used for the proper selection of the execution platform inside + `run_toolchain_action`. Returns: A `LinkerInput` containing any flags and/or input files that should be @@ -78,6 +82,7 @@ def ensure_swiftmodule_is_embedded( "Wrapping {} for debugging".format(swiftmodule.short_path) ), swift_toolchain = swift_toolchain, + toolchain_type = toolchain_type, ) # Passing the `.o` file directly to the linker ensures that it links to diff --git a/swift/internal/interface_synthesizing.bzl b/swift/internal/interface_synthesizing.bzl index 09be55890..f0a67f221 100644 --- a/swift/internal/interface_synthesizing.bzl +++ b/swift/internal/interface_synthesizing.bzl @@ -17,6 +17,7 @@ load("//swift:providers.bzl", "SwiftInfo") load(":action_names.bzl", "SWIFT_ACTION_SYNTHESIZE_INTERFACE") load(":actions.bzl", "run_toolchain_action") +load(":toolchain_utils.bzl", "SWIFT_TOOLCHAIN_TYPE") load(":utils.bzl", "merge_compilation_contexts") def synthesize_interface( @@ -27,7 +28,8 @@ def synthesize_interface( module_name, output_file, swift_infos, - swift_toolchain): + swift_toolchain, + toolchain_type = SWIFT_TOOLCHAIN_TYPE): """Extracts the symbol graph from a Swift module. Args: @@ -46,6 +48,9 @@ def synthesize_interface( target being compiled. This should include both propagated and non-propagated (implementation-only) dependencies. swift_toolchain: The `SwiftToolchainInfo` provider of the toolchain. + toolchain_type: The toolchain type of the `swift_toolchain` which is + used for the proper selection of the execution platform inside + `run_toolchain_action`. """ merged_compilation_context = merge_compilation_contexts( transitive_compilation_contexts = compilation_contexts + [ @@ -92,4 +97,5 @@ def synthesize_interface( "Synthesizing Swift interface for {}".format(module_name) ), swift_toolchain = swift_toolchain, + toolchain_type = toolchain_type, ) diff --git a/swift/internal/linking.bzl b/swift/internal/linking.bzl index d486b3bdd..771336d89 100644 --- a/swift/internal/linking.bzl +++ b/swift/internal/linking.bzl @@ -35,6 +35,7 @@ load( "get_cc_feature_configuration", "is_feature_enabled", ) +load(":toolchain_utils.bzl", "SWIFT_TOOLCHAIN_TYPE") load(":utils.bzl", "get_swift_implicit_deps") def configure_features_for_binary( @@ -138,7 +139,8 @@ def _create_embedded_debugging_linking_context( feature_configuration, label, module_context, - swift_toolchain): + swift_toolchain, + toolchain_type): """Creates a linking context that embeds a .swiftmodule for debugging. Args: @@ -152,6 +154,9 @@ def _create_embedded_debugging_linking_context( Typically, this is the first tuple element in the value returned by `compile`. swift_toolchain: The `SwiftToolchainInfo` provider of the toolchain. + toolchain_type: The toolchain type of the `swift_toolchain` which is + used for the proper selection of the execution platform inside + `run_toolchain_action`. Returns: A valid `CcLinkingContext`, or `None` if no linking context was created. @@ -171,6 +176,7 @@ def _create_embedded_debugging_linking_context( label = label, swiftmodule = module_context.swift.swiftmodule, swift_toolchain = swift_toolchain, + toolchain_type = toolchain_type, ), ] return cc_common.create_linking_context( @@ -193,6 +199,7 @@ def create_linking_context_from_compilation_outputs( module_context, name = None, swift_toolchain, + toolchain_type = SWIFT_TOOLCHAIN_TYPE, user_link_flags = []): """Creates a linking context from the outputs of a Swift compilation. @@ -236,6 +243,9 @@ def create_linking_context_from_compilation_outputs( information about the Swift module that was compiled. Typically, this is the first tuple element in the value returned by `compile`. swift_toolchain: The `SwiftToolchainInfo` provider of the toolchain. + toolchain_type: The toolchain type of the `swift_toolchain` which is + used for the proper selection of the execution platform inside + `run_toolchain_action`. user_link_flags: A `list` of strings containing additional flags that will be passed to the linker for any binary that links with the returned linking context. @@ -260,6 +270,7 @@ def create_linking_context_from_compilation_outputs( label = label, module_context = module_context, swift_toolchain = swift_toolchain, + toolchain_type = toolchain_type, ) if debugging_linking_context: extra_linking_contexts.append(debugging_linking_context) @@ -375,6 +386,7 @@ def register_link_binary_action( output_type, stamp, swift_toolchain, + toolchain_type = SWIFT_TOOLCHAIN_TYPE, user_link_flags = [], variables_extension = {}): """Registers an action that invokes the linker to produce a binary. @@ -407,6 +419,9 @@ def register_link_binary_action( stamping is enabled. See `cc_common.link` for details about the behavior of this argument. swift_toolchain: The `SwiftToolchainInfo` provider of the toolchain. + toolchain_type: The toolchain type of the `swift_toolchain` which is + used for the proper selection of the execution platform inside + `run_toolchain_action`. user_link_flags: Additional flags passed to the linker. Any `$(location ...)` placeholders are assumed to have already been expanded. @@ -435,6 +450,7 @@ def register_link_binary_action( label = label, module_context = module_context, swift_toolchain = swift_toolchain, + toolchain_type = toolchain_type, ) if debugging_linking_context: linking_contexts.append(debugging_linking_context) diff --git a/swift/internal/symbol_graph_extracting.bzl b/swift/internal/symbol_graph_extracting.bzl index 07ae5d1ff..2cba37cd0 100644 --- a/swift/internal/symbol_graph_extracting.bzl +++ b/swift/internal/symbol_graph_extracting.bzl @@ -17,6 +17,7 @@ load("//swift:providers.bzl", "SwiftInfo") load(":action_names.bzl", "SWIFT_ACTION_SYMBOL_GRAPH_EXTRACT") load(":actions.bzl", "run_toolchain_action") +load(":toolchain_utils.bzl", "SWIFT_TOOLCHAIN_TYPE") load(":utils.bzl", "merge_compilation_contexts") def extract_symbol_graph( @@ -30,7 +31,8 @@ def extract_symbol_graph( module_name, output_dir, swift_infos, - swift_toolchain): + swift_toolchain, + toolchain_type = SWIFT_TOOLCHAIN_TYPE): """Extracts the symbol graph from a Swift module. Args: @@ -62,6 +64,9 @@ def extract_symbol_graph( target being compiled. This should include both propagated and non-propagated (implementation-only) dependencies. swift_toolchain: The `SwiftToolchainInfo` provider of the toolchain. + toolchain_type: The toolchain type of the `swift_toolchain` which is + used for the proper selection of the execution platform inside + `run_toolchain_action`. """ merged_compilation_context = merge_compilation_contexts( transitive_compilation_contexts = compilation_contexts + [ @@ -116,4 +121,5 @@ def extract_symbol_graph( "Extracting symbol graph for {}".format(module_name) ), swift_toolchain = swift_toolchain, + toolchain_type = toolchain_type, ) diff --git a/swift/swift_clang_module_aspect.bzl b/swift/swift_clang_module_aspect.bzl index 066cae727..c4b3c5174 100644 --- a/swift/swift_clang_module_aspect.bzl +++ b/swift/swift_clang_module_aspect.bzl @@ -42,6 +42,7 @@ load( ) load( "//swift/internal:toolchain_utils.bzl", + "SWIFT_TOOLCHAIN_TYPE", "get_swift_toolchain", "use_swift_toolchain", ) @@ -298,6 +299,7 @@ def _handle_module( direct_swift_infos, swift_infos, swift_toolchain, + toolchain_type, target): """Processes a C/Objective-C target that is a dependency of a Swift target. @@ -319,6 +321,7 @@ def _handle_module( 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. + toolchain_type: The toolchain type of the Swift toolchain. Returns: A list of providers that should be returned by the aspect. @@ -413,6 +416,7 @@ def _handle_module( swift_infos = swift_infos, swift_toolchain = swift_toolchain, target_name = target.label.name, + toolchain_type = toolchain_type, ) precompiled_module = getattr(pcm_outputs, "pcm_file", None) pcm_indexstore = getattr(pcm_outputs, "indexstore_directory", None) @@ -455,6 +459,7 @@ def _handle_module( overlay_info = overlay_info, swift_infos = swift_infos_for_overlay, swift_toolchain = swift_toolchain, + toolchain_type = toolchain_type, ) overlay_swift_info = overlay_compile_result.swift_info overlay_swift_module = overlay_swift_info.direct_modules[0].swift @@ -495,7 +500,8 @@ def _compile_swift_overlay( module_name, overlay_info, swift_infos, - swift_toolchain): + swift_toolchain, + toolchain_type): """Compiles Swift code to be used as an overlay for a C/Objective-C module. Args: @@ -510,6 +516,7 @@ def _compile_swift_overlay( dependencies of both the original C/Objective-C target and the Swift overlay. swift_toolchain: The Swift toolchain to use when compiling. + toolchain_type: The toolchain type of the Swift toolchain. Returns: The compilation result, as returned by `swift_common.compile`. @@ -540,6 +547,7 @@ def _compile_swift_overlay( swift_infos = swift_infos, swift_toolchain = swift_toolchain, target_name = overlay_info.label.name, + toolchain_type = toolchain_type, workspace_name = aspect_ctx.workspace_name, ) linking_context, _ = ( @@ -565,6 +573,7 @@ def _compile_swift_overlay( ], module_context = compile_result.module_context, swift_toolchain = swift_toolchain, + toolchain_type = toolchain_type, user_link_flags = overlay_info.linkopts, ) ) @@ -706,7 +715,7 @@ def _find_swift_overlay_compile_info(aspect_ctx): return overlay_target[SwiftOverlayCompileInfo] return None -def _swift_clang_module_aspect_impl(target, aspect_ctx): +def _swift_clang_module_aspect_impl(target, aspect_ctx, toolchain_type): providers = [SwiftClangModuleAspectInfo()] # Do nothing if the target already propagates `SwiftInfo`. @@ -738,6 +747,7 @@ def _swift_clang_module_aspect_impl(target, aspect_ctx): swift_toolchain = get_swift_toolchain( aspect_ctx, + toolchain_type = toolchain_type, ) feature_configuration = configure_features( ctx = aspect_ctx, @@ -756,6 +766,7 @@ def _swift_clang_module_aspect_impl(target, aspect_ctx): direct_swift_infos = direct_swift_infos, swift_infos = swift_infos, swift_toolchain = swift_toolchain, + toolchain_type = toolchain_type, target = target, ) @@ -769,9 +780,26 @@ def _swift_clang_module_aspect_impl(target, aspect_ctx): return providers -swift_clang_module_aspect = aspect( - attr_aspects = _MULTIPLE_TARGET_ASPECT_ATTRS, - doc = """\ +def make_swift_clang_module_aspect(*, toolchain_type): + """Creates a `swift_clang_module_aspect` with the given toolchain type. + + Args: + toolchain_type: The toolchain type of the Swift toolchain. + + Returns: + A `swift_clang_module_aspect` with the given toolchain type. + """ + + def _impl(target, aspect_ctx): + return _swift_clang_module_aspect_impl( + target = target, + aspect_ctx = aspect_ctx, + toolchain_type = toolchain_type, + ) + + return aspect( + attr_aspects = _MULTIPLE_TARGET_ASPECT_ATTRS, + doc = """\ Propagates unified `SwiftInfo` providers for targets that represent C/Objective-C modules. @@ -810,12 +838,18 @@ it propagates for its targets. suppressed (for example, using the `no_module` aspect hint). In this case, transitive dependency information is intentionally discarded. """, - fragments = ["cpp"], - implementation = _swift_clang_module_aspect_impl, - provides = [SwiftClangModuleAspectInfo], - required_aspect_providers = [ - [apple_common.Objc], - [CcInfo], - ], - toolchains = use_swift_toolchain(), + fragments = ["cpp"], + implementation = _impl, + provides = [SwiftClangModuleAspectInfo], + required_aspect_providers = [ + [apple_common.Objc], + [CcInfo], + ], + toolchains = use_swift_toolchain( + toolchain_type = toolchain_type, + ), + ) + +swift_clang_module_aspect = make_swift_clang_module_aspect( + toolchain_type = SWIFT_TOOLCHAIN_TYPE, ) From c1ad52dbb3f6ec4c505a761e62c922cdcc1e315b Mon Sep 17 00:00:00 2001 From: Luis Padron Date: Thu, 29 May 2025 13:38:54 -0400 Subject: [PATCH 87/92] Upstream: Ensure that `swift_synthesize_interface_aspect` generates unique outputs (#1526) Cherry-pick: https://github.com/bazelbuild/rules_swift/commit/634d0d16608e03d6dde9d23a517c2b9b40ce637c The aspect generated a file named `.synthesized.swift`, which was incorrect if a target had multiple modules in its `SwiftInfo.direct_modules` field. It would also be incorrect to just switch to `.synthesized.swift`, because two targets could potentially re-export the same module. To fully disambiguate, we create an intermediate directory named for the target, and then use the module name as the filename in that directory. PiperOrigin-RevId: 740414505 Co-authored-by: Tony Allevato --- swift/swift_synthesize_interface_aspect.bzl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/swift/swift_synthesize_interface_aspect.bzl b/swift/swift_synthesize_interface_aspect.bzl index c38aeccdd..f69aa8b6c 100644 --- a/swift/swift_synthesize_interface_aspect.bzl +++ b/swift/swift_synthesize_interface_aspect.bzl @@ -60,7 +60,10 @@ def _swift_synthesize_interface_aspect_impl(target, aspect_ctx): for module in swift_info.direct_modules: output_file = aspect_ctx.actions.declare_file( - "{}.synthesized.swift".format(target.label.name), + "{}.synthesized_interfaces/{}.synthesized.swift".format( + target.label.name, + module.name, + ), ) direct_outputs.append(output_file) synthesize_interface( From 26cd607f1053ac829bfc9584424b55dc9cb2f8cd Mon Sep 17 00:00:00 2001 From: Luis Padron Date: Thu, 29 May 2025 14:02:30 -0400 Subject: [PATCH 88/92] Upstream: Add swift_common.is_action_enabled as a public API. (#1523) This allows callers to verify whether certain actions that are compiler-version-dependent, like `swift-synthesize-interface`, can be called by a rule or aspect. Cherry-picks: - https://github.com/bazelbuild/rules_swift/commit/d2d94bfc2a7b0da7a92bbca77d690bcb5008872d - https://github.com/bazelbuild/rules_swift/commit/59000d5e1932e7ac3fb67f4476516ae75b80551b - https://github.com/bazelbuild/rules_swift/commit/9e7c7c42a50e1451a43fa645343da25a374d0f39 --------- Co-authored-by: Thomas Van Lenten Co-authored-by: Tony Allevato --- doc/api.md | 30 ++++++++++++++++++++++++++++++ swift/BUILD | 1 + swift/internal/actions.bzl | 9 ++++++++- swift/internal/toolchain_utils.bzl | 2 +- swift/providers.bzl | 12 ++++++++++++ swift/swift_common.bzl | 5 +++++ 6 files changed, 57 insertions(+), 2 deletions(-) diff --git a/doc/api.md b/doc/api.md index fa78c9e0f..feca0f33a 100644 --- a/doc/api.md +++ b/doc/api.md @@ -463,6 +463,36 @@ A `SwiftToolchainInfo` provider, or `None` if the toolchain was not found and not required. + + +## swift_common.is_action_enabled + +
+swift_common.is_action_enabled(action_name, swift_toolchain)
+
+ +Returns True if the given action is enabled in the Swift toolchain. + +This function should be used before invoking APIs that invoke actions that +might not be available depending on the version of the Swift toolchain. For +example, `SwiftSynthesizeInterface` actions (created by calling +`swift_common.synthesize_interface`) are only available starting from Swift +6.1. + + +**PARAMETERS** + + +| Name | Description | Default Value | +| :------------- | :------------- | :------------- | +| action_name | The name of the action, which corresponds to the action's mnemonic (for example, `SwiftSynthesizeInterface`). | none | +| swift_toolchain | The Swift toolchain being used to build. | none | + +**RETURNS** + +True if the action is enabled, or False if it is not. + + ## swift_common.is_enabled diff --git a/swift/BUILD b/swift/BUILD index b0a262d00..17c8149bb 100644 --- a/swift/BUILD +++ b/swift/BUILD @@ -94,6 +94,7 @@ bzl_library( name = "swift_common", srcs = ["swift_common.bzl"], deps = [ + "//swift/internal:actions", "//swift/internal:compiling", "//swift/internal:features", "//swift/internal:interface_synthesizing", diff --git a/swift/internal/actions.bzl b/swift/internal/actions.bzl index 0843c543a..f01d6d18d 100644 --- a/swift/internal/actions.bzl +++ b/swift/internal/actions.bzl @@ -116,8 +116,15 @@ def _apply_action_configs( def is_action_enabled(action_name, swift_toolchain): """Returns True if the given action is enabled in the Swift toolchain. + This function should be used before invoking APIs that invoke actions that + might not be available depending on the version of the Swift toolchain. For + example, `SwiftSynthesizeInterface` actions (created by calling + `swift_common.synthesize_interface`) are only available starting from Swift + 6.1. + Args: - action_name: The name of the action. + action_name: The name of the action, which corresponds to the action's + mnemonic (for example, `SwiftSynthesizeInterface`). swift_toolchain: The Swift toolchain being used to build. Returns: diff --git a/swift/internal/toolchain_utils.bzl b/swift/internal/toolchain_utils.bzl index 2b0fbe375..ba4a97b85 100644 --- a/swift/internal/toolchain_utils.bzl +++ b/swift/internal/toolchain_utils.bzl @@ -51,7 +51,7 @@ def get_swift_toolchain( if group and toolchain_type in group.toolchains: return group.toolchains[toolchain_type].swift_toolchain - if toolchain_type in ctx.toolchains: + if toolchain_type in ctx.toolchains and ctx.toolchains[toolchain_type]: return ctx.toolchains[toolchain_type].swift_toolchain # TODO(b/205018581): Delete this code path when migration to the new diff --git a/swift/providers.bzl b/swift/providers.bzl index e0e069ba1..f7ab88f9e 100644 --- a/swift/providers.bzl +++ b/swift/providers.bzl @@ -83,6 +83,18 @@ def _swift_info_init( direct_swift_infos = [], modules = [], swift_infos = []): + """Creates a `SwiftInfo` provider from the given arguments. + + Args: + 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 `create_swift_module_context()`) + 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. + """ direct_modules = modules + [ module for provider in direct_swift_infos diff --git a/swift/swift_common.bzl b/swift/swift_common.bzl index 107754deb..132ad3994 100644 --- a/swift/swift_common.bzl +++ b/swift/swift_common.bzl @@ -20,6 +20,10 @@ example, `swift_proto_library` generates Swift source code from `.proto` files and then needs to compile them. This module provides that lower-level interface. """ +load( + "//swift/internal:actions.bzl", + "is_action_enabled", +) load( "//swift/internal:compiling.bzl", "compile", @@ -62,6 +66,7 @@ swift_common = struct( create_linking_context_from_compilation_outputs = create_linking_context_from_compilation_outputs, extract_symbol_graph = extract_symbol_graph, get_toolchain = get_swift_toolchain, + is_action_enabled = is_action_enabled, is_enabled = is_feature_enabled, precompile_clang_module = precompile_clang_module, synthesize_interface = synthesize_interface, From 81c1342d3ec7eafdf25c5b03ebb9c672ca18e92b Mon Sep 17 00:00:00 2001 From: Luis Padron Date: Sun, 1 Jun 2025 22:01:45 -0400 Subject: [PATCH 89/92] Fix build failures and update CI (#1528) - Fix some CI todos - Update CI to test on previous LTS and Bazel 8 for supported platforms - Add a patch to stardoc to fix us having to depend on an old version (this patch fixes `load` statements being added to the docs which point to the doc aliases instead of the actual rules) - We should push stardoc to support something like this and/or fix alias support - Fix bad dependency version on `bazel_features`. It needs to include a version with `bazel_features.cc.fixed_dsym_path_quoting` --- .bazelci/presubmit.yml | 45 +++--- MODULE.bazel | 23 +-- WORKSPACE | 6 + doc/api.md | 29 ++-- doc/providers.md | 14 +- doc/rules.md | 4 +- swift/internal/BUILD | 6 - swift/repositories.bzl | 14 +- third_party/patches/BUILD.bazel | 1 + ...ardoc-revert-load-statements-in-docs.patch | 139 ++++++++++++++++++ 10 files changed, 210 insertions(+), 71 deletions(-) create mode 100644 third_party/patches/BUILD.bazel create mode 100644 third_party/patches/stardoc-revert-load-statements-in-docs.patch diff --git a/.bazelci/presubmit.yml b/.bazelci/presubmit.yml index e1601eae7..b0a84232a 100644 --- a/.bazelci/presubmit.yml +++ b/.bazelci/presubmit.yml @@ -48,22 +48,19 @@ x_defaults: - "//tools/..." tasks: - # TODO: Uncomment once Bazel 8 is released - # macos_7: - # name: "Previous LTS" - # bazel: 7.x - # <<: *mac_common + macos_7: + name: "Previous LTS with no bzlmod" + bazel: 7.x + build_flags: + - "--noenable_bzlmod" + - "--enable_workspace" + <<: *mac_common macos_latest: name: "Current LTS" bazel: latest <<: *mac_common - macos_latest_bzlmod: - name: "Current LTS using bzlmod" - bazel: latest - <<: *mac_common - macos_last_green: name: "Last Green Bazel" bazel: last_green @@ -78,15 +75,17 @@ tasks: - .bazelci/update_workspace_to_deps_heads.sh <<: *mac_common - # TODO: Uncomment once Bazel 8 is released - # ubuntu2004_7: - # name: "Previous LTS" - # bazel: 7.x - # shell_commands: - # - "echo --- Downloading and extracting Swift $SWIFT_VERSION to $SWIFT_HOME" - # - "mkdir $SWIFT_HOME" - # - "curl https://download.swift.org/swift-${SWIFT_VERSION}-release/ubuntu2004/swift-${SWIFT_VERSION}-RELEASE/swift-${SWIFT_VERSION}-RELEASE-ubuntu20.04.tar.gz | tar xvz --strip-components=1 -C $SWIFT_HOME" - # <<: *linux_common + ubuntu2004_7: + name: "Previous LTS with no bzlmod" + bazel: 7.x + shell_commands: + - "echo --- Downloading and extracting Swift $SWIFT_VERSION to $SWIFT_HOME" + - "mkdir $SWIFT_HOME" + - "curl https://download.swift.org/swift-${SWIFT_VERSION}-release/ubuntu2004/swift-${SWIFT_VERSION}-RELEASE/swift-${SWIFT_VERSION}-RELEASE-ubuntu20.04.tar.gz | tar xvz --strip-components=1 -C $SWIFT_HOME" + build_flags: + - "--noenable_bzlmod" + - "--enable_workspace" + <<: *linux_common ubuntu2004_latest: name: "Current LTS" @@ -126,8 +125,7 @@ tasks: doc_tests: name: "Doc tests" - # TODO: Bump when https://github.com/bazelbuild/stardoc/issues/94#issuecomment-2492218404 is resolved - bazel: 7.x + bazel: latest <<: *linux_environment shell_commands: - "echo --- Downloading and extracting Swift $SWIFT_VERSION to $SWIFT_HOME" @@ -135,9 +133,6 @@ tasks: - "curl https://download.swift.org/swift-${SWIFT_VERSION}-release/ubuntu2004/swift-${SWIFT_VERSION}-RELEASE/swift-${SWIFT_VERSION}-RELEASE-ubuntu20.04.tar.gz | tar xvz --strip-components=1 -C $SWIFT_HOME" test_targets: - "doc/..." - # TODO: Remove this once stardoc is updated to 0.7.1+ (https://github.com/bazelbuild/stardoc/pull/238) - test_flags: - - "--action_env=PATH" - - "--noincompatible_disallow_empty_glob" +# TODO: Update to version 8+ and fix errors buildifier: 6.3.2 diff --git a/MODULE.bazel b/MODULE.bazel index 7288ee8e4..ff0fba626 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -6,13 +6,13 @@ module( repo_name = "build_bazel_rules_swift", ) -bazel_dep(name = "bazel_features", version = "1.9.0") +bazel_dep(name = "bazel_features", version = "1.10.0") bazel_dep(name = "bazel_skylib", version = "1.3.0") bazel_dep(name = "apple_support", version = "1.21.0", repo_name = "build_bazel_apple_support") bazel_dep(name = "rules_cc", version = "0.0.2") bazel_dep(name = "rules_shell", version = "0.3.0") bazel_dep(name = "platforms", version = "0.0.9") -bazel_dep(name = "protobuf", version = "21.7", repo_name = "com_google_protobuf") +bazel_dep(name = "protobuf", version = "23.1", repo_name = "com_google_protobuf") bazel_dep(name = "rules_proto", version = "5.3.0-21.7") bazel_dep(name = "nlohmann_json", version = "3.6.1", repo_name = "com_github_nlohmann_json") bazel_dep( @@ -45,7 +45,7 @@ register_toolchains("@build_bazel_rules_swift_local_config//:all") # Dev dependencies bazel_dep(name = "bazel_skylib_gazelle_plugin", version = "1.5.0", dev_dependency = True) bazel_dep(name = "gazelle", version = "0.33.0", dev_dependency = True, repo_name = "bazel_gazelle") -bazel_dep(name = "stardoc", version = "0.6.2", dev_dependency = True, repo_name = "io_bazel_stardoc") +bazel_dep(name = "stardoc", version = "0.7.1", dev_dependency = True, repo_name = "io_bazel_stardoc") http_archive = use_repo_rule("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") @@ -57,19 +57,10 @@ http_archive( url = "https://github.com/apple/swift-syntax/archive/01fc3e3ed4d26121c06790abf8fe5ddaa22a4cc5.tar.gz", ) -# TODO: Remove override when a protobuf release is available that supports -# Bazel 8 -archive_override( - module_name = "protobuf", - integrity = "sha256-+dloYVexGlGsxKLTARuU4KXZ5ORo/BWPR6obFk73d+Q=", - strip_prefix = "protobuf-b93b8e5f64ed922d101759380d7c6a2bbe474e26", - urls = ["https://github.com/protocolbuffers/protobuf/archive/b93b8e5f64ed922d101759380d7c6a2bbe474e26.zip"], -) - -# TODO: Remove override when a protobuf release that marks `stardoc` as a -# dev_dependency is available, until then it's upgrading our stardoc version -# so override it here. +# TODO: In stardoc 0.7.1+, the `load` statements added to the docs are relative to the `alias` targets which is incorrect. +# To keep the docs without confusing load statements we patch a partial revert of: https://github.com/bazelbuild/stardoc/pull/216 single_version_override( module_name = "stardoc", - version = "0.6.2", + patch_strip = 1, + patches = ["//third_party/patches:stardoc-revert-load-statements-in-docs.patch"], ) diff --git a/WORKSPACE b/WORKSPACE index 89e409cfd..05bbc27d0 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -75,3 +75,9 @@ bazel_skylib_gazelle_plugin_workspace() load("@bazel_skylib_gazelle_plugin//:setup.bzl", "bazel_skylib_gazelle_plugin_setup") bazel_skylib_gazelle_plugin_setup() + +load("@rules_shell//shell:repositories.bzl", "rules_shell_dependencies", "rules_shell_toolchains") + +rules_shell_dependencies() + +rules_shell_toolchains() diff --git a/doc/api.md b/doc/api.md index feca0f33a..e06c35d65 100644 --- a/doc/api.md +++ b/doc/api.md @@ -13,8 +13,8 @@ module. ## create_swift_interop_info
-create_swift_interop_info(exclude_headers, module_map, module_name, requested_features, suppressed,
-                          swift_infos, unsupported_features)
+create_swift_interop_info(*, exclude_headers, module_map, module_name, requested_features,
+                          suppressed, swift_infos, unsupported_features)
 
Returns a provider that lets a target expose C/Objective-C APIs to Swift. @@ -74,7 +74,7 @@ A provider whose type/layout is an implementation detail and should not ## derive_swift_module_name
-derive_swift_module_name(args)
+derive_swift_module_name(*args)
 
Returns a derived module name from the given build label. @@ -164,7 +164,7 @@ A C++ `FeatureConfiguration` value (see ## swift_common.compile
-swift_common.compile(actions, additional_inputs, cc_infos, copts, defines, exec_group,
+swift_common.compile(*, actions, additional_inputs, cc_infos, copts, defines, exec_group,
                      extra_swift_infos, feature_configuration, generated_header_name, is_test,
                      include_dev_srch_paths, module_name, package_name, plugins, private_cc_infos,
                      private_swift_infos, srcs, swift_infos, swift_toolchain, target_name,
@@ -250,7 +250,7 @@ A `struct` with the following fields:
 ## swift_common.compile_module_interface
 
 
-swift_common.compile_module_interface(actions, clang_module, compilation_contexts, copts,
+swift_common.compile_module_interface(*, actions, clang_module, compilation_contexts, copts,
                                       exec_group, feature_configuration, is_framework, module_name,
                                       swiftinterface_file, swift_infos, swift_toolchain, target_name,
                                       toolchain_type)
@@ -292,7 +292,7 @@ A Swift module context (as returned by `create_swift_module_context`)
 ## 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. @@ -359,8 +359,8 @@ A `struct` containing four fields: ## swift_common.create_linking_context_from_compilation_outputs
-swift_common.create_linking_context_from_compilation_outputs(actions, additional_inputs, alwayslink,
-                                                             compilation_outputs,
+swift_common.create_linking_context_from_compilation_outputs(*, actions, additional_inputs,
+                                                             alwayslink, compilation_outputs,
                                                              feature_configuration, is_test,
                                                              include_dev_srch_paths, label,
                                                              linking_contexts, module_context, name,
@@ -410,7 +410,7 @@ A tuple of `(CcLinkingContext, CcLinkingOutputs)` containing the linking
 ## swift_common.extract_symbol_graph
 
 
-swift_common.extract_symbol_graph(actions, compilation_contexts, emit_extension_block_symbols,
+swift_common.extract_symbol_graph(*, actions, compilation_contexts, emit_extension_block_symbols,
                                   feature_configuration, include_dev_srch_paths, minimum_access_level,
                                   module_name, output_dir, swift_infos, swift_toolchain,
                                   toolchain_type)
@@ -441,7 +441,7 @@ Extracts the symbol graph from a Swift module.
 ## swift_common.get_toolchain
 
 
-swift_common.get_toolchain(ctx, exec_group, mandatory, toolchain_type, attr)
+swift_common.get_toolchain(ctx, *, exec_group, mandatory, toolchain_type, attr)
 
Gets the Swift toolchain associated with the rule or aspect. @@ -526,7 +526,7 @@ check it. ## swift_common.precompile_clang_module
-swift_common.precompile_clang_module(actions, cc_compilation_context, exec_group,
+swift_common.precompile_clang_module(*, actions, cc_compilation_context, exec_group,
                                      feature_configuration, module_map_file, module_name,
                                      swift_toolchain, target_name, toolchain_type, swift_infos)
 
@@ -560,8 +560,9 @@ A struct containing the precompiled module and optional indexstore directory, ## swift_common.synthesize_interface
-swift_common.synthesize_interface(actions, compilation_contexts, feature_configuration, module_name,
-                                  output_file, swift_infos, swift_toolchain, toolchain_type)
+swift_common.synthesize_interface(*, actions, compilation_contexts, feature_configuration,
+                                  module_name, output_file, swift_infos, swift_toolchain,
+                                  toolchain_type)
 
Extracts the symbol graph from a Swift module. @@ -586,7 +587,7 @@ Extracts the symbol graph from a Swift module. ## swift_common.use_toolchain
-swift_common.use_toolchain(mandatory, toolchain_type)
+swift_common.use_toolchain(*, mandatory, toolchain_type)
 
Returns a list of toolchain types needed to use the Swift toolchain. diff --git a/doc/providers.md b/doc/providers.md index b905e6783..cc8e6f712 100644 --- a/doc/providers.md +++ b/doc/providers.md @@ -17,7 +17,7 @@ On this page: ## SwiftInfo
-SwiftInfo(direct_modules, transitive_modules)
+SwiftInfo(*, direct_swift_infos, modules, swift_infos)
 
Contains information about the compiled artifacts of a Swift module. @@ -48,8 +48,15 @@ where the arguments are: When reading an existing `SwiftInfo` provider, it has the two fields described below. -**FIELDS** +**CONSTRUCTOR 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 `create_swift_module_context()`) 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. | `[]` | +**FIELDS** | Name | Description | | :------------- | :------------- | @@ -69,7 +76,6 @@ Provides information needed to generate Swift code from `ProtoInfo` providers **FIELDS** - | Name | Description | | :------------- | :------------- | | bundled_proto_paths | List of proto paths for which to skip generation because they're built into the modules imported by the generated Swift proto code, e.g., SwiftProtobuf. | @@ -90,7 +96,6 @@ Propagates Swift-specific information about a `proto_library`. **FIELDS** - | Name | Description | | :------------- | :------------- | | module_name | The name of the Swift module compiled from the `swift_proto_library` which produced this provider. | @@ -117,7 +122,6 @@ that use the toolchain. **FIELDS** - | Name | Description | | :------------- | :------------- | | action_configs | This field is an internal implementation detail of the build rules. | diff --git a/doc/rules.md b/doc/rules.md index 0a76612b3..6c8951cc9 100644 --- a/doc/rules.md +++ b/doc/rules.md @@ -1066,11 +1066,11 @@ swift_library( ## mixed_language_library
-mixed_language_library(name, alwayslink, clang_copts, clang_defines, clang_srcs, data,
+mixed_language_library(*, name, alwayslink, clang_copts, clang_defines, clang_srcs, data,
                        enable_modules, hdrs, includes, linkopts, module_map, module_name,
                        non_arc_srcs, package_name, private_deps, sdk_dylibs, sdk_frameworks,
                        swift_copts, swift_defines, swift_plugins, swift_srcs, swiftc_inputs,
-                       textual_hdrs, umbrella_header, weak_sdk_frameworks, deps, kwargs)
+                       textual_hdrs, umbrella_header, weak_sdk_frameworks, deps, **kwargs)
 
Creates a mixed language library from a Clang and Swift library target pair. diff --git a/swift/internal/BUILD b/swift/internal/BUILD index 89dc87669..9b7e491ae 100644 --- a/swift/internal/BUILD +++ b/swift/internal/BUILD @@ -229,12 +229,6 @@ bzl_library( ], ) -bzl_library( - name = "runfiles", - srcs = ["runfiles.bzl"], - visibility = ["//swift:__subpackages__"], -) - bzl_library( name = "swift_autoconfiguration", srcs = ["swift_autoconfiguration.bzl"], diff --git a/swift/repositories.bzl b/swift/repositories.bzl index 51d649f6d..38cde3338 100644 --- a/swift/repositories.bzl +++ b/swift/repositories.bzl @@ -87,9 +87,9 @@ def swift_rules_dependencies(include_bzlmod_ready_dependencies = True): _maybe( http_archive, name = "bazel_features", - sha256 = "53182a68f172a2af4ad37051f82201e222bc19f7a40825b877da3ff4c922b9e0", - strip_prefix = "bazel_features-1.3.0", - url = "https://github.com/bazel-contrib/bazel_features/releases/download/v1.3.0/bazel_features-v1.3.0.tar.gz", + sha256 = "95fb3cfd11466b4cad6565e3647a76f89886d875556a4b827c021525cb2482bb", + strip_prefix = "bazel_features-1.10.0", + url = "https://github.com/bazel-contrib/bazel_features/releases/download/v1.10.0/bazel_features-v1.10.0.tar.gz", ) _maybe( @@ -246,6 +246,14 @@ def swift_rules_dependencies(include_bzlmod_ready_dependencies = True): sha256 = "9a54fc1674af6031125a9884480a1e31e1bcf48b8f558b3e8bcc6b6fcd6e8b61", ) + _maybe( + http_archive, + name = "rules_shell", + sha256 = "d8cd4a3a91fc1dc68d4c7d6b655f09def109f7186437e3f50a9b60ab436a0c53", + strip_prefix = "rules_shell-0.3.0", + url = "https://github.com/bazelbuild/rules_shell/releases/download/v0.3.0/rules_shell-v0.3.0.tar.gz", + ) + _maybe( swift_autoconfiguration, name = "build_bazel_rules_swift_local_config", diff --git a/third_party/patches/BUILD.bazel b/third_party/patches/BUILD.bazel new file mode 100644 index 000000000..d51811044 --- /dev/null +++ b/third_party/patches/BUILD.bazel @@ -0,0 +1 @@ +exports_files(glob(["*.patch"])) diff --git a/third_party/patches/stardoc-revert-load-statements-in-docs.patch b/third_party/patches/stardoc-revert-load-statements-in-docs.patch new file mode 100644 index 000000000..3abed7ce5 --- /dev/null +++ b/third_party/patches/stardoc-revert-load-statements-in-docs.patch @@ -0,0 +1,139 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Luis Padron +Date: Fri, 30 May 2025 21:55:02 -0400 +Subject: Revert load statements in docs + + +diff --git a/stardoc/templates/html_tables/aspect.vm b/stardoc/templates/html_tables/aspect.vm +index 7033279..e66b2b2 100644 +--- a/stardoc/templates/html_tables/aspect.vm ++++ b/stardoc/templates/html_tables/aspect.vm +@@ -3,8 +3,6 @@ + #[[##]]# ${aspectName} + +
+-${util.loadStatement($aspectName)}
+-
+ ${util.aspectSummary($aspectName, $aspectInfo)}
+ 
+ +diff --git a/stardoc/templates/html_tables/func.vm b/stardoc/templates/html_tables/func.vm +index 7d9191d..b52e5bc 100644 +--- a/stardoc/templates/html_tables/func.vm ++++ b/stardoc/templates/html_tables/func.vm +@@ -3,8 +3,6 @@ + #[[##]]# ${funcInfo.functionName} + +
+-${util.loadStatement($funcInfo.functionName)}
+-
+ ${util.funcSummary($funcInfo)}
+ 
+ +diff --git a/stardoc/templates/html_tables/provider.vm b/stardoc/templates/html_tables/provider.vm +index 4684676..a2919b0 100644 +--- a/stardoc/templates/html_tables/provider.vm ++++ b/stardoc/templates/html_tables/provider.vm +@@ -8,8 +8,6 @@ + #[[##]]# ${providerName} + +
+-${util.loadStatement($providerName)}
+-
+ #if ($providerInfo.hasInit() && !$mergeParamsAndFields)
+ ${util.providerSummaryWithInitAnchor($providerName, $providerInfo)}
+ #else
+diff --git a/stardoc/templates/html_tables/repository_rule.vm b/stardoc/templates/html_tables/repository_rule.vm
+index 90df09b..2378562 100644
+--- a/stardoc/templates/html_tables/repository_rule.vm
++++ b/stardoc/templates/html_tables/repository_rule.vm
+@@ -3,8 +3,6 @@
+ #[[##]]# ${ruleName}
+ 
+ 
+-${util.loadStatement($ruleName)}
+-
+ ${util.repositoryRuleSummary($ruleName, $ruleInfo)}
+ 
+ #if (!$ruleInfo.docString.isEmpty()) +diff --git a/stardoc/templates/html_tables/rule.vm b/stardoc/templates/html_tables/rule.vm +index 546e0c1..0d5b638 100644 +--- a/stardoc/templates/html_tables/rule.vm ++++ b/stardoc/templates/html_tables/rule.vm +@@ -3,8 +3,6 @@ + #[[##]]# ${ruleName} + +
+-${util.loadStatement($ruleName)}
+-
+ ${util.ruleSummary($ruleName, $ruleInfo)}
+ 
+ +diff --git a/stardoc/templates/markdown_tables/aspect.vm b/stardoc/templates/markdown_tables/aspect.vm +index 327bf10..36aa47a 100644 +--- a/stardoc/templates/markdown_tables/aspect.vm ++++ b/stardoc/templates/markdown_tables/aspect.vm +@@ -3,8 +3,6 @@ + #[[##]]# ${aspectName} + +
+-${util.loadStatement($aspectName)}
+-
+ ${util.aspectSummary($aspectName, $aspectInfo)}
+ 
+ +diff --git a/stardoc/templates/markdown_tables/func.vm b/stardoc/templates/markdown_tables/func.vm +index 5d529fc..e53b81a 100644 +--- a/stardoc/templates/markdown_tables/func.vm ++++ b/stardoc/templates/markdown_tables/func.vm +@@ -3,8 +3,6 @@ + #[[##]]# ${funcInfo.functionName} + +
+-${util.loadStatement($funcInfo.functionName)}
+-
+ ${util.funcSummary($funcInfo)}
+ 
+ +diff --git a/stardoc/templates/markdown_tables/provider.vm b/stardoc/templates/markdown_tables/provider.vm +index a198ac5..f623e0d 100644 +--- a/stardoc/templates/markdown_tables/provider.vm ++++ b/stardoc/templates/markdown_tables/provider.vm +@@ -8,8 +8,6 @@ + #[[##]]# ${providerName} + +
+-${util.loadStatement($providerName)}
+-
+ #if ($providerInfo.hasInit() && !$mergeParamsAndFields)
+ ${util.providerSummaryWithInitAnchor($providerName, $providerInfo)}
+ #else
+diff --git a/stardoc/templates/markdown_tables/repository_rule.vm b/stardoc/templates/markdown_tables/repository_rule.vm
+index 579e76a..4473f01 100644
+--- a/stardoc/templates/markdown_tables/repository_rule.vm
++++ b/stardoc/templates/markdown_tables/repository_rule.vm
+@@ -3,8 +3,6 @@
+ #[[##]]# ${ruleName}
+ 
+ 
+-${util.loadStatement($ruleName)}
+-
+ ${util.repositoryRuleSummary($ruleName, $ruleInfo)}
+ 
+ #if (!$ruleInfo.docString.isEmpty()) +diff --git a/stardoc/templates/markdown_tables/rule.vm b/stardoc/templates/markdown_tables/rule.vm +index 3b94cd5..0b73239 100644 +--- a/stardoc/templates/markdown_tables/rule.vm ++++ b/stardoc/templates/markdown_tables/rule.vm +@@ -3,8 +3,6 @@ + #[[##]]# ${ruleName} + +
+-${util.loadStatement($ruleName)}
+-
+ ${util.ruleSummary($ruleName, $ruleInfo)}
+ 
+ +-- +2.49.0 + From ed87447584ce53902561f86425ed681fabb8a285 Mon Sep 17 00:00:00 2001 From: Luis Padron Date: Mon, 2 Jun 2025 10:37:13 -0400 Subject: [PATCH 90/92] Revert "Remove the `swift.no_generated_module_map` feature" (#1530) This reverts commit 68b58f7a44026fcaa174de0f8dc678d753aadeb6. While we have a custom `mixed_language_library` rule that doesnt match `upstream` we must maintain this feature or we get duplicate module declaration errors as the mixed language rules depend on this feature when they create their own modulemap Without this commit reverted we get: ```sh bazel-out/ios_sim_arm64-dbg-ios-sim_arm64-min16.0-applebin_ios-ST-2491983d010b/bin/Code/CoreLibraries/Contacts/Aliases/AliasesLegacy/AliasesLegacy.lib_swift_modulemap/_/module.modulemap:1:8: error: redefinition of module 'AliasesLegacy' 1 | module "AliasesLegacy" { | ^ bazel-out/ios_sim_arm64-dbg-ios-sim_arm64-min16.0-applebin_ios-ST-2491983d010b/bin/Code/CoreLibraries/Contacts/Aliases/AliasesLegacy/AliasesLegacy.lib/AliasesLegacy/module.modulemap:1:8: note: previously defined here 1 | module "AliasesLegacy" { | ^ 1 error generated. ``` Part of: https://github.com/bazelbuild/rules_apple/pull/2729, we can remove this feature when we upstream the mixed language support in upstream. --- swift/internal/BUILD | 1 + swift/internal/compiling.bzl | 11 +++++++++-- swift/internal/feature_names.bzl | 5 +++++ swift/internal/features.bzl | 8 ++++++++ 4 files changed, 23 insertions(+), 2 deletions(-) diff --git a/swift/internal/BUILD b/swift/internal/BUILD index 9b7e491ae..f5a16714b 100644 --- a/swift/internal/BUILD +++ b/swift/internal/BUILD @@ -156,6 +156,7 @@ bzl_library( deps = [ ":feature_names", ":package_specs", + ":target_triples", "@bazel_skylib//lib:collections", "@bazel_skylib//lib:new_sets", "@bazel_skylib//rules:common_settings", diff --git a/swift/internal/compiling.bzl b/swift/internal/compiling.bzl index 0382e6c41..cb604ca2d 100644 --- a/swift/internal/compiling.bzl +++ b/swift/internal/compiling.bzl @@ -48,6 +48,7 @@ load( "SWIFT_FEATURE_INDEX_WHILE_BUILDING", "SWIFT_FEATURE_MODULAR_INDEXING", "SWIFT_FEATURE_MODULE_MAP_HOME_IS_CWD", + "SWIFT_FEATURE_NO_GENERATED_MODULE_MAP", "SWIFT_FEATURE_OPT", "SWIFT_FEATURE_OPT_USES_WMO", "SWIFT_FEATURE_PROPAGATE_GENERATED_MODULE_MAP", @@ -724,7 +725,10 @@ to use swift_common.compile(include_dev_srch_paths = ...) instead.\ # 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: + 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 = [ @@ -1274,7 +1278,10 @@ def _declare_compile_outputs( # trap door lets them escape the module redefinition error, with the # caveat that certain import scenarios could lead to incorrect behavior # because a header can be imported textually instead of modularly. - if generated_header: + if generated_header and not is_feature_enabled( + 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() diff --git a/swift/internal/feature_names.bzl b/swift/internal/feature_names.bzl index 208ccd114..3242857a4 100644 --- a/swift/internal/feature_names.bzl +++ b/swift/internal/feature_names.bzl @@ -153,6 +153,11 @@ SWIFT_FEATURE_MODULE_MAP_NO_PRIVATE_HEADERS = ( # link to any version of the ASAN runtime library. SWIFT_FEATURE_NO_ASAN_VERSION_CHECK = "swift.no_asan_version_check" +# If enabled, the compilation action for a library target will not generate a +# 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, the parent directory of the generated module map is added to # `CcInfo.compilation_context.includes`. This allows `objc_library` to import # the Swift module. If you swap this feature between enabled and disabled, and diff --git a/swift/internal/features.bzl b/swift/internal/features.bzl index e40909b28..f9489def0 100644 --- a/swift/internal/features.bzl +++ b/swift/internal/features.bzl @@ -33,6 +33,7 @@ load( "SWIFT_FEATURE_FILE_PREFIX_MAP", "SWIFT_FEATURE_FULL_DEBUG_INFO", "SWIFT_FEATURE_INTERNALIZE_AT_LINK", + "SWIFT_FEATURE_NO_GENERATED_MODULE_MAP", "SWIFT_FEATURE_OBJC_LINK_FLAGS", "SWIFT_FEATURE_OPT_USES_WMO", "SWIFT_FEATURE_REMAP_XCODE_PATH", @@ -40,6 +41,7 @@ load( "SWIFT_FEATURE__SUPPORTS_V6", ) load(":package_specs.bzl", "label_matches_package_specs") +load(":target_triples.bzl", "target_triples") def are_all_features_enabled(feature_configuration, feature_names): """Returns `True` if all features are enabled in the feature configuration. @@ -265,6 +267,12 @@ def default_features_for_toolchain(target_triple): SWIFT_FEATURE_REMAP_XCODE_PATH, ]) + # Linux specific features + if target_triples.unversioned_os(target_triple) == "linux": + features.extend([ + SWIFT_FEATURE_NO_GENERATED_MODULE_MAP, + ]) + return features def upcoming_and_experimental_features(feature_configuration): From f1720680a8b1ddae6f5f18b9426610e41a457a95 Mon Sep 17 00:00:00 2001 From: Keith Smiley Date: Fri, 6 Jun 2025 14:13:59 -0700 Subject: [PATCH 91/92] Remove prints when toolchain is skipped (#1535) If you have rules_swift in your dependency tree, but don't use swift anywhere in your build, this still warns is too noisy. Likely the most common case for this is because grpc depends on rules_apple. --- swift/internal/swift_autoconfiguration.bzl | 38 +++------------------- 1 file changed, 4 insertions(+), 34 deletions(-) diff --git a/swift/internal/swift_autoconfiguration.bzl b/swift/internal/swift_autoconfiguration.bzl index e6ef58978..46f1f3746 100644 --- a/swift/internal/swift_autoconfiguration.bzl +++ b/swift/internal/swift_autoconfiguration.bzl @@ -200,21 +200,14 @@ def _normalized_linux_cpu(cpu): return "x86_64" return cpu -def _create_linux_toolchain(*, repository_ctx, warn_on_no_swiftc): +def _create_linux_toolchain(*, repository_ctx): """Creates BUILD targets for the Swift toolchain on Linux. Args: repository_ctx: The repository rule context. - warn_on_no_swiftc: If True, print a warning if 'swiftc' is not found in - $PATH. """ path_to_swiftc = repository_ctx.which("swiftc") if not path_to_swiftc: - if warn_on_no_swiftc: - print("""\ -No 'swiftc' executable found in $PATH. Not auto-generating a Linux Swift \ -toolchain. -""") # buildifier: disable=print return """\ # No 'swiftc' executable found in $PATH. Not auto-generating a Linux Swift \ toolchain. @@ -314,21 +307,14 @@ def _get_python_bin(repository_ctx): return out return None -def _create_windows_toolchain(*, repository_ctx, warn_on_no_swiftc): +def _create_windows_toolchain(*, repository_ctx): """Creates BUILD targets for the Swift toolchain on Linux. Args: repository_ctx: The repository rule context. - warn_on_no_swiftc: If True, print a warning if 'swiftc.exe' is not found - in $PATH. """ path_to_swiftc = repository_ctx.which("swiftc.exe") if not path_to_swiftc: - if warn_on_no_swiftc: - print("""\ -No 'swiftc.exe' executable found in $PATH. Not auto-generating a Windows Swift \ -toolchain. -""") # buildifier: disable=print return """\ # No 'swiftc.exe' executable found in $PATH. Not auto-generating a Windows \ Swift toolchain. @@ -394,16 +380,6 @@ toolchain( ) def _swift_autoconfiguration_impl(repository_ctx): - os_name = repository_ctx.os.name.lower() - is_linux = False - is_windows = False - if os_name.startswith("mac os"): - pass - elif os_name.startswith("windows"): - is_windows = True - else: - is_linux = True - repository_ctx.file( "BUILD", "\n".join([ @@ -424,14 +400,8 @@ load( package(default_visibility = ["//visibility:public"]) """, _create_xcode_toolchain(), - _create_windows_toolchain( - repository_ctx = repository_ctx, - warn_on_no_swiftc = is_windows, - ), - _create_linux_toolchain( - repository_ctx = repository_ctx, - warn_on_no_swiftc = is_linux, - ), + _create_windows_toolchain(repository_ctx = repository_ctx), + _create_linux_toolchain(repository_ctx = repository_ctx), ]), ) From def32f358f75a04f585575445f1c82587ee1f71e Mon Sep 17 00:00:00 2001 From: Luis Padron Date: Mon, 9 Jun 2025 11:01:03 -0400 Subject: [PATCH 92/92] Ensure undeclared outputs are deleted when not tracked (#1533) In rules_swift < 3.x the .swiftsourceinfo files are unconditionally written to the module path. In rules_swift >= 3.x these same files are no longer tracked by Bazel unless explicitly requested. When using non-sandboxed mode, previous builds will contain these files and cause build failures when Swift tries to use them, in order to work around this compatibility issue, we check the module path for the presence of .swiftsourceinfo files and if they are present but not requested, we remove them. Testing: - `bazel clean --expunge` - `git checkout 2.8.2` - `bazel build //examples/... --spawn_strategy=local` (pass) - `git checkout master` - `bazel build //examples/... --spawn_strategy=local` (failure) - `git checkout ` - `bazel build //examples/... --spawn_strategy=local` (pass) --- swift/toolchains/config/compile_config.bzl | 10 ++++++++++ tools/worker/swift_runner.cc | 17 +++++++++++++++++ tools/worker/swift_runner.h | 6 ++++++ 3 files changed, 33 insertions(+) diff --git a/swift/toolchains/config/compile_config.bzl b/swift/toolchains/config/compile_config.bzl index 0aca88ebf..dffefc13d 100644 --- a/swift/toolchains/config/compile_config.bzl +++ b/swift/toolchains/config/compile_config.bzl @@ -41,6 +41,7 @@ load( "SWIFT_FEATURE_COVERAGE_PREFIX_MAP", "SWIFT_FEATURE_DBG", "SWIFT_FEATURE_DEBUG_PREFIX_MAP", + "SWIFT_FEATURE_DECLARE_SWIFTSOURCEINFO", "SWIFT_FEATURE_DISABLE_AVAILABILITY_CHECKING", "SWIFT_FEATURE_DISABLE_CLANG_SPI", "SWIFT_FEATURE_DISABLE_SWIFT_SANDBOX", @@ -562,6 +563,15 @@ def compile_action_configs( [SWIFT_FEATURE_COVERAGE_PREFIX_MAP, SWIFT_FEATURE_COVERAGE], ], ), + + # Ensure that .swiftsourceinfo files are tracked and not deleted by the worker + ActionConfigInfo( + actions = [ + SWIFT_ACTION_COMPILE, + ], + configurators = [add_arg("-Xwrapped-swift=-emit-swiftsourceinfo")], + features = [SWIFT_FEATURE_DECLARE_SWIFTSOURCEINFO], + ), ] #### Coverage and sanitizer instrumentation flags diff --git a/tools/worker/swift_runner.cc b/tools/worker/swift_runner.cc index 0c5bce9ef..4788c331f 100644 --- a/tools/worker/swift_runner.cc +++ b/tools/worker/swift_runner.cc @@ -116,6 +116,15 @@ SwiftRunner::SwiftRunner(const std::vector &args, } int SwiftRunner::Run(std::ostream *stderr_stream, bool stdout_to_stderr) { + // In rules_swift < 3.x the .swiftsourceinfo files are unconditionally written to the module path. + // In rules_swift >= 3.x these same files are no longer tracked by Bazel unless explicitly requested. + // When using non-sandboxed mode, previous builds will contain these files and cause build failures + // when Swift tries to use them, in order to work around this compatibility issue, we check the module path for + // the presence of .swiftsourceinfo files and if they are present but not requested, we remove them. + if (swift_source_info_path_ != "" && !emit_swift_source_info_) { + std::filesystem::remove(swift_source_info_path_); + } + int exit_code = RunSubProcess( args_, &job_env_, stderr_stream, stdout_to_stderr); @@ -392,6 +401,8 @@ std::vector SwiftRunner::ParseArguments(Iterator itr) { target_label_ = arg; } else if (arg == "-file-prefix-pwd-is-dot") { file_prefix_pwd_is_dot_ = true; + } else if (arg == "-emit-swiftsourceinfo") { + emit_swift_source_info_ = true; } } else { if (arg == "-output-file-map") { @@ -406,6 +417,12 @@ std::vector SwiftRunner::ParseArguments(Iterator itr) { out_args.push_back(arg); } else if (arg == "-dump-ast") { is_dump_ast_ = true; + } else if (arg == "-emit-module-path") { + ++it; + arg = *it; + std::filesystem::path module_path(arg); + swift_source_info_path_ = module_path.replace_extension(".swiftsourceinfo").string(); + out_args.push_back(arg); } } } diff --git a/tools/worker/swift_runner.h b/tools/worker/swift_runner.h index 2fabbd462..74b30b98d 100644 --- a/tools/worker/swift_runner.h +++ b/tools/worker/swift_runner.h @@ -176,6 +176,12 @@ class SwiftRunner { // `-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_; + + // The path where the module files will be written + std::string swift_source_info_path_; + + // Whether `.swiftsourceinfo` files are being generated. + bool emit_swift_source_info_; }; #endif // BUILD_BAZEL_RULES_SWIFT_TOOLS_WORKER_SWIFT_RUNNER_H_