Skip to content

Commit efa3f31

Browse files
committed
Fix swift mdule compilation with swift_import targets that depend on each other
1 parent 532d7ea commit efa3f31

File tree

3 files changed

+152
-1
lines changed

3 files changed

+152
-1
lines changed

swift/toolchains/config/compile_module_interface_config.bzl

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ load(
1818
"//swift/internal:action_names.bzl",
1919
"SWIFT_ACTION_COMPILE_MODULE_INTERFACE",
2020
)
21-
load(":action_config.bzl", "ActionConfigInfo", "add_arg")
21+
load(":action_config.bzl", "ActionConfigInfo", "ConfigResultInfo", "add_arg")
2222

2323
def compile_module_interface_action_configs():
2424
return [
@@ -41,8 +41,32 @@ def compile_module_interface_action_configs():
4141
add_arg("-compile-module-from-interface"),
4242
],
4343
),
44+
ActionConfigInfo(
45+
actions = [SWIFT_ACTION_COMPILE_MODULE_INTERFACE],
46+
configurators = [
47+
_dependencies_swiftmodules_configurator,
48+
],
49+
),
4450
]
4551

4652
def _emit_module_path_from_module_interface_configurator(prerequisites, args):
4753
"""Adds the `.swiftmodule` output path to the command line."""
4854
args.add("-o", prerequisites.swiftmodule_file)
55+
56+
def _dependencies_swiftmodules_configurator(prerequisites):
57+
"""Adds transitive swiftmodule dependencies as inputs for module interface compilation."""
58+
59+
# For module interface compilation, we need access to dependency swiftinterface/swiftmodule files
60+
# This ensures they are available in the sandbox
61+
transitive_inputs = []
62+
for module in prerequisites.transitive_modules:
63+
swift_module = module.swift
64+
if swift_module:
65+
if swift_module.swiftmodule:
66+
transitive_inputs.append(swift_module.swiftmodule)
67+
if swift_module.swiftinterface:
68+
transitive_inputs.append(swift_module.swiftinterface)
69+
70+
return ConfigResultInfo(
71+
inputs = transitive_inputs,
72+
)

test/module_interface_tests.bzl

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ load(
1919
"//test/rules:action_command_line_test.bzl",
2020
"make_action_command_line_test_rule",
2121
)
22+
load("//test/rules:action_inputs_test.bzl", "action_inputs_test")
2223
load("//test/rules:provider_test.bzl", "provider_test")
2324

2425
explicit_swift_module_map_test = make_action_command_line_test_rule(
@@ -113,6 +114,17 @@ def module_interface_test_suite(name, tags = []):
113114
target_under_test = "//test/fixtures/module_interface:toy_module",
114115
)
115116

117+
# Test that dependency swiftinterface files are included as action inputs
118+
action_inputs_test(
119+
name = "{}_dependencies_included_as_inputs".format(name),
120+
tags = all_tags,
121+
mnemonic = "SwiftCompileModuleInterface",
122+
expected_inputs = [
123+
"ToyModule.swiftinterface",
124+
],
125+
target_under_test = "//test/fixtures/module_interface:toy_module",
126+
)
127+
116128
native.test_suite(
117129
name = name,
118130
tags = all_tags,

test/rules/action_inputs_test.bzl

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
"""Rules for testing action inputs contain expected files."""
2+
3+
load("@bazel_skylib//lib:collections.bzl", "collections")
4+
load("@bazel_skylib//lib:unittest.bzl", "analysistest", "unittest")
5+
6+
def _action_inputs_test_impl(ctx):
7+
env = analysistest.begin(ctx)
8+
target_under_test = analysistest.target_under_test(env)
9+
10+
actions = analysistest.target_actions(env)
11+
mnemonic = ctx.attr.mnemonic
12+
matching_actions = [
13+
action
14+
for action in actions
15+
if action.mnemonic == mnemonic
16+
]
17+
if not matching_actions:
18+
actual_mnemonics = collections.uniq(
19+
[action.mnemonic for action in actions],
20+
)
21+
unittest.fail(
22+
env,
23+
("Target '{}' registered no actions with the mnemonic '{}' " +
24+
"(it had {}).").format(
25+
str(target_under_test.label),
26+
mnemonic,
27+
actual_mnemonics,
28+
),
29+
)
30+
return analysistest.end(env)
31+
if len(matching_actions) != 1:
32+
unittest.fail(
33+
env,
34+
("Expected exactly one action with the mnemonic '{}', " +
35+
"but found {}.").format(
36+
mnemonic,
37+
len(matching_actions),
38+
),
39+
)
40+
return analysistest.end(env)
41+
42+
action = matching_actions[0]
43+
message_prefix = "In {} action for target '{}', ".format(
44+
mnemonic,
45+
str(target_under_test.label),
46+
)
47+
48+
input_paths = [input.short_path for input in action.inputs.to_list()]
49+
50+
for expected_input in ctx.attr.expected_inputs:
51+
found = False
52+
for path in input_paths:
53+
if expected_input in path:
54+
found = True
55+
break
56+
if not found:
57+
unittest.fail(
58+
env,
59+
"{}expected inputs to contain file matching '{}', but it did not. Inputs: {}".format(
60+
message_prefix,
61+
expected_input,
62+
input_paths,
63+
),
64+
)
65+
66+
for not_expected_input in ctx.attr.not_expected_inputs:
67+
found = False
68+
for path in input_paths:
69+
if not_expected_input in path:
70+
found = True
71+
break
72+
if found:
73+
unittest.fail(
74+
env,
75+
"{}expected inputs to not contain file matching '{}', but it did. Inputs: {}".format(
76+
message_prefix,
77+
not_expected_input,
78+
input_paths,
79+
),
80+
)
81+
82+
return analysistest.end(env)
83+
84+
def make_action_inputs_test_rule(config_settings = {}):
85+
"""A `action_inputs_test`-like rule with custom configs.
86+
87+
Args:
88+
config_settings: A dictionary of configuration settings and their values
89+
that should be applied during tests.
90+
91+
Returns:
92+
A rule returned by `analysistest.make` that has the `action_inputs_test`
93+
interface and the given config settings.
94+
"""
95+
return analysistest.make(
96+
_action_inputs_test_impl,
97+
attrs = {
98+
"mnemonic": attr.string(
99+
mandatory = True,
100+
doc = "The mnemonic of the action to test.",
101+
),
102+
"expected_inputs": attr.string_list(
103+
default = [],
104+
doc = "List of file patterns that should be present in action inputs.",
105+
),
106+
"not_expected_inputs": attr.string_list(
107+
default = [],
108+
doc = "List of file patterns that should not be present in action inputs.",
109+
),
110+
},
111+
config_settings = config_settings,
112+
)
113+
114+
# A default instantiation of the rule when no custom config settings are needed.
115+
action_inputs_test = make_action_inputs_test_rule()

0 commit comments

Comments
 (0)