Skip to content

Commit 3061c87

Browse files
authored
Add feature for partially explicit modules (bazelbuild#763)
As of Swift 5.6 and this PR swiftlang/swift#39887 the new json format used for entirely explicit module builds can be used without entirely disabling implicit modules. This provides an alternative to VFS overlays for discovering dependencies without `-I` search paths. This is theoretically about the same but I expect this file format to have better support in general than VFS overlays for things like the new incremental compilation support, which currently doesn't support VFS overlays, and this shouldn't have any downsides vs VFS files. This format does support some more keys than just the 2 we pass, as far as I can tell they are unused today, so I'm opting not to pass them. We may have to revisit this in the future. This kind of cherry picks 1666670 but I kept the VFS feature for now for legacy users.
1 parent 16e2a14 commit 3061c87

File tree

9 files changed

+196
-3
lines changed

9 files changed

+196
-3
lines changed

swift/internal/BUILD

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ bzl_library(
4545
":debugging",
4646
":derived_files",
4747
":developer_dirs",
48+
":explicit_module_map_file",
4849
":feature_names",
4950
":features",
5051
":providers",
@@ -388,6 +389,11 @@ bzl_library(
388389
],
389390
)
390391

392+
bzl_library(
393+
name = "explicit_module_map_file",
394+
srcs = ["explicit_module_map_file.bzl"],
395+
)
396+
391397
bzl_library(
392398
name = "feature_names",
393399
srcs = ["feature_names.bzl"],

swift/internal/compiling.bzl

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ load(
2424
"run_toolchain_action",
2525
"swift_action_names",
2626
)
27+
load(":explicit_module_map_file.bzl", "write_explicit_swift_module_map_file")
2728
load(":derived_files.bzl", "derived_files")
2829
load(
2930
":feature_names.bzl",
@@ -62,6 +63,7 @@ load(
6263
"SWIFT_FEATURE_SUPPORTS_SYSTEM_MODULE_FLAG",
6364
"SWIFT_FEATURE_SYSTEM_MODULE",
6465
"SWIFT_FEATURE_USE_C_MODULES",
66+
"SWIFT_FEATURE_USE_EXPLICIT_SWIFT_MODULE_MAP",
6567
"SWIFT_FEATURE_USE_GLOBAL_INDEX_STORE",
6668
"SWIFT_FEATURE_USE_GLOBAL_MODULE_CACHE",
6769
"SWIFT_FEATURE_USE_OLD_DRIVER",
@@ -875,7 +877,10 @@ def compile_action_configs(
875877
swift_action_names.DUMP_AST,
876878
],
877879
configurators = [_dependencies_swiftmodules_configurator],
878-
not_features = [SWIFT_FEATURE_VFSOVERLAY],
880+
not_features = [
881+
[SWIFT_FEATURE_VFSOVERLAY],
882+
[SWIFT_FEATURE_USE_EXPLICIT_SWIFT_MODULE_MAP],
883+
],
879884
),
880885
swift_toolchain_config.action_config(
881886
actions = [
@@ -888,6 +893,17 @@ def compile_action_configs(
888893
],
889894
features = [SWIFT_FEATURE_VFSOVERLAY],
890895
),
896+
swift_toolchain_config.action_config(
897+
actions = [
898+
swift_action_names.COMPILE,
899+
swift_action_names.DERIVE_FILES,
900+
swift_action_names.DUMP_AST,
901+
],
902+
configurators = [
903+
_explicit_swift_module_map_configurator,
904+
],
905+
features = [SWIFT_FEATURE_USE_EXPLICIT_SWIFT_MODULE_MAP],
906+
),
891907
])
892908

893909
#### Search paths for framework dependencies
@@ -1620,6 +1636,21 @@ def _dependencies_swiftmodules_vfsoverlay_configurator(prerequisites, args):
16201636
inputs = swiftmodules + [prerequisites.vfsoverlay_file],
16211637
)
16221638

1639+
def _explicit_swift_module_map_configurator(prerequisites, args):
1640+
"""Adds the explicit Swift module map file to the command line."""
1641+
args.add_all(
1642+
[
1643+
"-explicit-swift-module-map-file",
1644+
prerequisites.explicit_swift_module_map_file,
1645+
],
1646+
before_each = "-Xfrontend",
1647+
)
1648+
return swift_toolchain_config.config_result(
1649+
inputs = prerequisites.transitive_swiftmodules + [
1650+
prerequisites.explicit_swift_module_map_file,
1651+
],
1652+
)
1653+
16231654
def _module_name_configurator(prerequisites, args):
16241655
"""Adds the module name flag to the command line."""
16251656
args.add("-module-name", prerequisites.module_name)
@@ -2081,11 +2112,32 @@ def compile(
20812112
else:
20822113
vfsoverlay_file = None
20832114

2115+
if is_feature_enabled(
2116+
feature_configuration = feature_configuration,
2117+
feature_name = SWIFT_FEATURE_USE_EXPLICIT_SWIFT_MODULE_MAP,
2118+
):
2119+
if vfsoverlay_file:
2120+
fail("Cannot use both `swift.vfsoverlay` and `swift.use_explicit_swift_module_map` features at the same time.")
2121+
2122+
# Generate the JSON file that contains the manifest of Swift
2123+
# dependencies.
2124+
explicit_swift_module_map_file = actions.declare_file(
2125+
"{}.swift-explicit-module-map.json".format(target_name),
2126+
)
2127+
write_explicit_swift_module_map_file(
2128+
actions = actions,
2129+
explicit_swift_module_map_file = explicit_swift_module_map_file,
2130+
module_contexts = transitive_modules,
2131+
)
2132+
else:
2133+
explicit_swift_module_map_file = None
2134+
20842135
prerequisites = struct(
20852136
additional_inputs = additional_inputs,
20862137
bin_dir = feature_configuration._bin_dir,
20872138
cc_compilation_context = merged_providers.cc_info.compilation_context,
20882139
defines = sets.to_list(defines_set),
2140+
explicit_swift_module_map_file = explicit_swift_module_map_file,
20892141
developer_dirs = swift_toolchain.developer_dirs,
20902142
genfiles_dir = feature_configuration._genfiles_dir,
20912143
is_swift = True,
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# Copyright 2022 The Bazel Authors. All rights reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
"""Generates the JSON manifest used to pass Swift modules to the compiler."""
16+
17+
def write_explicit_swift_module_map_file(
18+
*,
19+
actions,
20+
explicit_swift_module_map_file,
21+
module_contexts):
22+
"""Generates the JSON-formatted explicit module map file.
23+
24+
This file is a manifest that contains the path information for all the
25+
Swift modules from dependencies that are needed to compile a particular
26+
module.
27+
28+
Args:
29+
actions: The object used to register actions.
30+
explicit_swift_module_map_file: A `File` to which the generated JSON
31+
will be written.
32+
module_contexts: A list of module contexts that provide the Swift
33+
dependencies for the compilation.
34+
"""
35+
module_descriptions = []
36+
37+
for module_context in module_contexts:
38+
if not module_context.swift:
39+
continue
40+
41+
swift_context = module_context.swift
42+
module_description = {"moduleName": module_context.name}
43+
if swift_context.swiftmodule:
44+
module_description["modulePath"] = swift_context.swiftmodule.path
45+
module_descriptions.append(module_description)
46+
47+
actions.write(
48+
content = json.encode(module_descriptions),
49+
output = explicit_swift_module_map_file,
50+
)

swift/internal/feature_names.bzl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,10 @@ SWIFT_FEATURE_REWRITE_GENERATED_HEADER = "swift.rewrite_generated_header"
166166
# them.
167167
SWIFT_FEATURE_USE_C_MODULES = "swift.use_c_modules"
168168

169+
# If enabled, Swift modules for dependencies will be passed to the compiler
170+
# using a JSON file instead of `-I` search paths.
171+
SWIFT_FEATURE_USE_EXPLICIT_SWIFT_MODULE_MAP = "swift.use_explicit_swift_module_map"
172+
169173
# If enabled, Swift compilation actions will use the same global Clang module
170174
# cache used by Objective-C compilation actions. This is disabled by default
171175
# because under some circumstances Clang module cache corruption can cause the

test/features_tests.bzl

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,22 @@ file_prefix_xcode_remap_test = make_action_command_line_test_rule(
3838
},
3939
)
4040

41+
vfsoverlay_test = make_action_command_line_test_rule(
42+
config_settings = {
43+
"//command_line_option:features": [
44+
"swift.vfsoverlay",
45+
],
46+
},
47+
)
48+
49+
explicit_swift_module_map_test = make_action_command_line_test_rule(
50+
config_settings = {
51+
"//command_line_option:features": [
52+
"swift.use_explicit_swift_module_map",
53+
],
54+
},
55+
)
56+
4157
def features_test_suite(name):
4258
"""Test suite for various features.
4359
@@ -47,13 +63,16 @@ def features_test_suite(name):
4763
default_test(
4864
name = "{}_default_test".format(name),
4965
tags = [name],
50-
expected_argv = ["-emit-object"],
66+
expected_argv = [
67+
"-emit-object",
68+
"-I$(BIN_DIR)/test/fixtures/basic",
69+
],
5170
not_expected_argv = [
5271
"-file-prefix-map",
5372
"-Xwrapped-swift=-file-prefix-pwd-is-dot",
5473
],
5574
mnemonic = "SwiftCompile",
56-
target_under_test = "@build_bazel_rules_swift//test/fixtures/debug_settings:simple",
75+
target_under_test = "@build_bazel_rules_swift//test/fixtures/basic:second",
5776
)
5877

5978
file_prefix_map_test(
@@ -95,3 +114,33 @@ def features_test_suite(name):
95114
mnemonic = "SwiftCompile",
96115
target_under_test = "@build_bazel_rules_swift//test/fixtures/debug_settings:simple",
97116
)
117+
118+
vfsoverlay_test(
119+
name = "{}_vfsoverlay_test".format(name),
120+
tags = [name],
121+
expected_argv = [
122+
"-Xfrontend -vfsoverlay$(BIN_DIR)/test/fixtures/basic/second.vfsoverlay.yaml",
123+
"-I/__build_bazel_rules_swift/swiftmodules",
124+
],
125+
not_expected_argv = [
126+
"-I$(BIN_DIR)/test/fixtures/basic",
127+
"-explicit-swift-module-map-file",
128+
],
129+
mnemonic = "SwiftCompile",
130+
target_under_test = "@build_bazel_rules_swift//test/fixtures/basic:second",
131+
)
132+
133+
explicit_swift_module_map_test(
134+
name = "{}_explicit_swift_module_map_test".format(name),
135+
tags = [name],
136+
expected_argv = [
137+
"-Xfrontend -explicit-swift-module-map-file -Xfrontend $(BIN_DIR)/test/fixtures/basic/second.swift-explicit-module-map.json",
138+
],
139+
not_expected_argv = [
140+
"-I$(BIN_DIR)/test/fixtures/basic",
141+
"-I/__build_bazel_rules_swift/swiftmodules",
142+
"-Xfrontend -vfsoverlay$(BIN_DIR)/test/fixtures/basic/second.vfsoverlay.yaml",
143+
],
144+
mnemonic = "SwiftCompile",
145+
target_under_test = "@build_bazel_rules_swift//test/fixtures/basic:second",
146+
)

test/fixtures/basic/BUILD

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
load("//swift:swift.bzl", "swift_library")
2+
load("//test/fixtures:common.bzl", "FIXTURE_TAGS")
3+
4+
package(
5+
default_visibility = ["//test:__subpackages__"],
6+
)
7+
8+
licenses(["notice"])
9+
10+
swift_library(
11+
name = "first",
12+
srcs = ["first.swift"],
13+
tags = FIXTURE_TAGS,
14+
)
15+
16+
swift_library(
17+
name = "second",
18+
srcs = ["second.swift"],
19+
tags = FIXTURE_TAGS,
20+
deps = ["first"],
21+
)

test/fixtures/basic/first.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
public func foo() -> String {
2+
return "foo"
3+
}

test/fixtures/basic/second.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import first
2+
3+
public func bar() -> String {
4+
return foo()
5+
}

test/rules/action_command_line_test.bzl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,9 @@ def _action_command_line_test_impl(ctx):
7777
# end and look for arguments followed by a trailing space so that having
7878
# `-foo` in the expected list doesn't match `-foobar`, for example.
7979
concatenated_args = " ".join(action.argv) + " "
80+
bin_dir = analysistest.target_bin_dir_path(env)
8081
for expected in ctx.attr.expected_argv:
82+
expected = expected.replace("$(BIN_DIR)", bin_dir)
8183
if expected + " " not in concatenated_args and expected + "=" not in concatenated_args:
8284
unittest.fail(
8385
env,
@@ -88,6 +90,7 @@ def _action_command_line_test_impl(ctx):
8890
),
8991
)
9092
for not_expected in ctx.attr.not_expected_argv:
93+
not_expected = not_expected.replace("$(BIN_DIR)", bin_dir)
9194
if not_expected + " " in concatenated_args or not_expected + "=" in concatenated_args:
9295
unittest.fail(
9396
env,

0 commit comments

Comments
 (0)