diff --git a/doc/api.md b/doc/api.md index 0e5e9b127..320a0a410 100644 --- a/doc/api.md +++ b/doc/api.md @@ -446,21 +446,28 @@ provider whose compilation context contains the headers that it wants to make into a module, and then also propagate the provider returned from this function. -The simplest usage is for a custom rule to call -`swift_common.create_swift_interop_info` passing it only the list of -`SwiftInfo` providers from its dependencies; this tells -`swift_clang_module_aspect` to derive the module name from the target label -and create a module map using the headers from the compilation context. +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 do so automatically. This allows the rule to not only add extra -dependencies (such as support libraries from implicit attributes) but also -exclude dependencies if necessary. +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** diff --git a/swift/BUILD b/swift/BUILD index 1773f4363..d93735e00 100644 --- a/swift/BUILD +++ b/swift/BUILD @@ -93,6 +93,7 @@ bzl_library( deps = [ ":module_name", ":providers", + ":swift_interop_info", "//swift/internal:attrs", "//swift/internal:compiling", "//swift/internal:features", @@ -166,7 +167,17 @@ bzl_library( bzl_library( name = "swift_interop_hint", srcs = ["swift_interop_hint.bzl"], - deps = ["//swift/internal:swift_interop_info"], + deps = [ + ":swift_interop_info", + ], +) + +bzl_library( + name = "swift_interop_info", + srcs = ["swift_interop_info.bzl"], + deps = [ + "//swift/internal:swift_interop_info", + ], ) bzl_library( @@ -285,6 +296,7 @@ bzl_library( ":swift_feature_allowlist", ":swift_import", ":swift_interop_hint", + ":swift_interop_info", ":swift_library", ":swift_library_group", ":swift_module_alias", diff --git a/swift/internal/swift_interop_info.bzl b/swift/internal/swift_interop_info.bzl index 49fde2bf7..374c9c59c 100644 --- a/swift/internal/swift_interop_info.bzl +++ b/swift/internal/swift_interop_info.bzl @@ -74,96 +74,3 @@ enabled by default in the toolchain. """, }, ) - -def create_swift_interop_info( - *, - exclude_headers = [], - module_map = None, - module_name = None, - requested_features = [], - suppressed = False, - swift_infos = [], - unsupported_features = []): - """Returns a provider that lets a target expose C/Objective-C APIs to Swift. - - The provider returned by this function allows custom build rules written in - Starlark to be uninvolved with much of the low-level machinery involved in - making a Swift-compatible module. Such a target should propagate a `CcInfo` - provider whose compilation context contains the headers that it wants to - make into a module, and then also propagate the provider returned from this - function. - - The simplest usage is for a custom rule to call - `swift_common.create_swift_interop_info` passing it only the list of - `SwiftInfo` providers from its dependencies; this tells - `swift_clang_module_aspect` to derive the module name from the target label - and create a module map using the headers from the compilation context. - - If the custom rule has reason to provide its own module name or module map, - then it can do so using the `module_name` and `module_map` arguments. - - When a rule returns this provider, it must provide the full set of - `SwiftInfo` providers from dependencies that will be merged with the one - that `swift_clang_module_aspect` creates for the target itself; the aspect - will not do so automatically. This allows the rule to not only add extra - dependencies (such as support libraries from implicit attributes) but also - exclude dependencies if necessary. - - Args: - 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. - module_name: A string denoting the name of the module, or `None` (the - default) if the name should be derived automatically from the target - label. - requested_features: A list of features (empty by default) that should be - requested for the target, which are added to those supplied in the - `features` attribute of the target. These features will be enabled - unless they are otherwise marked as unsupported (either on the - target or by the toolchain). This allows the rule implementation to - have additional control over features that should be supported by - default for all instances of that rule as if it were creating the - feature configuration itself; for example, a rule can request that - `swift.emit_c_module` always be enabled for its targets even if it - is not explicitly enabled in the toolchain or on the target - directly. - suppressed: A `bool` indicating whether the module that the aspect would - create for the target should instead be suppressed. - swift_infos: A list of `SwiftInfo` providers from dependencies, which - will be merged with the new `SwiftInfo` created by the aspect. - unsupported_features: A list of features (empty by default) that should - be considered unsupported for the target, which are added to those - supplied as negations in the `features` attribute. This allows the - rule implementation to have additional control over features that - should be disabled by default for all instances of that rule as if - it were creating the feature configuration itself; for example, a - rule that processes frameworks with headers that do not follow - strict layering can request that `swift.strict_module` always be - disabled for its targets even if it is enabled by default in the - toolchain. - - Returns: - A provider whose type/layout is an implementation detail and should not - be relied upon. - """ - if module_map: - if not module_name: - fail("'module_name' must be specified when 'module_map' is " + - "specified.") - if exclude_headers: - fail("'exclude_headers' may not be specified when 'module_map' " + - "is specified.") - - return SwiftInteropInfo( - exclude_headers = exclude_headers, - module_map = module_map, - module_name = module_name, - requested_features = requested_features, - suppressed = suppressed, - swift_infos = swift_infos, - unsupported_features = unsupported_features, - ) diff --git a/swift/swift_common.bzl b/swift/swift_common.bzl index 5d273c340..8d95a3b96 100644 --- a/swift/swift_common.bzl +++ b/swift/swift_common.bzl @@ -43,7 +43,6 @@ load( "//swift/internal:linking.bzl", "create_linking_context_from_compilation_outputs", ) -load("//swift/internal:swift_interop_info.bzl", "create_swift_interop_info") load( "//swift/internal:symbol_graph_extracting.bzl", "extract_symbol_graph", @@ -61,6 +60,7 @@ load( "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. @@ -81,6 +81,8 @@ swift_common = struct( # 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. diff --git a/swift/swift_interop_hint.bzl b/swift/swift_interop_hint.bzl index fbe909e09..8ccb1bd78 100644 --- a/swift/swift_interop_hint.bzl +++ b/swift/swift_interop_hint.bzl @@ -14,10 +14,7 @@ """Implementation of the `swift_interop_hint` rule.""" -load( - "//swift/internal:swift_interop_info.bzl", - "create_swift_interop_info", -) +load(":swift_interop_info.bzl", "create_swift_interop_info") def _swift_interop_hint_impl(ctx): # TODO(b/194733180): Take advantage of the richer API to add support for diff --git a/swift/swift_interop_info.bzl b/swift/swift_interop_info.bzl new file mode 100644 index 000000000..91423ea3f --- /dev/null +++ b/swift/swift_interop_info.bzl @@ -0,0 +1,134 @@ +# 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. + +"""Defines the public interface for creating a lightweight interop provider. + +Note that the only way for external users to create this provider is by calling +the `create_swift_interop_info` function. This is because it is meant to be a +"write-only" provider; one that targets can propagate but should not attempt to +read. + +**NOTE:** This file intentionally contains no `load` statements other than the +one that defines the provider, which also contains no `load` statements. It is +loaded by the `swift_interop_hint` rule, and packages loading that rule often +contain no other Swift code and load no other Swift rules. The purpose of +avoiding unnecessary loads in this file and in `swift_interop_hint.bzl` is to +minimize build graph invalidations among those packages when other, unrelated +parts of the Swift rules change. +""" + +load( + "@build_bazel_rules_swift//swift/internal:swift_interop_info.bzl", + "SwiftInteropInfo", +) + +def create_swift_interop_info( + *, + exclude_headers = [], + module_map = None, + module_name = None, + requested_features = [], + suppressed = False, + 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. + + Args: + 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. + module_name: A string denoting the name of the module, or `None` (the + default) if the name should be derived automatically from the target + label. + requested_features: A list of features (empty by default) that should be + requested for the target, which are added to those supplied in the + `features` attribute of the target. These features will be enabled + unless they are otherwise marked as unsupported (either on the + target or by the toolchain). This allows the rule implementation to + have additional control over features that should be supported by + default for all instances of that rule as if it were creating the + feature configuration itself; for example, a rule can request that + `swift.emit_c_module` always be enabled for its targets even if it + is not explicitly enabled in the toolchain or on the target + directly. + suppressed: A `bool` indicating whether the module that the aspect would + create for the target should instead be suppressed. + swift_infos: A list of `SwiftInfo` providers from dependencies, which + will be merged with the new `SwiftInfo` created by the aspect. + unsupported_features: A list of features (empty by default) that should + be considered unsupported for the target, which are added to those + supplied as negations in the `features` attribute. This allows the + rule implementation to have additional control over features that + should be disabled by default for all instances of that rule as if + it were creating the feature configuration itself; for example, a + rule that processes frameworks with headers that do not follow + strict layering can request that `swift.strict_module` always be + disabled for its targets even if it is enabled by default in the + toolchain. + + Returns: + A provider whose type/layout is an implementation detail and should not + be relied upon. + """ + if module_map: + if not module_name: + fail("'module_name' must be specified when 'module_map' is " + + "specified.") + if exclude_headers: + fail("'exclude_headers' may not be specified when 'module_map' " + + "is specified.") + + return SwiftInteropInfo( + exclude_headers = exclude_headers, + module_map = module_map, + module_name = module_name, + requested_features = requested_features, + suppressed = suppressed, + swift_infos = swift_infos, + unsupported_features = unsupported_features, + )