Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement compilation support for swift_module_mapping #1333

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 9 additions & 8 deletions doc/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -479,9 +479,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 @@ -491,17 +491,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 @@ -477,6 +477,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 @@ -687,8 +695,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 @@ -822,6 +831,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