Skip to content

Commit 8f1424c

Browse files
authored
Fixed loss of data in gen_rust_project for duplicate crate ids (#3317)
Previously `aliases` and `source` were susceptible to being dropped from `rust-project.json` in the event a crate without either of these fields was processed before another crate with them. These are now aggregated and `gen_rust_project` should be more resilient to the ordering targets are listed in aquery outputs.
1 parent 77eec0c commit 8f1424c

File tree

3 files changed

+235
-19
lines changed

3 files changed

+235
-19
lines changed

test/rust_analyzer/generated_srcs_test/rust_project_json_test.rs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ mod tests {
2727
let rust_project_path = PathBuf::from(env::var("RUST_PROJECT_JSON").unwrap());
2828
let content = std::fs::read_to_string(&rust_project_path)
2929
.unwrap_or_else(|_| panic!("couldn't open {:?}", &rust_project_path));
30-
println!("{}", content);
3130
let project: Project =
3231
serde_json::from_str(&content).expect("Failed to deserialize project JSON");
3332

@@ -42,15 +41,15 @@ mod tests {
4241
.unwrap();
4342
println!("output_base: {output_base}");
4443

45-
let gen = project
44+
let with_gen = project
4645
.crates
4746
.iter()
4847
.find(|c| &c.display_name == "generated_srcs")
4948
.unwrap();
50-
assert!(gen.root_module.starts_with("/"));
51-
assert!(gen.root_module.ends_with("/lib.rs"));
49+
assert!(with_gen.root_module.starts_with("/"));
50+
assert!(with_gen.root_module.ends_with("/lib.rs"));
5251

53-
let include_dirs = &gen.source.as_ref().unwrap().include_dirs;
52+
let include_dirs = &with_gen.source.as_ref().unwrap().include_dirs;
5453
assert!(include_dirs.len() == 1);
5554
assert!(include_dirs[0].starts_with(output_base));
5655
}

test/rust_analyzer/rust_analyzer_test_runner.sh

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#!/bin/bash
1+
#!/usr/bin/env bash
22

33
# This script creates temporary workspaces and generates `rust-project.json`
44
# files unique to the set of targets defined in the generated workspace.
@@ -77,6 +77,10 @@ function rust_analyzer_test() {
7777
local source_dir="$1"
7878
local workspace="$2"
7979
local generator_arg="$3"
80+
local rust_log="info"
81+
if [[ -n "${RUST_ANALYZER_TEST_DEBUG:-}" ]]; then
82+
rust_log="debug"
83+
fi
8084

8185
echo "Testing '$(basename "${source_dir}")'"
8286
rm -f "${workspace}"/*.rs "${workspace}"/*.json "${workspace}"/*.bzl "${workspace}/BUILD.bazel" "${workspace}/BUILD.bazel-e"
@@ -92,10 +96,11 @@ function rust_analyzer_test() {
9296

9397
pushd "${workspace}" &>/dev/null
9498
echo "Generating rust-project.json..."
99+
95100
if [[ -n "${generator_arg}" ]]; then
96-
bazel run "@rules_rust//tools/rust_analyzer:gen_rust_project" -- "${generator_arg}"
101+
RUST_LOG="${rust_log}" bazel run "@rules_rust//tools/rust_analyzer:gen_rust_project" -- "${generator_arg}"
97102
else
98-
bazel run "@rules_rust//tools/rust_analyzer:gen_rust_project"
103+
RUST_LOG="${rust_log}" bazel run "@rules_rust//tools/rust_analyzer:gen_rust_project"
99104
fi
100105
echo "Building..."
101106
bazel build //...
@@ -109,9 +114,11 @@ function rust_analyzer_test() {
109114
function cleanup() {
110115
local workspace="$1"
111116
pushd "${workspace}" &>/dev/null
112-
bazel clean --async
117+
bazel clean --expunge --async
113118
popd &>/dev/null
114-
rm -rf "${workspace}"
119+
if [[ -z "${RUST_ANALYZER_TEST_DEBUG:-}" ]]; then
120+
rm -rf "${workspace}"
121+
fi
115122
}
116123

117124
function run_test_suite() {

tools/rust_analyzer/aquery.rs

Lines changed: 219 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
use std::collections::{BTreeMap, BTreeSet};
2-
use std::fs::File;
32
use std::path::Path;
43
use std::path::PathBuf;
54
use std::process::Command;
@@ -71,7 +70,8 @@ pub fn get_crate_specs(
7170
log::debug!("Get crate specs with targets: {:?}", targets);
7271
let target_pattern = format!("deps({})", targets.join("+"));
7372

74-
let aquery_output = Command::new(bazel)
73+
let mut aquery_command = Command::new(bazel);
74+
aquery_command
7575
.current_dir(workspace)
7676
.env_remove("BAZELISK_SKIP_WRAPPER")
7777
.env_remove("BUILD_WORKING_DIRECTORY")
@@ -86,18 +86,26 @@ pub fn get_crate_specs(
8686
.arg(format!(
8787
r#"outputs(".*\.rust_analyzer_crate_spec\.json",{target_pattern})"#
8888
))
89-
.arg("--output=jsonproto")
90-
.output()?;
89+
.arg("--output=jsonproto");
90+
log::trace!("Running aquery: {:#?}", aquery_command);
91+
let aquery_output = aquery_command
92+
.output()
93+
.context("Failed to spawn aquery command")?;
9194

92-
let crate_spec_files =
93-
parse_aquery_output_files(execution_root, &String::from_utf8(aquery_output.stdout)?)?;
95+
let aquery_results = String::from_utf8(aquery_output.stdout)
96+
.context("Failed to decode aquery results as utf-8.")?;
97+
98+
log::trace!("Aquery results: {}", &aquery_results);
99+
100+
let crate_spec_files = parse_aquery_output_files(execution_root, &aquery_results)?;
94101

95102
let crate_specs = crate_spec_files
96103
.into_iter()
97104
.map(|file| {
98-
let f = File::open(&file)
99-
.with_context(|| format!("Failed to open file: {}", file.display()))?;
100-
serde_json::from_reader(f)
105+
let content = std::fs::read_to_string(&file)
106+
.with_context(|| format!("Failed to read file: {}", file.display()))?;
107+
log::trace!("{}\n{}", file.display(), content);
108+
serde_json::from_str(&content)
101109
.with_context(|| format!("Failed to deserialize file: {}", file.display()))
102110
})
103111
.collect::<anyhow::Result<Vec<CrateSpec>>>()?;
@@ -177,6 +185,22 @@ fn consolidate_crate_specs(crate_specs: Vec<CrateSpec>) -> anyhow::Result<BTreeS
177185
log::debug!("{:?}", spec);
178186
if let Some(existing) = consolidated_specs.get_mut(&spec.crate_id) {
179187
existing.deps.extend(spec.deps);
188+
existing.aliases.extend(spec.aliases);
189+
190+
if let Some(source) = &mut existing.source {
191+
if let Some(mut new_source) = spec.source {
192+
new_source
193+
.exclude_dirs
194+
.retain(|src| !source.exclude_dirs.contains(src));
195+
new_source
196+
.include_dirs
197+
.retain(|src| !source.include_dirs.contains(src));
198+
source.exclude_dirs.extend(new_source.exclude_dirs);
199+
source.include_dirs.extend(new_source.include_dirs);
200+
}
201+
} else {
202+
existing.source = spec.source;
203+
}
180204

181205
spec.cfg.retain(|cfg| !existing.cfg.contains(cfg));
182206
existing.cfg.extend(spec.cfg);
@@ -622,4 +646,190 @@ mod test {
622646
);
623647
}
624648
}
649+
650+
#[test]
651+
fn consolidate_spec_with_aliases() {
652+
let crate_specs = vec![
653+
CrateSpec {
654+
aliases: BTreeMap::new(),
655+
crate_id: "ID-mylib.rs".into(),
656+
display_name: "mylib".into(),
657+
edition: "2018".into(),
658+
root_module: "mylib.rs".into(),
659+
is_workspace_member: true,
660+
deps: BTreeSet::new(),
661+
proc_macro_dylib_path: None,
662+
source: None,
663+
cfg: vec!["test".into(), "debug_assertions".into()],
664+
env: BTreeMap::new(),
665+
target: "x86_64-unknown-linux-gnu".into(),
666+
crate_type: "rlib".into(),
667+
},
668+
CrateSpec {
669+
aliases: BTreeMap::from([("ID-mylib_dep.rs".into(), "aliased_name".into())]),
670+
crate_id: "ID-mylib.rs".into(),
671+
display_name: "mylib_test".into(),
672+
edition: "2018".into(),
673+
root_module: "mylib.rs".into(),
674+
is_workspace_member: true,
675+
deps: BTreeSet::new(),
676+
proc_macro_dylib_path: None,
677+
source: None,
678+
cfg: vec!["test".into(), "debug_assertions".into()],
679+
env: BTreeMap::new(),
680+
target: "x86_64-unknown-linux-gnu".into(),
681+
crate_type: "bin".into(),
682+
},
683+
];
684+
685+
for perm in crate_specs.into_iter().permutations(2) {
686+
assert_eq!(
687+
consolidate_crate_specs(perm).unwrap(),
688+
BTreeSet::from([CrateSpec {
689+
aliases: BTreeMap::from([("ID-mylib_dep.rs".into(), "aliased_name".into())]),
690+
crate_id: "ID-mylib.rs".into(),
691+
display_name: "mylib".into(),
692+
edition: "2018".into(),
693+
root_module: "mylib.rs".into(),
694+
is_workspace_member: true,
695+
deps: BTreeSet::from([]),
696+
proc_macro_dylib_path: None,
697+
source: None,
698+
cfg: vec!["test".into(), "debug_assertions".into()],
699+
env: BTreeMap::new(),
700+
target: "x86_64-unknown-linux-gnu".into(),
701+
crate_type: "rlib".into(),
702+
}])
703+
);
704+
}
705+
}
706+
707+
#[test]
708+
fn consolidate_spec_with_sources() {
709+
let crate_specs = vec![
710+
CrateSpec {
711+
aliases: BTreeMap::new(),
712+
crate_id: "ID-mylib.rs".into(),
713+
display_name: "mylib".into(),
714+
edition: "2018".into(),
715+
root_module: "mylib.rs".into(),
716+
is_workspace_member: true,
717+
deps: BTreeSet::new(),
718+
proc_macro_dylib_path: None,
719+
source: None,
720+
cfg: vec!["test".into(), "debug_assertions".into()],
721+
env: BTreeMap::new(),
722+
target: "x86_64-unknown-linux-gnu".into(),
723+
crate_type: "rlib".into(),
724+
},
725+
CrateSpec {
726+
aliases: BTreeMap::new(),
727+
crate_id: "ID-mylib.rs".into(),
728+
display_name: "mylib_test".into(),
729+
edition: "2018".into(),
730+
root_module: "mylib.rs".into(),
731+
is_workspace_member: true,
732+
deps: BTreeSet::new(),
733+
proc_macro_dylib_path: None,
734+
source: Some(CrateSpecSource {
735+
exclude_dirs: vec!["exclude".into()],
736+
include_dirs: vec!["include".into()],
737+
}),
738+
cfg: vec!["test".into(), "debug_assertions".into()],
739+
env: BTreeMap::new(),
740+
target: "x86_64-unknown-linux-gnu".into(),
741+
crate_type: "bin".into(),
742+
},
743+
];
744+
745+
for perm in crate_specs.into_iter().permutations(2) {
746+
assert_eq!(
747+
consolidate_crate_specs(perm).unwrap(),
748+
BTreeSet::from([CrateSpec {
749+
aliases: BTreeMap::new(),
750+
crate_id: "ID-mylib.rs".into(),
751+
display_name: "mylib".into(),
752+
edition: "2018".into(),
753+
root_module: "mylib.rs".into(),
754+
is_workspace_member: true,
755+
deps: BTreeSet::from([]),
756+
proc_macro_dylib_path: None,
757+
source: Some(CrateSpecSource {
758+
exclude_dirs: vec!["exclude".into()],
759+
include_dirs: vec!["include".into()],
760+
}),
761+
cfg: vec!["test".into(), "debug_assertions".into()],
762+
env: BTreeMap::new(),
763+
target: "x86_64-unknown-linux-gnu".into(),
764+
crate_type: "rlib".into(),
765+
}])
766+
);
767+
}
768+
}
769+
770+
#[test]
771+
fn consolidate_spec_with_duplicate_sources() {
772+
let crate_specs = vec![
773+
CrateSpec {
774+
aliases: BTreeMap::new(),
775+
crate_id: "ID-mylib.rs".into(),
776+
display_name: "mylib".into(),
777+
edition: "2018".into(),
778+
root_module: "mylib.rs".into(),
779+
is_workspace_member: true,
780+
deps: BTreeSet::new(),
781+
proc_macro_dylib_path: None,
782+
source: Some(CrateSpecSource {
783+
exclude_dirs: vec!["exclude".into()],
784+
include_dirs: vec!["include".into()],
785+
}),
786+
cfg: vec!["test".into(), "debug_assertions".into()],
787+
env: BTreeMap::new(),
788+
target: "x86_64-unknown-linux-gnu".into(),
789+
crate_type: "rlib".into(),
790+
},
791+
CrateSpec {
792+
aliases: BTreeMap::new(),
793+
crate_id: "ID-mylib.rs".into(),
794+
display_name: "mylib_test".into(),
795+
edition: "2018".into(),
796+
root_module: "mylib.rs".into(),
797+
is_workspace_member: true,
798+
deps: BTreeSet::new(),
799+
proc_macro_dylib_path: None,
800+
source: Some(CrateSpecSource {
801+
exclude_dirs: vec!["exclude".into()],
802+
include_dirs: vec!["include".into()],
803+
}),
804+
cfg: vec!["test".into(), "debug_assertions".into()],
805+
env: BTreeMap::new(),
806+
target: "x86_64-unknown-linux-gnu".into(),
807+
crate_type: "bin".into(),
808+
},
809+
];
810+
811+
for perm in crate_specs.into_iter().permutations(2) {
812+
assert_eq!(
813+
consolidate_crate_specs(perm).unwrap(),
814+
BTreeSet::from([CrateSpec {
815+
aliases: BTreeMap::new(),
816+
crate_id: "ID-mylib.rs".into(),
817+
display_name: "mylib".into(),
818+
edition: "2018".into(),
819+
root_module: "mylib.rs".into(),
820+
is_workspace_member: true,
821+
deps: BTreeSet::from([]),
822+
proc_macro_dylib_path: None,
823+
source: Some(CrateSpecSource {
824+
exclude_dirs: vec!["exclude".into()],
825+
include_dirs: vec!["include".into()],
826+
}),
827+
cfg: vec!["test".into(), "debug_assertions".into()],
828+
env: BTreeMap::new(),
829+
target: "x86_64-unknown-linux-gnu".into(),
830+
crate_type: "rlib".into(),
831+
}])
832+
);
833+
}
834+
}
625835
}

0 commit comments

Comments
 (0)