Skip to content

Commit 89c0878

Browse files
committed
Fix swift_import with dependencies that include .swiftinterface files
1 parent 532d7ea commit 89c0878

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
@@ -2005,8 +2005,19 @@ def _dependencies_swiftmodules_configurator(prerequisites, args):
20052005
uniquify = True,
20062006
)
20072007

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

20122023
def _module_aliases_configurator(prerequisites, args):
@@ -2064,7 +2075,6 @@ def _plugin_search_paths_configurator(prerequisites, args):
20642075

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

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

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

20832104
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)