diff --git a/swift/toolchains/config/compile_config.bzl b/swift/toolchains/config/compile_config.bzl index 606c8092a..504da688e 100644 --- a/swift/toolchains/config/compile_config.bzl +++ b/swift/toolchains/config/compile_config.bzl @@ -2005,8 +2005,19 @@ def _dependencies_swiftmodules_configurator(prerequisites, args): uniquify = True, ) + # Include both swiftmodule and swiftinterface files as inputs to ensure + # they are available in the sandbox for compilation + transitive_inputs = [] + for module in prerequisites.transitive_modules: + swift_module = module.swift + if swift_module: + if swift_module.swiftmodule: + transitive_inputs.append(swift_module.swiftmodule) + if swift_module.swiftinterface: + transitive_inputs.append(swift_module.swiftinterface) + return ConfigResultInfo( - inputs = prerequisites.transitive_swiftmodules, + inputs = transitive_inputs, ) def _module_aliases_configurator(prerequisites, args): @@ -2064,7 +2075,6 @@ def _plugin_search_paths_configurator(prerequisites, args): def _dependencies_swiftmodules_vfsoverlay_configurator(prerequisites, args, is_frontend = False): """Provides a single `.swiftmodule` search path using a VFS overlay.""" - swiftmodules = prerequisites.transitive_swiftmodules # Bug: `swiftc` doesn't pass its `-vfsoverlay` arg to the frontend. # Workaround: Pass `-vfsoverlay` directly via `-Xfrontend`. @@ -2076,8 +2086,19 @@ def _dependencies_swiftmodules_vfsoverlay_configurator(prerequisites, args, is_f "-I{}".format(prerequisites.vfsoverlay_search_path), ) + # Include both swiftmodule and swiftinterface files as inputs to ensure + # they are available in the sandbox for compilation + transitive_inputs = [prerequisites.vfsoverlay_file] + for module in prerequisites.transitive_modules: + swift_module = module.swift + if swift_module: + if swift_module.swiftmodule: + transitive_inputs.append(swift_module.swiftmodule) + if swift_module.swiftinterface: + transitive_inputs.append(swift_module.swiftinterface) + return ConfigResultInfo( - inputs = swiftmodules + [prerequisites.vfsoverlay_file], + inputs = transitive_inputs, ) def _explicit_swift_module_map_configurator(prerequisites, args, is_frontend = False): diff --git a/test/module_interface_tests.bzl b/test/module_interface_tests.bzl index fc556fc03..1f31cc617 100644 --- a/test/module_interface_tests.bzl +++ b/test/module_interface_tests.bzl @@ -19,6 +19,7 @@ load( "//test/rules:action_command_line_test.bzl", "make_action_command_line_test_rule", ) +load("//test/rules:action_inputs_test.bzl", "action_inputs_test") load("//test/rules:provider_test.bzl", "provider_test") explicit_swift_module_map_test = make_action_command_line_test_rule( @@ -113,6 +114,17 @@ def module_interface_test_suite(name, tags = []): target_under_test = "//test/fixtures/module_interface:toy_module", ) + # Test that dependency swiftinterface files are included as action inputs + action_inputs_test( + name = "{}_dependencies_included_as_inputs".format(name), + tags = all_tags, + mnemonic = "SwiftCompileModuleInterface", + expected_inputs = [ + "ToyModule.swiftinterface", + ], + target_under_test = "//test/fixtures/module_interface:toy_module", + ) + native.test_suite( name = name, tags = all_tags, diff --git a/test/rules/action_inputs_test.bzl b/test/rules/action_inputs_test.bzl new file mode 100644 index 000000000..ac67403c8 --- /dev/null +++ b/test/rules/action_inputs_test.bzl @@ -0,0 +1,115 @@ +"""Rules for testing action inputs contain expected files.""" + +load("@bazel_skylib//lib:collections.bzl", "collections") +load("@bazel_skylib//lib:unittest.bzl", "analysistest", "unittest") + +def _action_inputs_test_impl(ctx): + env = analysistest.begin(ctx) + target_under_test = analysistest.target_under_test(env) + + actions = analysistest.target_actions(env) + mnemonic = ctx.attr.mnemonic + matching_actions = [ + action + for action in actions + if action.mnemonic == mnemonic + ] + if not matching_actions: + actual_mnemonics = collections.uniq( + [action.mnemonic for action in actions], + ) + unittest.fail( + env, + ("Target '{}' registered no actions with the mnemonic '{}' " + + "(it had {}).").format( + str(target_under_test.label), + mnemonic, + actual_mnemonics, + ), + ) + return analysistest.end(env) + if len(matching_actions) != 1: + unittest.fail( + env, + ("Expected exactly one action with the mnemonic '{}', " + + "but found {}.").format( + mnemonic, + len(matching_actions), + ), + ) + return analysistest.end(env) + + action = matching_actions[0] + message_prefix = "In {} action for target '{}', ".format( + mnemonic, + str(target_under_test.label), + ) + + input_paths = [input.short_path for input in action.inputs.to_list()] + + for expected_input in ctx.attr.expected_inputs: + found = False + for path in input_paths: + if expected_input in path: + found = True + break + if not found: + unittest.fail( + env, + "{}expected inputs to contain file matching '{}', but it did not. Inputs: {}".format( + message_prefix, + expected_input, + input_paths, + ), + ) + + for not_expected_input in ctx.attr.not_expected_inputs: + found = False + for path in input_paths: + if not_expected_input in path: + found = True + break + if found: + unittest.fail( + env, + "{}expected inputs to not contain file matching '{}', but it did. Inputs: {}".format( + message_prefix, + not_expected_input, + input_paths, + ), + ) + + return analysistest.end(env) + +def make_action_inputs_test_rule(config_settings = {}): + """A `action_inputs_test`-like rule with custom configs. + + Args: + config_settings: A dictionary of configuration settings and their values + that should be applied during tests. + + Returns: + A rule returned by `analysistest.make` that has the `action_inputs_test` + interface and the given config settings. + """ + return analysistest.make( + _action_inputs_test_impl, + attrs = { + "mnemonic": attr.string( + mandatory = True, + doc = "The mnemonic of the action to test.", + ), + "expected_inputs": attr.string_list( + default = [], + doc = "List of file patterns that should be present in action inputs.", + ), + "not_expected_inputs": attr.string_list( + default = [], + doc = "List of file patterns that should not be present in action inputs.", + ), + }, + config_settings = config_settings, + ) + +# A default instantiation of the rule when no custom config settings are needed. +action_inputs_test = make_action_inputs_test_rule()