Skip to content

Commit

Permalink
Implement compilation support for swift_module_mapping
Browse files Browse the repository at this point in the history
PiperOrigin-RevId: 486694413
(cherry picked from commit ba41644)
Signed-off-by: Brentley Jones <github@brentleyjones.com>
  • Loading branch information
allevato authored and brentleyjones committed Oct 4, 2024
1 parent c6d8d84 commit 149e554
Show file tree
Hide file tree
Showing 10 changed files with 268 additions and 24 deletions.
17 changes: 9 additions & 8 deletions doc/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -480,9 +480,9 @@ A provider whose type/layout is an implementation detail and should not
## swift_common.create_swift_module

<pre>
swift_common.create_swift_module(<a href="#swift_common.create_swift_module-swiftdoc">swiftdoc</a>, <a href="#swift_common.create_swift_module-swiftmodule">swiftmodule</a>, <a href="#swift_common.create_swift_module-ast_files">ast_files</a>, <a href="#swift_common.create_swift_module-defines">defines</a>, <a href="#swift_common.create_swift_module-generated_header">generated_header</a>,
<a href="#swift_common.create_swift_module-indexstore">indexstore</a>, <a href="#swift_common.create_swift_module-plugins">plugins</a>, <a href="#swift_common.create_swift_module-swiftsourceinfo">swiftsourceinfo</a>, <a href="#swift_common.create_swift_module-swiftinterface">swiftinterface</a>,
<a href="#swift_common.create_swift_module-private_swiftinterface">private_swiftinterface</a>, <a href="#swift_common.create_swift_module-const_protocols_to_gather">const_protocols_to_gather</a>)
swift_common.create_swift_module(<a href="#swift_common.create_swift_module-ast_files">ast_files</a>, <a href="#swift_common.create_swift_module-const_protocols_to_gather">const_protocols_to_gather</a>, <a href="#swift_common.create_swift_module-defines">defines</a>, <a href="#swift_common.create_swift_module-generated_header">generated_header</a>,
<a href="#swift_common.create_swift_module-indexstore">indexstore</a>, <a href="#swift_common.create_swift_module-original_module_name">original_module_name</a>, <a href="#swift_common.create_swift_module-plugins">plugins</a>, <a href="#swift_common.create_swift_module-private_swiftinterface">private_swiftinterface</a>,
<a href="#swift_common.create_swift_module-swiftdoc">swiftdoc</a>, <a href="#swift_common.create_swift_module-swiftinterface">swiftinterface</a>, <a href="#swift_common.create_swift_module-swiftmodule">swiftmodule</a>, <a href="#swift_common.create_swift_module-swiftsourceinfo">swiftsourceinfo</a>)
</pre>

Creates a value representing a Swift module use as a Swift dependency.
Expand All @@ -492,17 +492,18 @@ Creates a value representing a Swift module use as a Swift dependency.

| Name | Description | Default Value |
| :------------- | :------------- | :------------- |
| <a id="swift_common.create_swift_module-swiftdoc"></a>swiftdoc | The `.swiftdoc` file emitted by the compiler for this module. | none |
| <a id="swift_common.create_swift_module-swiftmodule"></a>swiftmodule | The `.swiftmodule` file emitted by the compiler for this module. | none |
| <a id="swift_common.create_swift_module-ast_files"></a>ast_files | A list of `File`s output from the `DUMP_AST` action. | `[]` |
| <a id="swift_common.create_swift_module-const_protocols_to_gather"></a>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. | `[]` |
| <a id="swift_common.create_swift_module-defines"></a>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. | `[]` |
| <a id="swift_common.create_swift_module-generated_header"></a>generated_header | A `File` representing the Swift generated header. | `None` |
| <a id="swift_common.create_swift_module-indexstore"></a>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` |
| <a id="swift_common.create_swift_module-original_module_name"></a>original_module_name | The original name of the module if it was changed by a module mapping; otherwise, `None`. | `None` |
| <a id="swift_common.create_swift_module-plugins"></a>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. | `[]` |
| <a id="swift_common.create_swift_module-swiftsourceinfo"></a>swiftsourceinfo | The `.swiftsourceinfo` file emitted by the compiler for this module. May be `None` if no source info file was emitted. | `None` |
| <a id="swift_common.create_swift_module-swiftinterface"></a>swiftinterface | The `.swiftinterface` file emitted by the compiler for this module. May be `None` if no module interface file was emitted. | `None` |
| <a id="swift_common.create_swift_module-private_swiftinterface"></a>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` |
| <a id="swift_common.create_swift_module-const_protocols_to_gather"></a>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. | `[]` |
| <a id="swift_common.create_swift_module-swiftdoc"></a>swiftdoc | The `.swiftdoc` file emitted by the compiler for this module. | none |
| <a id="swift_common.create_swift_module-swiftinterface"></a>swiftinterface | The `.swiftinterface` file emitted by the compiler for this module. May be `None` if no module interface file was emitted. | `None` |
| <a id="swift_common.create_swift_module-swiftmodule"></a>swiftmodule | The `.swiftmodule` file emitted by the compiler for this module. | none |
| <a id="swift_common.create_swift_module-swiftsourceinfo"></a>swiftsourceinfo | The `.swiftsourceinfo` file emitted by the compiler for this module. May be `None` if no source info file was emitted. | `None` |

**RETURNS**

Expand Down
12 changes: 11 additions & 1 deletion swift/internal/compiling.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -479,6 +479,14 @@ def compile(
macros).
"""

# Apply the module alias for the module being compiled, if present.
module_alias = swift_toolchain.module_aliases.get(module_name)
if module_alias:
original_module_name = module_name
module_name = module_alias
else:
original_module_name = None

# Collect the `SwiftInfo` providers that represent the dependencies of the
# Objective-C generated header module -- this includes the dependencies of
# the Swift module, plus any additional dependencies that the toolchain says
Expand Down Expand Up @@ -689,8 +697,9 @@ 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,
package_name = package_name,
objc_info = merged_objc_info,
original_module_name = original_module_name,
package_name = package_name,
plugins = depset(used_plugins),
source_files = srcs,
target_label = feature_configuration._label,
Expand Down Expand Up @@ -824,6 +833,7 @@ to use swift_common.compile(include_dev_srch_paths = ...) instead.\
defines = defines,
generated_header = compile_outputs.generated_header_file,
indexstore = compile_outputs.indexstore_directory,
original_module_name = original_module_name,
plugins = depset(plugins),
private_swiftinterface = compile_outputs.private_swiftinterface_file,
swiftdoc = compile_outputs.swiftdoc_file,
Expand Down
34 changes: 19 additions & 15 deletions swift/internal/providers.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -148,44 +148,47 @@ def create_clang_module(

def create_swift_module(
*,
swiftdoc,
swiftmodule,
ast_files = [],
const_protocols_to_gather = [],
defines = [],
generated_header = None,
indexstore = None,
original_module_name = None,
plugins = [],
swiftsourceinfo = None,
swiftinterface = None,
private_swiftinterface = None,
const_protocols_to_gather = []):
swiftdoc,
swiftinterface = None,
swiftmodule,
swiftsourceinfo = None):
"""Creates a value representing a Swift module use as a Swift dependency.
Args:
swiftdoc: The `.swiftdoc` file emitted by the compiler for this module.
swiftmodule: The `.swiftmodule` file emitted by the compiler for this
module.
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.
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`.
original_module_name: The original name of the module if it was changed
by a module mapping; otherwise, `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.
swiftsourceinfo: The `.swiftsourceinfo` file emitted by the compiler for
this module. May be `None` if no source info file was emitted.
swiftdoc: The `.swiftdoc` file emitted by the compiler for this module.
swiftinterface: The `.swiftinterface` file emitted by the compiler for
this module. May be `None` if no module interface file was emitted.
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.
swiftmodule: The `.swiftmodule` file emitted by the compiler for this
module.
swiftsourceinfo: The `.swiftsourceinfo` file emitted by the compiler for
this module. May be `None` if no source info file was emitted.
Returns:
A `struct` containing the `ast_files`, `defines`, `indexstore,
Expand All @@ -194,16 +197,17 @@ def create_swift_module(
"""
return struct(
ast_files = tuple(ast_files),
const_protocols_to_gather = tuple(const_protocols_to_gather),
defines = tuple(defines),
generated_header = generated_header,
indexstore = indexstore,
plugins = plugins,
private_swiftinterface = private_swiftinterface,
indexstore = indexstore,
original_module_name = original_module_name,
swiftdoc = swiftdoc,
swiftinterface = swiftinterface,
swiftmodule = swiftmodule,
swiftsourceinfo = swiftsourceinfo,
const_protocols_to_gather = tuple(const_protocols_to_gather),
)

def create_swift_info(
Expand Down
57 changes: 57 additions & 0 deletions swift/toolchains/config/compile_config.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -798,6 +798,10 @@ def compile_action_configs(
],
features = [SWIFT_FEATURE_VFSOVERLAY],
),
ActionConfigInfo(
actions = [SWIFT_ACTION_COMPILE],
configurators = [_module_aliases_configurator],
),
ActionConfigInfo(
actions = [
SWIFT_ACTION_COMPILE,
Expand Down Expand Up @@ -1717,6 +1721,44 @@ def _swift_module_search_path_map_fn(module):
else:
return None

def _module_alias_flags(name, original):
"""Returns compiler flags to set the given module alias."""

# TODO(b/257269318): Remove `-Xfrontend`; this is only needed to workaround
# a bug in toolchains still using the legacy C++ driver.
return [
"-Xfrontend",
"-module-alias",
"-Xfrontend",
"{original}={name}".format(
name = name,
original = original,
),
]

def _module_alias_map_fn(module):
"""Returns compiler flags to alias the given module.
This function is intended to be used as a mapping function for modules
passed into `Args.add_all`.
Args:
module: The module structure (as returned by
`swift_common.create_module`) extracted from the transitive
modules of a `SwiftInfo` provider.
Returns:
The flags to pass to the compiler to alias the given module, or `None`
if no alias applies.
"""
if module.swift and module.swift.original_module_name:
return _module_alias_flags(
original = module.swift.original_module_name,
name = module.name,
)
else:
return None

def _dependencies_swiftmodules_and_swiftdocs_configurator(prerequisites, args):
"""Adds `.swiftmodule` and `.swiftdoc` files from the transitive modules to search paths and action inputs."""
args.add_all(
Expand Down Expand Up @@ -1744,6 +1786,21 @@ def _dependencies_swiftmodules_configurator(prerequisites, args):
inputs = prerequisites.transitive_swiftmodules,
)

def _module_aliases_configurator(prerequisites, args):
"""Adds `-module-alias` flags for the active module mapping, if any."""
args.add_all(
prerequisites.transitive_modules,
map_each = _module_alias_map_fn,
)

if prerequisites.original_module_name:
args.add_all(
_module_alias_flags(
original = prerequisites.original_module_name,
name = prerequisites.module_name,
),
)

def _load_executable_plugin_map_fn(plugin):
"""Returns frontend flags to load compiler plugins."""
return [
Expand Down
3 changes: 3 additions & 0 deletions test/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ load(":interop_hints_tests.bzl", "interop_hints_test_suite")
load(":mainattr_tests.bzl", "mainattr_test_suite")
load(":module_cache_settings_tests.bzl", "module_cache_settings_test_suite")
load(":module_interface_tests.bzl", "module_interface_test_suite")
load(":module_mapping_tests.bzl", "module_mapping_test_suite")
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")
Expand Down Expand Up @@ -52,6 +53,8 @@ module_cache_settings_test_suite(name = "module_cache_settings")

module_interface_test_suite(name = "module_interface")

module_mapping_test_suite(name = "module_mapping")

output_file_map_test_suite(name = "output_file_map")

private_deps_test_suite(name = "private_deps")
Expand Down
47 changes: 47 additions & 0 deletions test/fixtures/module_mapping/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
load("//swift:swift_library.bzl", "swift_library")
load("//swift:swift_module_mapping.bzl", "swift_module_mapping")
load(
"//test/fixtures:common.bzl",
"FIXTURE_TAGS",
)
load(":apply_mapping.bzl", "apply_mapping")

package(
default_testonly = True,
default_visibility = ["//test:__subpackages__"],
)

licenses(["notice"])

swift_library(
name = "Common",
srcs = ["Common.swift"],
module_name = "Common",
tags = FIXTURE_TAGS,
)

swift_library(
name = "MySDK",
srcs = ["MySDK.swift"],
module_name = "MySDK",
tags = FIXTURE_TAGS,
deps = [":Common"],
)

swift_module_mapping(
name = "MySDK_module_mapping",
# This must not be testonly because it is used by the toolchain through the
# `:module_mapping` label flag.
testonly = False,
aliases = {
"Common": "MySDKInternal_Common",
},
)

# This is the target that will be tested in `module_mapping.bzl`, to force the
# `MySDK` target to build in a configuration that sets the flag.
apply_mapping(
name = "MySDK_with_mapping",
mapping = ":MySDK_module_mapping",
target = ":MySDK",
)
19 changes: 19 additions & 0 deletions test/fixtures/module_mapping/Common.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// 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.

public final class Cat {
public class var standardIssue: Common.Cat { Common.Cat() }

public init() {}
}
19 changes: 19 additions & 0 deletions test/fixtures/module_mapping/MySDK.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// 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.

import Common

public func makeStandardIssueCat() -> Common.Cat {
return Common.Cat.standardIssue
}
40 changes: 40 additions & 0 deletions test/fixtures/module_mapping/apply_mapping.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# 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.

"""Rule and transition to force a specific module mapping in tests."""

def _apply_mapping_transition_impl(settings, attr):
settings = dict(settings)
settings["@build_bazel_rules_swift//swift:module_mapping"] = attr.mapping
return settings

apply_mapping_transition = transition(
implementation = _apply_mapping_transition_impl,
inputs = [],
outputs = ["@build_bazel_rules_swift//swift:module_mapping"],
)

def _apply_mapping_impl(ctx):
return [ctx.attr.target[0][DefaultInfo]]

apply_mapping = rule(
attrs = {
"mapping": attr.label(),
"target": attr.label(cfg = apply_mapping_transition),
"_allowlist_function_transition": attr.label(
default = "@bazel_tools//tools/allowlists/function_transition_allowlist",
),
},
implementation = _apply_mapping_impl,
)
Loading

0 comments on commit 149e554

Please sign in to comment.