From cbfffbc6fa255969f8b762ea4dc0890e06ec0e52 Mon Sep 17 00:00:00 2001 From: Damien DeVille Date: Mon, 6 Dec 2021 10:01:10 -0800 Subject: [PATCH] Add the Windows import library to the providers when building a cdylib (#1044) Co-authored-by: UebelAndre --- rust/private/rustc.bzl | 21 +++++-- test/unit/cc_info/cc_info_test.bzl | 3 +- test/unit/win_interface_library/BUILD.bazel | 4 ++ test/unit/win_interface_library/bin.cc | 6 ++ test/unit/win_interface_library/lib.rs | 4 ++ .../win_interface_library_analysis_test.bzl | 55 +++++++++++++++++++ 6 files changed, 88 insertions(+), 5 deletions(-) create mode 100644 test/unit/win_interface_library/BUILD.bazel create mode 100644 test/unit/win_interface_library/bin.cc create mode 100644 test/unit/win_interface_library/lib.rs create mode 100644 test/unit/win_interface_library/win_interface_library_analysis_test.bzl diff --git a/rust/private/rustc.bzl b/rust/private/rustc.bzl index 89aa097ecf..86c741051e 100644 --- a/rust/private/rustc.bzl +++ b/rust/private/rustc.bzl @@ -777,10 +777,21 @@ def rustc_compile_action( else: formatted_version = "" + outputs = [crate_info.output] + + # For a cdylib that might be added as a dependency to a cc_* target on Windows, it is important to include the + # interface library that rustc generates in the output files. + interface_library = None + if toolchain.os == "windows" and crate_info.type == "cdylib": + # Rustc generates the import library with a `.dll.lib` extension rather than the usual `.lib` one that msvc + # expects (see https://github.com/rust-lang/rust/pull/29520 for more context). + interface_library = ctx.actions.declare_file(crate_info.output.basename + ".lib") + outputs.append(interface_library) + ctx.actions.run( executable = ctx.executable._process_wrapper, inputs = compile_inputs, - outputs = [crate_info.output], + outputs = outputs, env = env, arguments = args.all, mnemonic = "Rustc", @@ -806,20 +817,20 @@ def rustc_compile_action( dep_info, DefaultInfo( # nb. This field is required for cc_library to depend on our output. - files = depset([crate_info.output]), + files = depset(outputs), runfiles = runfiles, executable = crate_info.output if crate_info.type == "bin" or crate_info.is_test or out_binary else None, ), ] if toolchain.target_arch != "wasm32": - providers += establish_cc_info(ctx, attr, crate_info, toolchain, cc_toolchain, feature_configuration) + providers += establish_cc_info(ctx, attr, crate_info, toolchain, cc_toolchain, feature_configuration, interface_library) return providers def _is_dylib(dep): return not bool(dep.static_library or dep.pic_static_library) -def establish_cc_info(ctx, attr, crate_info, toolchain, cc_toolchain, feature_configuration): +def establish_cc_info(ctx, attr, crate_info, toolchain, cc_toolchain, feature_configuration, interface_library): """If the produced crate is suitable yield a CcInfo to allow for interop with cc rules Args: @@ -829,6 +840,7 @@ def establish_cc_info(ctx, attr, crate_info, toolchain, cc_toolchain, feature_co toolchain (rust_toolchain): The current `rust_toolchain` cc_toolchain (CcToolchainInfo): The current `CcToolchainInfo` feature_configuration (FeatureConfiguration): Feature configuration to be queried. + interface_library (File): Optional interface library for cdylib crates on Windows. Returns: list: A list containing the CcInfo provider @@ -877,6 +889,7 @@ def establish_cc_info(ctx, attr, crate_info, toolchain, cc_toolchain, feature_co feature_configuration = feature_configuration, cc_toolchain = cc_toolchain, dynamic_library = crate_info.output, + interface_library = interface_library, ) else: fail("Unexpected case") diff --git a/test/unit/cc_info/cc_info_test.bzl b/test/unit/cc_info/cc_info_test.bzl index 0f0fae18b5..9873a1ffbe 100644 --- a/test/unit/cc_info/cc_info_test.bzl +++ b/test/unit/cc_info/cc_info_test.bzl @@ -22,10 +22,11 @@ def _assert_cc_info_has_library_to_link(env, tut, type, ccinfo_count): if type == "cdylib": asserts.true(env, library_to_link.dynamic_library != None) - asserts.equals(env, None, library_to_link.interface_library) if _is_dylib_on_windows(env.ctx): + asserts.true(env, library_to_link.interface_library != None) asserts.true(env, library_to_link.resolved_symlink_dynamic_library == None) else: + asserts.equals(env, None, library_to_link.interface_library) asserts.true(env, library_to_link.resolved_symlink_dynamic_library != None) asserts.equals(env, None, library_to_link.resolved_symlink_interface_library) asserts.equals(env, None, library_to_link.static_library) diff --git a/test/unit/win_interface_library/BUILD.bazel b/test/unit/win_interface_library/BUILD.bazel new file mode 100644 index 0000000000..a0a3e19991 --- /dev/null +++ b/test/unit/win_interface_library/BUILD.bazel @@ -0,0 +1,4 @@ +load(":win_interface_library_analysis_test.bzl", "win_interface_library_analysis_test_suite") + +############################ UNIT TESTS ############################# +win_interface_library_analysis_test_suite(name = "win_interface_library_analysis_test_suite") diff --git a/test/unit/win_interface_library/bin.cc b/test/unit/win_interface_library/bin.cc new file mode 100644 index 0000000000..a3c073a5e7 --- /dev/null +++ b/test/unit/win_interface_library/bin.cc @@ -0,0 +1,6 @@ +extern "C" void hello(void); + +int main(int argc, char **argv) { + hello(); + return 0; +} diff --git a/test/unit/win_interface_library/lib.rs b/test/unit/win_interface_library/lib.rs new file mode 100644 index 0000000000..1f19b6db39 --- /dev/null +++ b/test/unit/win_interface_library/lib.rs @@ -0,0 +1,4 @@ +#[no_mangle] +pub extern "C" fn hello() { + println!("Hello"); +} diff --git a/test/unit/win_interface_library/win_interface_library_analysis_test.bzl b/test/unit/win_interface_library/win_interface_library_analysis_test.bzl new file mode 100644 index 0000000000..4edab68cf9 --- /dev/null +++ b/test/unit/win_interface_library/win_interface_library_analysis_test.bzl @@ -0,0 +1,55 @@ +"""Analysis tests for exporting the Windows interface library.""" + +load("@bazel_skylib//lib:unittest.bzl", "analysistest", "asserts") +load("@rules_cc//cc:defs.bzl", "cc_binary") +load("//rust:defs.bzl", "rust_shared_library") + +def _win_interface_library_test_impl(ctx): + env = analysistest.begin(ctx) + target = analysistest.target_under_test(env) + + files = target[DefaultInfo].files.to_list() + cc_library = target[CcInfo].linking_context.linker_inputs.to_list()[0].libraries[0] + + # Make sure that we have both the `.dll` and the `.dll.lib` file in the default info's files + asserts.equals(env, len(files), 2) + asserts.true(env, files[0].basename.endswith(".dll")) + asserts.true(env, files[1].basename.endswith(".dll.lib")) + + # Make sure that the cc_library has both a dynamic and interface library + asserts.true(env, cc_library.dynamic_library != None) + asserts.true(env, cc_library.interface_library != None) + + return analysistest.end(env) + +win_interface_library_test = analysistest.make(_win_interface_library_test_impl) + +def win_interface_library_analysis_test_suite(name): + """Analysis tests for exporting the Windows interface library. + + Args: + name: the test suite name + """ + rust_shared_library( + name = "mylib", + srcs = ["lib.rs"], + target_compatible_with = ["@platforms//os:windows"], + ) + + cc_binary( + name = "mybin", + srcs = ["bin.cc"], + deps = [":mylib"], + target_compatible_with = ["@platforms//os:windows"], + ) + + win_interface_library_test( + name = "win_interface_library_test", + target_under_test = ":mylib", + target_compatible_with = ["@platforms//os:windows"], + ) + + native.test_suite( + name = name, + tests = [":win_interface_library_test"], + )