Skip to content

Commit c640605

Browse files
committed
Fix swift_import with dependencies that include .swiftinterface files
1 parent f846226 commit c640605

File tree

3 files changed

+151
-3
lines changed

3 files changed

+151
-3
lines changed

swift/toolchains/config/compile_config.bzl

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2006,8 +2006,19 @@ def _dependencies_swiftmodules_configurator(prerequisites, args):
20062006
uniquify = True,
20072007
)
20082008

2009+
# Include both swiftmodule and swiftinterface files as inputs to ensure
2010+
# they are available in the sandbox for compilation
2011+
transitive_inputs = []
2012+
for module in prerequisites.transitive_modules:
2013+
swift_module = module.swift
2014+
if swift_module:
2015+
if swift_module.swiftmodule:
2016+
transitive_inputs.append(swift_module.swiftmodule)
2017+
if swift_module.swiftinterface:
2018+
transitive_inputs.append(swift_module.swiftinterface)
2019+
20092020
return ConfigResultInfo(
2010-
inputs = prerequisites.transitive_swiftmodules,
2021+
inputs = transitive_inputs,
20112022
)
20122023

20132024
def _module_aliases_configurator(prerequisites, args):
@@ -2065,7 +2076,6 @@ def _plugin_search_paths_configurator(prerequisites, args):
20652076

20662077
def _dependencies_swiftmodules_vfsoverlay_configurator(prerequisites, args, is_frontend = False):
20672078
"""Provides a single `.swiftmodule` search path using a VFS overlay."""
2068-
swiftmodules = prerequisites.transitive_swiftmodules
20692079

20702080
# Bug: `swiftc` doesn't pass its `-vfsoverlay` arg to the frontend.
20712081
# Workaround: Pass `-vfsoverlay` directly via `-Xfrontend`.
@@ -2077,8 +2087,19 @@ def _dependencies_swiftmodules_vfsoverlay_configurator(prerequisites, args, is_f
20772087
"-I{}".format(prerequisites.vfsoverlay_search_path),
20782088
)
20792089

2090+
# Include both swiftmodule and swiftinterface files as inputs to ensure
2091+
# they are available in the sandbox for compilation
2092+
transitive_inputs = [prerequisites.vfsoverlay_file]
2093+
for module in prerequisites.transitive_modules:
2094+
swift_module = module.swift
2095+
if swift_module:
2096+
if swift_module.swiftmodule:
2097+
transitive_inputs.append(swift_module.swiftmodule)
2098+
if swift_module.swiftinterface:
2099+
transitive_inputs.append(swift_module.swiftinterface)
2100+
20802101
return ConfigResultInfo(
2081-
inputs = swiftmodules + [prerequisites.vfsoverlay_file],
2102+
inputs = transitive_inputs,
20822103
)
20832104

20842105
def _explicit_swift_module_map_configurator(prerequisites, args, is_frontend = False):

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)