diff --git a/examples/WORKSPACE.bazel b/examples/WORKSPACE.bazel index 005d052c45..ad29a83a2f 100644 --- a/examples/WORKSPACE.bazel +++ b/examples/WORKSPACE.bazel @@ -16,6 +16,10 @@ rust_register_toolchains( edition = "2018", ) +load("@rules_rust//tools/rust_analyzer:deps.bzl", "rust_analyzer_dependencies") + +rust_analyzer_dependencies() + load("@rules_rust//bindgen:repositories.bzl", "rust_bindgen_dependencies", "rust_bindgen_register_toolchains") rust_bindgen_dependencies() diff --git a/rust/private/rust_analyzer.bzl b/rust/private/rust_analyzer.bzl index 84081970dd..48582b3c33 100644 --- a/rust/private/rust_analyzer.bzl +++ b/rust/private/rust_analyzer.bzl @@ -39,7 +39,7 @@ RustAnalyzerInfo = provider( ) def _rust_analyzer_aspect_impl(target, ctx): - if rust_common.crate_info not in target: + if rust_common.crate_info not in target and rust_common.test_crate_info not in target: return [] toolchain = find_toolchain(ctx) @@ -70,7 +70,12 @@ def _rust_analyzer_aspect_impl(target, ctx): crate_spec = ctx.actions.declare_file(ctx.label.name + ".rust_analyzer_crate_spec") - crate_info = target[rust_common.crate_info] + if rust_common.crate_info in target: + crate_info = target[rust_common.crate_info] + elif rust_common.test_crate_info in target: + crate_info = target[rust_common.test_crate_info].crate + else: + fail("Unexpected target type: {}".format(target)) rust_analyzer_info = RustAnalyzerInfo( crate = crate_info, @@ -101,7 +106,14 @@ def find_proc_macro_dylib_path(toolchain, target): Returns: (path): The path to the proc macro dylib, or None if this crate is not a proc-macro. """ - if target[rust_common.crate_info].type != "proc-macro": + if rust_common.crate_info in target: + crate_info = target[rust_common.crate_info] + elif rust_common.test_crate_info in target: + crate_info = target[rust_common.test_crate_info].crate + else: + return None + + if crate_info.type != "proc-macro": return None dylib_ext = system_to_dylib_ext(triple_to_system(toolchain.target_triple)) diff --git a/test/rust_analyzer/rust_analyzer_test_runner.sh b/test/rust_analyzer/rust_analyzer_test_runner.sh index 781fd34c50..1696506030 100755 --- a/test/rust_analyzer/rust_analyzer_test_runner.sh +++ b/test/rust_analyzer/rust_analyzer_test_runner.sh @@ -21,7 +21,7 @@ function generate_workspace() { local new_workspace="${temp_dir}/rules_rust_test_rust_analyzer" mkdir -p "${new_workspace}" - cat << EOF > "${new_workspace}/WORKSPACE.bazel" + cat <"${new_workspace}/WORKSPACE.bazel" workspace(name = "rules_rust_test_rust_analyzer") local_repository( name = "rules_rust", @@ -33,7 +33,7 @@ load("@rules_rust//tools/rust_analyzer:deps.bzl", "rust_analyzer_dependencies") rust_analyzer_dependencies() EOF -cat << EOF > "${new_workspace}/.bazelrc" + cat <"${new_workspace}/.bazelrc" build --keep_going test --test_output=errors # The 'strict' config is used to ensure extra checks are run on the test @@ -45,17 +45,18 @@ build:strict --aspects=@rules_rust//rust:defs.bzl%rust_clippy_aspect build:strict --output_groups=+clippy_checks EOF - echo "${new_workspace}" + echo "${new_workspace}" } function rust_analyzer_test() { local source_dir="$1" local workspace="$2" - + local generator_arg="$3" + echo "Testing '$(basename "${source_dir}")'" - rm -f "${workspace}"/*.rs "${workspace}"/*.json "${workspace}/BUILD.bazel" + rm -f "${workspace}"/*.rs "${workspace}"/*.json "${workspace}"/*.bzl "${workspace}/BUILD.bazel" "${workspace}/BUILD.bazel-e" cp -r "${source_dir}"/* "${workspace}" - + # Drop the 'manual' tags if [ "$(uname)" == "Darwin" ]; then SEDOPTS=(-i '' -e) @@ -63,17 +64,29 @@ function rust_analyzer_test() { SEDOPTS=(-i) fi sed ${SEDOPTS[@]} 's/"manual"//' "${workspace}/BUILD.bazel" - - pushd "${workspace}" &> /dev/null + + pushd "${workspace}" &>/dev/null echo "Generating rust-project.json..." - bazel run "@rules_rust//tools/rust_analyzer:gen_rust_project" -- //:mylib_test + if [[ -n "${generator_arg}" ]]; then + bazel run "@rules_rust//tools/rust_analyzer:gen_rust_project" -- "${generator_arg}" + else + bazel run "@rules_rust//tools/rust_analyzer:gen_rust_project" + fi echo "Building..." bazel build //... echo "Testing..." bazel test //... echo "Building with Aspects..." bazel build //... --config=strict - popd &> /dev/null + popd &>/dev/null +} + +function cleanup() { + local workspace="$1" + pushd "${workspace}" &>/dev/null + bazel clean --async + popd &>/dev/null + rm -rf "${workspace}" } function run_test_suite() { @@ -86,10 +99,20 @@ function run_test_suite() { continue fi - rust_analyzer_test "${test_dir}" "${temp_workspace}" + # Some tests have arguments that need to be passed to the rust-project.json generator. + if [[ "${test_dir}" = "aspect_traversal_test" ]]; then + test_arg="//mylib_test" + elif [[ "${test_dir}" = "merging_crates_test" ]]; then + test_arg="//mylib_test" + else + test_arg="" + fi + + rust_analyzer_test "${test_dir}" "${temp_workspace}" "${test_arg}" done - rm -rf "${temp_workspace}" + echo "Done" + cleanup "${temp_workspace}" } run_test_suite diff --git a/test/rust_analyzer/static_and_shared_lib_test/BUILD.bazel b/test/rust_analyzer/static_and_shared_lib_test/BUILD.bazel new file mode 100644 index 0000000000..7efd036406 --- /dev/null +++ b/test/rust_analyzer/static_and_shared_lib_test/BUILD.bazel @@ -0,0 +1,38 @@ +load( + "@rules_rust//rust:defs.bzl", + "rust_shared_library", + "rust_static_library", + "rust_test", +) + +rust_shared_library( + name = "greeter_cdylib", + srcs = [ + "greeter.rs", + "shared_lib.rs", + ], + crate_root = "shared_lib.rs", + edition = "2018", +) + +rust_static_library( + name = "greeter_staticlib", + srcs = [ + "greeter.rs", + "static_lib.rs", + ], + crate_root = "static_lib.rs", + edition = "2018", +) + +rust_test( + name = "rust_project_json_test", + srcs = ["rust_project_json_test.rs"], + data = [":rust-project.json"], + edition = "2018", + env = {"RUST_PROJECT_JSON": "$(rootpath :rust-project.json)"}, + # This target is tagged as manual since it's not expected to pass in + # contexts outside of `//test/rust_analyzer:rust_analyzer_test`. Run + # that target to execute this test. + tags = ["manual"], +) diff --git a/test/rust_analyzer/static_and_shared_lib_test/greeter.rs b/test/rust_analyzer/static_and_shared_lib_test/greeter.rs new file mode 100644 index 0000000000..ac87521090 --- /dev/null +++ b/test/rust_analyzer/static_and_shared_lib_test/greeter.rs @@ -0,0 +1,61 @@ +/// Object that displays a greeting. +pub struct Greeter { + greeting: String, +} + +/// Implementation of Greeter. +impl Greeter { + /// Constructs a new `Greeter`. + /// + /// # Examples + /// + /// ``` + /// use hello_lib::greeter::Greeter; + /// + /// let greeter = Greeter::new("Hello"); + /// ``` + pub fn new(greeting: &str) -> Greeter { + Greeter { + greeting: greeting.to_string(), + } + } + + /// Returns the greeting as a string. + /// + /// # Examples + /// + /// ``` + /// use hello_lib::greeter::Greeter; + /// + /// let greeter = Greeter::new("Hello"); + /// let greeting = greeter.greeting("World"); + /// ``` + pub fn greeting(&self, thing: &str) -> String { + format!("{} {}", &self.greeting, thing) + } + + /// Prints the greeting. + /// + /// # Examples + /// + /// ``` + /// use hello_lib::greeter::Greeter; + /// + /// let greeter = Greeter::new("Hello"); + /// greeter.greet("World"); + /// ``` + pub fn greet(&self, thing: &str) { + println!("{} {}", &self.greeting, thing); + } +} + +#[cfg(test)] +mod test { + use super::Greeter; + + #[test] + fn test_greeting() { + let hello = Greeter::new("Hi"); + assert_eq!("Hi Rust", hello.greeting("Rust")); + } +} diff --git a/test/rust_analyzer/static_and_shared_lib_test/rust_project_json_test.rs b/test/rust_analyzer/static_and_shared_lib_test/rust_project_json_test.rs new file mode 100644 index 0000000000..fc573d6493 --- /dev/null +++ b/test/rust_analyzer/static_and_shared_lib_test/rust_project_json_test.rs @@ -0,0 +1,22 @@ +#[cfg(test)] +mod tests { + use std::env; + use std::path::PathBuf; + + #[test] + fn test_deps_of_crate_and_its_test_are_merged() { + let rust_project_path = PathBuf::from(env::var("RUST_PROJECT_JSON").unwrap()); + + let content = std::fs::read_to_string(&rust_project_path) + .unwrap_or_else(|_| panic!("couldn't open {:?}", &rust_project_path)); + + assert!( + content.contains(r#"{"display_name":"greeter_cdylib","root_module":"shared_lib.rs"#), + "expected rust-project.json to contain a rust_shared_library target." + ); + assert!( + content.contains(r#"{"display_name":"greeter_staticlib","root_module":"static_lib.rs"#), + "expected rust-project.json to contain a rust_static_library target." + ); + } +} diff --git a/test/rust_analyzer/static_and_shared_lib_test/shared_lib.rs b/test/rust_analyzer/static_and_shared_lib_test/shared_lib.rs new file mode 100644 index 0000000000..44969c66c7 --- /dev/null +++ b/test/rust_analyzer/static_and_shared_lib_test/shared_lib.rs @@ -0,0 +1 @@ +pub mod greeter; diff --git a/test/rust_analyzer/static_and_shared_lib_test/static_lib.rs b/test/rust_analyzer/static_and_shared_lib_test/static_lib.rs new file mode 100644 index 0000000000..44969c66c7 --- /dev/null +++ b/test/rust_analyzer/static_and_shared_lib_test/static_lib.rs @@ -0,0 +1 @@ +pub mod greeter;