xref: /aosp_15_r20/external/bazelbuild-rules_rust/tools/rust_analyzer/aquery.rs (revision d4726bddaa87cc4778e7472feed243fa4b6c267f)
1*d4726bddSHONG Yifan use std::collections::{BTreeMap, BTreeSet};
2*d4726bddSHONG Yifan use std::fs::File;
3*d4726bddSHONG Yifan use std::path::Path;
4*d4726bddSHONG Yifan use std::path::PathBuf;
5*d4726bddSHONG Yifan use std::process::Command;
6*d4726bddSHONG Yifan 
7*d4726bddSHONG Yifan use anyhow::Context;
8*d4726bddSHONG Yifan use serde::Deserialize;
9*d4726bddSHONG Yifan 
10*d4726bddSHONG Yifan #[derive(Debug, Deserialize)]
11*d4726bddSHONG Yifan struct AqueryOutput {
12*d4726bddSHONG Yifan     artifacts: Vec<Artifact>,
13*d4726bddSHONG Yifan     actions: Vec<Action>,
14*d4726bddSHONG Yifan     #[serde(rename = "pathFragments")]
15*d4726bddSHONG Yifan     path_fragments: Vec<PathFragment>,
16*d4726bddSHONG Yifan }
17*d4726bddSHONG Yifan 
18*d4726bddSHONG Yifan #[derive(Debug, Deserialize)]
19*d4726bddSHONG Yifan struct Artifact {
20*d4726bddSHONG Yifan     id: u32,
21*d4726bddSHONG Yifan     #[serde(rename = "pathFragmentId")]
22*d4726bddSHONG Yifan     path_fragment_id: u32,
23*d4726bddSHONG Yifan }
24*d4726bddSHONG Yifan 
25*d4726bddSHONG Yifan #[derive(Debug, Deserialize)]
26*d4726bddSHONG Yifan struct PathFragment {
27*d4726bddSHONG Yifan     id: u32,
28*d4726bddSHONG Yifan     label: String,
29*d4726bddSHONG Yifan     #[serde(rename = "parentId")]
30*d4726bddSHONG Yifan     parent_id: Option<u32>,
31*d4726bddSHONG Yifan }
32*d4726bddSHONG Yifan 
33*d4726bddSHONG Yifan #[derive(Debug, Deserialize)]
34*d4726bddSHONG Yifan struct Action {
35*d4726bddSHONG Yifan     #[serde(rename = "outputIds")]
36*d4726bddSHONG Yifan     output_ids: Vec<u32>,
37*d4726bddSHONG Yifan }
38*d4726bddSHONG Yifan 
39*d4726bddSHONG Yifan #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Deserialize)]
40*d4726bddSHONG Yifan #[serde(deny_unknown_fields)]
41*d4726bddSHONG Yifan pub struct CrateSpec {
42*d4726bddSHONG Yifan     pub aliases: BTreeMap<String, String>,
43*d4726bddSHONG Yifan     pub crate_id: String,
44*d4726bddSHONG Yifan     pub display_name: String,
45*d4726bddSHONG Yifan     pub edition: String,
46*d4726bddSHONG Yifan     pub root_module: String,
47*d4726bddSHONG Yifan     pub is_workspace_member: bool,
48*d4726bddSHONG Yifan     pub deps: BTreeSet<String>,
49*d4726bddSHONG Yifan     pub proc_macro_dylib_path: Option<String>,
50*d4726bddSHONG Yifan     pub source: Option<CrateSpecSource>,
51*d4726bddSHONG Yifan     pub cfg: Vec<String>,
52*d4726bddSHONG Yifan     pub env: BTreeMap<String, String>,
53*d4726bddSHONG Yifan     pub target: String,
54*d4726bddSHONG Yifan     pub crate_type: String,
55*d4726bddSHONG Yifan }
56*d4726bddSHONG Yifan 
57*d4726bddSHONG Yifan #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Deserialize)]
58*d4726bddSHONG Yifan #[serde(deny_unknown_fields)]
59*d4726bddSHONG Yifan pub struct CrateSpecSource {
60*d4726bddSHONG Yifan     pub exclude_dirs: Vec<String>,
61*d4726bddSHONG Yifan     pub include_dirs: Vec<String>,
62*d4726bddSHONG Yifan }
63*d4726bddSHONG Yifan 
get_crate_specs( bazel: &Path, workspace: &Path, execution_root: &Path, targets: &[String], rules_rust_name: &str, ) -> anyhow::Result<BTreeSet<CrateSpec>>64*d4726bddSHONG Yifan pub fn get_crate_specs(
65*d4726bddSHONG Yifan     bazel: &Path,
66*d4726bddSHONG Yifan     workspace: &Path,
67*d4726bddSHONG Yifan     execution_root: &Path,
68*d4726bddSHONG Yifan     targets: &[String],
69*d4726bddSHONG Yifan     rules_rust_name: &str,
70*d4726bddSHONG Yifan ) -> anyhow::Result<BTreeSet<CrateSpec>> {
71*d4726bddSHONG Yifan     log::debug!("Get crate specs with targets: {:?}", targets);
72*d4726bddSHONG Yifan     let target_pattern = targets
73*d4726bddSHONG Yifan         .iter()
74*d4726bddSHONG Yifan         .map(|t| format!("deps({t})"))
75*d4726bddSHONG Yifan         .collect::<Vec<_>>()
76*d4726bddSHONG Yifan         .join("+");
77*d4726bddSHONG Yifan 
78*d4726bddSHONG Yifan     let aquery_output = Command::new(bazel)
79*d4726bddSHONG Yifan         .current_dir(workspace)
80*d4726bddSHONG Yifan         .env_remove("BAZELISK_SKIP_WRAPPER")
81*d4726bddSHONG Yifan         .env_remove("BUILD_WORKING_DIRECTORY")
82*d4726bddSHONG Yifan         .env_remove("BUILD_WORKSPACE_DIRECTORY")
83*d4726bddSHONG Yifan         .arg("aquery")
84*d4726bddSHONG Yifan         .arg("--include_aspects")
85*d4726bddSHONG Yifan         .arg("--include_artifacts")
86*d4726bddSHONG Yifan         .arg(format!(
87*d4726bddSHONG Yifan             "--aspects={rules_rust_name}//rust:defs.bzl%rust_analyzer_aspect"
88*d4726bddSHONG Yifan         ))
89*d4726bddSHONG Yifan         .arg("--output_groups=rust_analyzer_crate_spec")
90*d4726bddSHONG Yifan         .arg(format!(
91*d4726bddSHONG Yifan             r#"outputs(".*\.rust_analyzer_crate_spec\.json",{target_pattern})"#
92*d4726bddSHONG Yifan         ))
93*d4726bddSHONG Yifan         .arg("--output=jsonproto")
94*d4726bddSHONG Yifan         .output()?;
95*d4726bddSHONG Yifan 
96*d4726bddSHONG Yifan     let crate_spec_files =
97*d4726bddSHONG Yifan         parse_aquery_output_files(execution_root, &String::from_utf8(aquery_output.stdout)?)?;
98*d4726bddSHONG Yifan 
99*d4726bddSHONG Yifan     let crate_specs = crate_spec_files
100*d4726bddSHONG Yifan         .into_iter()
101*d4726bddSHONG Yifan         .map(|file| {
102*d4726bddSHONG Yifan             let f = File::open(&file)
103*d4726bddSHONG Yifan                 .with_context(|| format!("Failed to open file: {}", file.display()))?;
104*d4726bddSHONG Yifan             serde_json::from_reader(f)
105*d4726bddSHONG Yifan                 .with_context(|| format!("Failed to deserialize file: {}", file.display()))
106*d4726bddSHONG Yifan         })
107*d4726bddSHONG Yifan         .collect::<anyhow::Result<Vec<CrateSpec>>>()?;
108*d4726bddSHONG Yifan 
109*d4726bddSHONG Yifan     consolidate_crate_specs(crate_specs)
110*d4726bddSHONG Yifan }
111*d4726bddSHONG Yifan 
parse_aquery_output_files( execution_root: &Path, aquery_stdout: &str, ) -> anyhow::Result<Vec<PathBuf>>112*d4726bddSHONG Yifan fn parse_aquery_output_files(
113*d4726bddSHONG Yifan     execution_root: &Path,
114*d4726bddSHONG Yifan     aquery_stdout: &str,
115*d4726bddSHONG Yifan ) -> anyhow::Result<Vec<PathBuf>> {
116*d4726bddSHONG Yifan     let out: AqueryOutput = serde_json::from_str(aquery_stdout).map_err(|_| {
117*d4726bddSHONG Yifan         // Parsing to `AqueryOutput` failed, try parsing into a `serde_json::Value`:
118*d4726bddSHONG Yifan         match serde_json::from_str::<serde_json::Value>(aquery_stdout) {
119*d4726bddSHONG Yifan             Ok(serde_json::Value::Object(_)) => {
120*d4726bddSHONG Yifan                 // If the JSON is an object, it's likely that the aquery command failed.
121*d4726bddSHONG Yifan                 anyhow::anyhow!("Aquery returned an empty result, are there any Rust targets in the specified paths?.")
122*d4726bddSHONG Yifan             }
123*d4726bddSHONG Yifan             _ => {
124*d4726bddSHONG Yifan                 anyhow::anyhow!("Failed to parse aquery output as JSON")
125*d4726bddSHONG Yifan             }
126*d4726bddSHONG Yifan         }
127*d4726bddSHONG Yifan     })?;
128*d4726bddSHONG Yifan 
129*d4726bddSHONG Yifan     let artifacts = out
130*d4726bddSHONG Yifan         .artifacts
131*d4726bddSHONG Yifan         .iter()
132*d4726bddSHONG Yifan         .map(|a| (a.id, a))
133*d4726bddSHONG Yifan         .collect::<BTreeMap<_, _>>();
134*d4726bddSHONG Yifan     let path_fragments = out
135*d4726bddSHONG Yifan         .path_fragments
136*d4726bddSHONG Yifan         .iter()
137*d4726bddSHONG Yifan         .map(|pf| (pf.id, pf))
138*d4726bddSHONG Yifan         .collect::<BTreeMap<_, _>>();
139*d4726bddSHONG Yifan 
140*d4726bddSHONG Yifan     let mut output_files: Vec<PathBuf> = Vec::new();
141*d4726bddSHONG Yifan     for action in out.actions {
142*d4726bddSHONG Yifan         for output_id in action.output_ids {
143*d4726bddSHONG Yifan             let artifact = artifacts
144*d4726bddSHONG Yifan                 .get(&output_id)
145*d4726bddSHONG Yifan                 .expect("internal consistency error in bazel output");
146*d4726bddSHONG Yifan             let path = path_from_fragments(artifact.path_fragment_id, &path_fragments)?;
147*d4726bddSHONG Yifan             let path = execution_root.join(path);
148*d4726bddSHONG Yifan             if path.exists() {
149*d4726bddSHONG Yifan                 output_files.push(path);
150*d4726bddSHONG Yifan             } else {
151*d4726bddSHONG Yifan                 log::warn!("Skipping missing crate_spec file: {:?}", path);
152*d4726bddSHONG Yifan             }
153*d4726bddSHONG Yifan         }
154*d4726bddSHONG Yifan     }
155*d4726bddSHONG Yifan 
156*d4726bddSHONG Yifan     Ok(output_files)
157*d4726bddSHONG Yifan }
158*d4726bddSHONG Yifan 
path_from_fragments( id: u32, fragments: &BTreeMap<u32, &PathFragment>, ) -> anyhow::Result<PathBuf>159*d4726bddSHONG Yifan fn path_from_fragments(
160*d4726bddSHONG Yifan     id: u32,
161*d4726bddSHONG Yifan     fragments: &BTreeMap<u32, &PathFragment>,
162*d4726bddSHONG Yifan ) -> anyhow::Result<PathBuf> {
163*d4726bddSHONG Yifan     let path_fragment = fragments
164*d4726bddSHONG Yifan         .get(&id)
165*d4726bddSHONG Yifan         .expect("internal consistency error in bazel output");
166*d4726bddSHONG Yifan 
167*d4726bddSHONG Yifan     let buf = match path_fragment.parent_id {
168*d4726bddSHONG Yifan         Some(parent_id) => path_from_fragments(parent_id, fragments)?
169*d4726bddSHONG Yifan             .join(PathBuf::from(&path_fragment.label.clone())),
170*d4726bddSHONG Yifan         None => PathBuf::from(&path_fragment.label.clone()),
171*d4726bddSHONG Yifan     };
172*d4726bddSHONG Yifan 
173*d4726bddSHONG Yifan     Ok(buf)
174*d4726bddSHONG Yifan }
175*d4726bddSHONG Yifan 
176*d4726bddSHONG Yifan /// Read all crate specs, deduplicating crates with the same ID. This happens when
177*d4726bddSHONG Yifan /// a rust_test depends on a rust_library, for example.
consolidate_crate_specs(crate_specs: Vec<CrateSpec>) -> anyhow::Result<BTreeSet<CrateSpec>>178*d4726bddSHONG Yifan fn consolidate_crate_specs(crate_specs: Vec<CrateSpec>) -> anyhow::Result<BTreeSet<CrateSpec>> {
179*d4726bddSHONG Yifan     let mut consolidated_specs: BTreeMap<String, CrateSpec> = BTreeMap::new();
180*d4726bddSHONG Yifan     for mut spec in crate_specs.into_iter() {
181*d4726bddSHONG Yifan         log::debug!("{:?}", spec);
182*d4726bddSHONG Yifan         if let Some(existing) = consolidated_specs.get_mut(&spec.crate_id) {
183*d4726bddSHONG Yifan             existing.deps.extend(spec.deps);
184*d4726bddSHONG Yifan 
185*d4726bddSHONG Yifan             spec.cfg.retain(|cfg| !existing.cfg.contains(cfg));
186*d4726bddSHONG Yifan             existing.cfg.extend(spec.cfg);
187*d4726bddSHONG Yifan 
188*d4726bddSHONG Yifan             // display_name should match the library's crate name because Rust Analyzer
189*d4726bddSHONG Yifan             // seems to use display_name for matching crate entries in rust-project.json
190*d4726bddSHONG Yifan             // against symbols in source files. For more details, see
191*d4726bddSHONG Yifan             // https://github.com/bazelbuild/rules_rust/issues/1032
192*d4726bddSHONG Yifan             if spec.crate_type == "rlib" {
193*d4726bddSHONG Yifan                 existing.display_name = spec.display_name;
194*d4726bddSHONG Yifan                 existing.crate_type = "rlib".into();
195*d4726bddSHONG Yifan             }
196*d4726bddSHONG Yifan 
197*d4726bddSHONG Yifan             // For proc-macro crates that exist within the workspace, there will be a
198*d4726bddSHONG Yifan             // generated crate-spec in both the fastbuild and opt-exec configuration.
199*d4726bddSHONG Yifan             // Prefer proc macro paths with an opt-exec component in the path.
200*d4726bddSHONG Yifan             if let Some(dylib_path) = spec.proc_macro_dylib_path.as_ref() {
201*d4726bddSHONG Yifan                 const OPT_PATH_COMPONENT: &str = "-opt-exec-";
202*d4726bddSHONG Yifan                 if dylib_path.contains(OPT_PATH_COMPONENT) {
203*d4726bddSHONG Yifan                     existing.proc_macro_dylib_path.replace(dylib_path.clone());
204*d4726bddSHONG Yifan                 }
205*d4726bddSHONG Yifan             }
206*d4726bddSHONG Yifan         } else {
207*d4726bddSHONG Yifan             consolidated_specs.insert(spec.crate_id.clone(), spec);
208*d4726bddSHONG Yifan         }
209*d4726bddSHONG Yifan     }
210*d4726bddSHONG Yifan 
211*d4726bddSHONG Yifan     Ok(consolidated_specs.into_values().collect())
212*d4726bddSHONG Yifan }
213*d4726bddSHONG Yifan 
214*d4726bddSHONG Yifan #[cfg(test)]
215*d4726bddSHONG Yifan mod test {
216*d4726bddSHONG Yifan     use super::*;
217*d4726bddSHONG Yifan     use itertools::Itertools;
218*d4726bddSHONG Yifan 
219*d4726bddSHONG Yifan     #[test]
consolidate_lib_then_test_specs()220*d4726bddSHONG Yifan     fn consolidate_lib_then_test_specs() {
221*d4726bddSHONG Yifan         let crate_specs = vec![
222*d4726bddSHONG Yifan             CrateSpec {
223*d4726bddSHONG Yifan                 aliases: BTreeMap::new(),
224*d4726bddSHONG Yifan                 crate_id: "ID-mylib.rs".into(),
225*d4726bddSHONG Yifan                 display_name: "mylib".into(),
226*d4726bddSHONG Yifan                 edition: "2018".into(),
227*d4726bddSHONG Yifan                 root_module: "mylib.rs".into(),
228*d4726bddSHONG Yifan                 is_workspace_member: true,
229*d4726bddSHONG Yifan                 deps: BTreeSet::from(["ID-lib_dep.rs".into()]),
230*d4726bddSHONG Yifan                 proc_macro_dylib_path: None,
231*d4726bddSHONG Yifan                 source: None,
232*d4726bddSHONG Yifan                 cfg: vec!["test".into(), "debug_assertions".into()],
233*d4726bddSHONG Yifan                 env: BTreeMap::new(),
234*d4726bddSHONG Yifan                 target: "x86_64-unknown-linux-gnu".into(),
235*d4726bddSHONG Yifan                 crate_type: "rlib".into(),
236*d4726bddSHONG Yifan             },
237*d4726bddSHONG Yifan             CrateSpec {
238*d4726bddSHONG Yifan                 aliases: BTreeMap::new(),
239*d4726bddSHONG Yifan                 crate_id: "ID-extra_test_dep.rs".into(),
240*d4726bddSHONG Yifan                 display_name: "extra_test_dep".into(),
241*d4726bddSHONG Yifan                 edition: "2018".into(),
242*d4726bddSHONG Yifan                 root_module: "extra_test_dep.rs".into(),
243*d4726bddSHONG Yifan                 is_workspace_member: true,
244*d4726bddSHONG Yifan                 deps: BTreeSet::new(),
245*d4726bddSHONG Yifan                 proc_macro_dylib_path: None,
246*d4726bddSHONG Yifan                 source: None,
247*d4726bddSHONG Yifan                 cfg: vec!["test".into(), "debug_assertions".into()],
248*d4726bddSHONG Yifan                 env: BTreeMap::new(),
249*d4726bddSHONG Yifan                 target: "x86_64-unknown-linux-gnu".into(),
250*d4726bddSHONG Yifan                 crate_type: "rlib".into(),
251*d4726bddSHONG Yifan             },
252*d4726bddSHONG Yifan             CrateSpec {
253*d4726bddSHONG Yifan                 aliases: BTreeMap::new(),
254*d4726bddSHONG Yifan                 crate_id: "ID-lib_dep.rs".into(),
255*d4726bddSHONG Yifan                 display_name: "lib_dep".into(),
256*d4726bddSHONG Yifan                 edition: "2018".into(),
257*d4726bddSHONG Yifan                 root_module: "lib_dep.rs".into(),
258*d4726bddSHONG Yifan                 is_workspace_member: true,
259*d4726bddSHONG Yifan                 deps: BTreeSet::new(),
260*d4726bddSHONG Yifan                 proc_macro_dylib_path: None,
261*d4726bddSHONG Yifan                 source: None,
262*d4726bddSHONG Yifan                 cfg: vec!["test".into(), "debug_assertions".into()],
263*d4726bddSHONG Yifan                 env: BTreeMap::new(),
264*d4726bddSHONG Yifan                 target: "x86_64-unknown-linux-gnu".into(),
265*d4726bddSHONG Yifan                 crate_type: "rlib".into(),
266*d4726bddSHONG Yifan             },
267*d4726bddSHONG Yifan             CrateSpec {
268*d4726bddSHONG Yifan                 aliases: BTreeMap::new(),
269*d4726bddSHONG Yifan                 crate_id: "ID-mylib.rs".into(),
270*d4726bddSHONG Yifan                 display_name: "mylib_test".into(),
271*d4726bddSHONG Yifan                 edition: "2018".into(),
272*d4726bddSHONG Yifan                 root_module: "mylib.rs".into(),
273*d4726bddSHONG Yifan                 is_workspace_member: true,
274*d4726bddSHONG Yifan                 deps: BTreeSet::from(["ID-extra_test_dep.rs".into()]),
275*d4726bddSHONG Yifan                 proc_macro_dylib_path: None,
276*d4726bddSHONG Yifan                 source: None,
277*d4726bddSHONG Yifan                 cfg: vec!["test".into(), "debug_assertions".into()],
278*d4726bddSHONG Yifan                 env: BTreeMap::new(),
279*d4726bddSHONG Yifan                 target: "x86_64-unknown-linux-gnu".into(),
280*d4726bddSHONG Yifan                 crate_type: "bin".into(),
281*d4726bddSHONG Yifan             },
282*d4726bddSHONG Yifan         ];
283*d4726bddSHONG Yifan 
284*d4726bddSHONG Yifan         assert_eq!(
285*d4726bddSHONG Yifan             consolidate_crate_specs(crate_specs).unwrap(),
286*d4726bddSHONG Yifan             BTreeSet::from([
287*d4726bddSHONG Yifan                 CrateSpec {
288*d4726bddSHONG Yifan                     aliases: BTreeMap::new(),
289*d4726bddSHONG Yifan                     crate_id: "ID-mylib.rs".into(),
290*d4726bddSHONG Yifan                     display_name: "mylib".into(),
291*d4726bddSHONG Yifan                     edition: "2018".into(),
292*d4726bddSHONG Yifan                     root_module: "mylib.rs".into(),
293*d4726bddSHONG Yifan                     is_workspace_member: true,
294*d4726bddSHONG Yifan                     deps: BTreeSet::from(["ID-lib_dep.rs".into(), "ID-extra_test_dep.rs".into()]),
295*d4726bddSHONG Yifan                     proc_macro_dylib_path: None,
296*d4726bddSHONG Yifan                     source: None,
297*d4726bddSHONG Yifan                     cfg: vec!["test".into(), "debug_assertions".into()],
298*d4726bddSHONG Yifan                     env: BTreeMap::new(),
299*d4726bddSHONG Yifan                     target: "x86_64-unknown-linux-gnu".into(),
300*d4726bddSHONG Yifan                     crate_type: "rlib".into(),
301*d4726bddSHONG Yifan                 },
302*d4726bddSHONG Yifan                 CrateSpec {
303*d4726bddSHONG Yifan                     aliases: BTreeMap::new(),
304*d4726bddSHONG Yifan                     crate_id: "ID-extra_test_dep.rs".into(),
305*d4726bddSHONG Yifan                     display_name: "extra_test_dep".into(),
306*d4726bddSHONG Yifan                     edition: "2018".into(),
307*d4726bddSHONG Yifan                     root_module: "extra_test_dep.rs".into(),
308*d4726bddSHONG Yifan                     is_workspace_member: true,
309*d4726bddSHONG Yifan                     deps: BTreeSet::new(),
310*d4726bddSHONG Yifan                     proc_macro_dylib_path: None,
311*d4726bddSHONG Yifan                     source: None,
312*d4726bddSHONG Yifan                     cfg: vec!["test".into(), "debug_assertions".into()],
313*d4726bddSHONG Yifan                     env: BTreeMap::new(),
314*d4726bddSHONG Yifan                     target: "x86_64-unknown-linux-gnu".into(),
315*d4726bddSHONG Yifan                     crate_type: "rlib".into(),
316*d4726bddSHONG Yifan                 },
317*d4726bddSHONG Yifan                 CrateSpec {
318*d4726bddSHONG Yifan                     aliases: BTreeMap::new(),
319*d4726bddSHONG Yifan                     crate_id: "ID-lib_dep.rs".into(),
320*d4726bddSHONG Yifan                     display_name: "lib_dep".into(),
321*d4726bddSHONG Yifan                     edition: "2018".into(),
322*d4726bddSHONG Yifan                     root_module: "lib_dep.rs".into(),
323*d4726bddSHONG Yifan                     is_workspace_member: true,
324*d4726bddSHONG Yifan                     deps: BTreeSet::new(),
325*d4726bddSHONG Yifan                     proc_macro_dylib_path: None,
326*d4726bddSHONG Yifan                     source: None,
327*d4726bddSHONG Yifan                     cfg: vec!["test".into(), "debug_assertions".into()],
328*d4726bddSHONG Yifan                     env: BTreeMap::new(),
329*d4726bddSHONG Yifan                     target: "x86_64-unknown-linux-gnu".into(),
330*d4726bddSHONG Yifan                     crate_type: "rlib".into(),
331*d4726bddSHONG Yifan                 },
332*d4726bddSHONG Yifan             ])
333*d4726bddSHONG Yifan         );
334*d4726bddSHONG Yifan     }
335*d4726bddSHONG Yifan 
336*d4726bddSHONG Yifan     #[test]
consolidate_test_then_lib_specs()337*d4726bddSHONG Yifan     fn consolidate_test_then_lib_specs() {
338*d4726bddSHONG Yifan         let crate_specs = vec![
339*d4726bddSHONG Yifan             CrateSpec {
340*d4726bddSHONG Yifan                 aliases: BTreeMap::new(),
341*d4726bddSHONG Yifan                 crate_id: "ID-mylib.rs".into(),
342*d4726bddSHONG Yifan                 display_name: "mylib_test".into(),
343*d4726bddSHONG Yifan                 edition: "2018".into(),
344*d4726bddSHONG Yifan                 root_module: "mylib.rs".into(),
345*d4726bddSHONG Yifan                 is_workspace_member: true,
346*d4726bddSHONG Yifan                 deps: BTreeSet::from(["ID-extra_test_dep.rs".into()]),
347*d4726bddSHONG Yifan                 proc_macro_dylib_path: None,
348*d4726bddSHONG Yifan                 source: None,
349*d4726bddSHONG Yifan                 cfg: vec!["test".into(), "debug_assertions".into()],
350*d4726bddSHONG Yifan                 env: BTreeMap::new(),
351*d4726bddSHONG Yifan                 target: "x86_64-unknown-linux-gnu".into(),
352*d4726bddSHONG Yifan                 crate_type: "bin".into(),
353*d4726bddSHONG Yifan             },
354*d4726bddSHONG Yifan             CrateSpec {
355*d4726bddSHONG Yifan                 aliases: BTreeMap::new(),
356*d4726bddSHONG Yifan                 crate_id: "ID-mylib.rs".into(),
357*d4726bddSHONG Yifan                 display_name: "mylib".into(),
358*d4726bddSHONG Yifan                 edition: "2018".into(),
359*d4726bddSHONG Yifan                 root_module: "mylib.rs".into(),
360*d4726bddSHONG Yifan                 is_workspace_member: true,
361*d4726bddSHONG Yifan                 deps: BTreeSet::from(["ID-lib_dep.rs".into()]),
362*d4726bddSHONG Yifan                 proc_macro_dylib_path: None,
363*d4726bddSHONG Yifan                 source: None,
364*d4726bddSHONG Yifan                 cfg: vec!["test".into(), "debug_assertions".into()],
365*d4726bddSHONG Yifan                 env: BTreeMap::new(),
366*d4726bddSHONG Yifan                 target: "x86_64-unknown-linux-gnu".into(),
367*d4726bddSHONG Yifan                 crate_type: "rlib".into(),
368*d4726bddSHONG Yifan             },
369*d4726bddSHONG Yifan             CrateSpec {
370*d4726bddSHONG Yifan                 aliases: BTreeMap::new(),
371*d4726bddSHONG Yifan                 crate_id: "ID-extra_test_dep.rs".into(),
372*d4726bddSHONG Yifan                 display_name: "extra_test_dep".into(),
373*d4726bddSHONG Yifan                 edition: "2018".into(),
374*d4726bddSHONG Yifan                 root_module: "extra_test_dep.rs".into(),
375*d4726bddSHONG Yifan                 is_workspace_member: true,
376*d4726bddSHONG Yifan                 deps: BTreeSet::new(),
377*d4726bddSHONG Yifan                 proc_macro_dylib_path: None,
378*d4726bddSHONG Yifan                 source: None,
379*d4726bddSHONG Yifan                 cfg: vec!["test".into(), "debug_assertions".into()],
380*d4726bddSHONG Yifan                 env: BTreeMap::new(),
381*d4726bddSHONG Yifan                 target: "x86_64-unknown-linux-gnu".into(),
382*d4726bddSHONG Yifan                 crate_type: "rlib".into(),
383*d4726bddSHONG Yifan             },
384*d4726bddSHONG Yifan             CrateSpec {
385*d4726bddSHONG Yifan                 aliases: BTreeMap::new(),
386*d4726bddSHONG Yifan                 crate_id: "ID-lib_dep.rs".into(),
387*d4726bddSHONG Yifan                 display_name: "lib_dep".into(),
388*d4726bddSHONG Yifan                 edition: "2018".into(),
389*d4726bddSHONG Yifan                 root_module: "lib_dep.rs".into(),
390*d4726bddSHONG Yifan                 is_workspace_member: true,
391*d4726bddSHONG Yifan                 deps: BTreeSet::new(),
392*d4726bddSHONG Yifan                 proc_macro_dylib_path: None,
393*d4726bddSHONG Yifan                 source: None,
394*d4726bddSHONG Yifan                 cfg: vec!["test".into(), "debug_assertions".into()],
395*d4726bddSHONG Yifan                 env: BTreeMap::new(),
396*d4726bddSHONG Yifan                 target: "x86_64-unknown-linux-gnu".into(),
397*d4726bddSHONG Yifan                 crate_type: "rlib".into(),
398*d4726bddSHONG Yifan             },
399*d4726bddSHONG Yifan         ];
400*d4726bddSHONG Yifan 
401*d4726bddSHONG Yifan         assert_eq!(
402*d4726bddSHONG Yifan             consolidate_crate_specs(crate_specs).unwrap(),
403*d4726bddSHONG Yifan             BTreeSet::from([
404*d4726bddSHONG Yifan                 CrateSpec {
405*d4726bddSHONG Yifan                     aliases: BTreeMap::new(),
406*d4726bddSHONG Yifan                     crate_id: "ID-mylib.rs".into(),
407*d4726bddSHONG Yifan                     display_name: "mylib".into(),
408*d4726bddSHONG Yifan                     edition: "2018".into(),
409*d4726bddSHONG Yifan                     root_module: "mylib.rs".into(),
410*d4726bddSHONG Yifan                     is_workspace_member: true,
411*d4726bddSHONG Yifan                     deps: BTreeSet::from(["ID-lib_dep.rs".into(), "ID-extra_test_dep.rs".into()]),
412*d4726bddSHONG Yifan                     proc_macro_dylib_path: None,
413*d4726bddSHONG Yifan                     source: None,
414*d4726bddSHONG Yifan                     cfg: vec!["test".into(), "debug_assertions".into()],
415*d4726bddSHONG Yifan                     env: BTreeMap::new(),
416*d4726bddSHONG Yifan                     target: "x86_64-unknown-linux-gnu".into(),
417*d4726bddSHONG Yifan                     crate_type: "rlib".into(),
418*d4726bddSHONG Yifan                 },
419*d4726bddSHONG Yifan                 CrateSpec {
420*d4726bddSHONG Yifan                     aliases: BTreeMap::new(),
421*d4726bddSHONG Yifan                     crate_id: "ID-extra_test_dep.rs".into(),
422*d4726bddSHONG Yifan                     display_name: "extra_test_dep".into(),
423*d4726bddSHONG Yifan                     edition: "2018".into(),
424*d4726bddSHONG Yifan                     root_module: "extra_test_dep.rs".into(),
425*d4726bddSHONG Yifan                     is_workspace_member: true,
426*d4726bddSHONG Yifan                     deps: BTreeSet::new(),
427*d4726bddSHONG Yifan                     proc_macro_dylib_path: None,
428*d4726bddSHONG Yifan                     source: None,
429*d4726bddSHONG Yifan                     cfg: vec!["test".into(), "debug_assertions".into()],
430*d4726bddSHONG Yifan                     env: BTreeMap::new(),
431*d4726bddSHONG Yifan                     target: "x86_64-unknown-linux-gnu".into(),
432*d4726bddSHONG Yifan                     crate_type: "rlib".into(),
433*d4726bddSHONG Yifan                 },
434*d4726bddSHONG Yifan                 CrateSpec {
435*d4726bddSHONG Yifan                     aliases: BTreeMap::new(),
436*d4726bddSHONG Yifan                     crate_id: "ID-lib_dep.rs".into(),
437*d4726bddSHONG Yifan                     display_name: "lib_dep".into(),
438*d4726bddSHONG Yifan                     edition: "2018".into(),
439*d4726bddSHONG Yifan                     root_module: "lib_dep.rs".into(),
440*d4726bddSHONG Yifan                     is_workspace_member: true,
441*d4726bddSHONG Yifan                     deps: BTreeSet::new(),
442*d4726bddSHONG Yifan                     proc_macro_dylib_path: None,
443*d4726bddSHONG Yifan                     source: None,
444*d4726bddSHONG Yifan                     cfg: vec!["test".into(), "debug_assertions".into()],
445*d4726bddSHONG Yifan                     env: BTreeMap::new(),
446*d4726bddSHONG Yifan                     target: "x86_64-unknown-linux-gnu".into(),
447*d4726bddSHONG Yifan                     crate_type: "rlib".into(),
448*d4726bddSHONG Yifan                 },
449*d4726bddSHONG Yifan             ])
450*d4726bddSHONG Yifan         );
451*d4726bddSHONG Yifan     }
452*d4726bddSHONG Yifan 
453*d4726bddSHONG Yifan     #[test]
consolidate_lib_test_main_specs()454*d4726bddSHONG Yifan     fn consolidate_lib_test_main_specs() {
455*d4726bddSHONG Yifan         // mylib.rs is a library but has tests and an entry point, and mylib2.rs
456*d4726bddSHONG Yifan         // depends on mylib.rs. The display_name of the library target mylib.rs
457*d4726bddSHONG Yifan         // should be "mylib" no matter what order the crate specs is in.
458*d4726bddSHONG Yifan         // Otherwise Rust Analyzer will not be able to resolve references to
459*d4726bddSHONG Yifan         // mylib in mylib2.rs.
460*d4726bddSHONG Yifan         let crate_specs = vec![
461*d4726bddSHONG Yifan             CrateSpec {
462*d4726bddSHONG Yifan                 aliases: BTreeMap::new(),
463*d4726bddSHONG Yifan                 crate_id: "ID-mylib.rs".into(),
464*d4726bddSHONG Yifan                 display_name: "mylib".into(),
465*d4726bddSHONG Yifan                 edition: "2018".into(),
466*d4726bddSHONG Yifan                 root_module: "mylib.rs".into(),
467*d4726bddSHONG Yifan                 is_workspace_member: true,
468*d4726bddSHONG Yifan                 deps: BTreeSet::new(),
469*d4726bddSHONG Yifan                 proc_macro_dylib_path: None,
470*d4726bddSHONG Yifan                 source: None,
471*d4726bddSHONG Yifan                 cfg: vec!["test".into(), "debug_assertions".into()],
472*d4726bddSHONG Yifan                 env: BTreeMap::new(),
473*d4726bddSHONG Yifan                 target: "x86_64-unknown-linux-gnu".into(),
474*d4726bddSHONG Yifan                 crate_type: "rlib".into(),
475*d4726bddSHONG Yifan             },
476*d4726bddSHONG Yifan             CrateSpec {
477*d4726bddSHONG Yifan                 aliases: BTreeMap::new(),
478*d4726bddSHONG Yifan                 crate_id: "ID-mylib.rs".into(),
479*d4726bddSHONG Yifan                 display_name: "mylib_test".into(),
480*d4726bddSHONG Yifan                 edition: "2018".into(),
481*d4726bddSHONG Yifan                 root_module: "mylib.rs".into(),
482*d4726bddSHONG Yifan                 is_workspace_member: true,
483*d4726bddSHONG Yifan                 deps: BTreeSet::new(),
484*d4726bddSHONG Yifan                 proc_macro_dylib_path: None,
485*d4726bddSHONG Yifan                 source: None,
486*d4726bddSHONG Yifan                 cfg: vec!["test".into(), "debug_assertions".into()],
487*d4726bddSHONG Yifan                 env: BTreeMap::new(),
488*d4726bddSHONG Yifan                 target: "x86_64-unknown-linux-gnu".into(),
489*d4726bddSHONG Yifan                 crate_type: "bin".into(),
490*d4726bddSHONG Yifan             },
491*d4726bddSHONG Yifan             CrateSpec {
492*d4726bddSHONG Yifan                 aliases: BTreeMap::new(),
493*d4726bddSHONG Yifan                 crate_id: "ID-mylib.rs".into(),
494*d4726bddSHONG Yifan                 display_name: "mylib_main".into(),
495*d4726bddSHONG Yifan                 edition: "2018".into(),
496*d4726bddSHONG Yifan                 root_module: "mylib.rs".into(),
497*d4726bddSHONG Yifan                 is_workspace_member: true,
498*d4726bddSHONG Yifan                 deps: BTreeSet::new(),
499*d4726bddSHONG Yifan                 proc_macro_dylib_path: None,
500*d4726bddSHONG Yifan                 source: None,
501*d4726bddSHONG Yifan                 cfg: vec!["test".into(), "debug_assertions".into()],
502*d4726bddSHONG Yifan                 env: BTreeMap::new(),
503*d4726bddSHONG Yifan                 target: "x86_64-unknown-linux-gnu".into(),
504*d4726bddSHONG Yifan                 crate_type: "bin".into(),
505*d4726bddSHONG Yifan             },
506*d4726bddSHONG Yifan             CrateSpec {
507*d4726bddSHONG Yifan                 aliases: BTreeMap::new(),
508*d4726bddSHONG Yifan                 crate_id: "ID-mylib2.rs".into(),
509*d4726bddSHONG Yifan                 display_name: "mylib2".into(),
510*d4726bddSHONG Yifan                 edition: "2018".into(),
511*d4726bddSHONG Yifan                 root_module: "mylib2.rs".into(),
512*d4726bddSHONG Yifan                 is_workspace_member: true,
513*d4726bddSHONG Yifan                 deps: BTreeSet::from(["ID-mylib.rs".into()]),
514*d4726bddSHONG Yifan                 proc_macro_dylib_path: None,
515*d4726bddSHONG Yifan                 source: None,
516*d4726bddSHONG Yifan                 cfg: vec!["test".into(), "debug_assertions".into()],
517*d4726bddSHONG Yifan                 env: BTreeMap::new(),
518*d4726bddSHONG Yifan                 target: "x86_64-unknown-linux-gnu".into(),
519*d4726bddSHONG Yifan                 crate_type: "rlib".into(),
520*d4726bddSHONG Yifan             },
521*d4726bddSHONG Yifan         ];
522*d4726bddSHONG Yifan 
523*d4726bddSHONG Yifan         for perm in crate_specs.into_iter().permutations(4) {
524*d4726bddSHONG Yifan             assert_eq!(
525*d4726bddSHONG Yifan                 consolidate_crate_specs(perm).unwrap(),
526*d4726bddSHONG Yifan                 BTreeSet::from([
527*d4726bddSHONG Yifan                     CrateSpec {
528*d4726bddSHONG Yifan                         aliases: BTreeMap::new(),
529*d4726bddSHONG Yifan                         crate_id: "ID-mylib.rs".into(),
530*d4726bddSHONG Yifan                         display_name: "mylib".into(),
531*d4726bddSHONG Yifan                         edition: "2018".into(),
532*d4726bddSHONG Yifan                         root_module: "mylib.rs".into(),
533*d4726bddSHONG Yifan                         is_workspace_member: true,
534*d4726bddSHONG Yifan                         deps: BTreeSet::from([]),
535*d4726bddSHONG Yifan                         proc_macro_dylib_path: None,
536*d4726bddSHONG Yifan                         source: None,
537*d4726bddSHONG Yifan                         cfg: vec!["test".into(), "debug_assertions".into()],
538*d4726bddSHONG Yifan                         env: BTreeMap::new(),
539*d4726bddSHONG Yifan                         target: "x86_64-unknown-linux-gnu".into(),
540*d4726bddSHONG Yifan                         crate_type: "rlib".into(),
541*d4726bddSHONG Yifan                     },
542*d4726bddSHONG Yifan                     CrateSpec {
543*d4726bddSHONG Yifan                         aliases: BTreeMap::new(),
544*d4726bddSHONG Yifan                         crate_id: "ID-mylib2.rs".into(),
545*d4726bddSHONG Yifan                         display_name: "mylib2".into(),
546*d4726bddSHONG Yifan                         edition: "2018".into(),
547*d4726bddSHONG Yifan                         root_module: "mylib2.rs".into(),
548*d4726bddSHONG Yifan                         is_workspace_member: true,
549*d4726bddSHONG Yifan                         deps: BTreeSet::from(["ID-mylib.rs".into()]),
550*d4726bddSHONG Yifan                         proc_macro_dylib_path: None,
551*d4726bddSHONG Yifan                         source: None,
552*d4726bddSHONG Yifan                         cfg: vec!["test".into(), "debug_assertions".into()],
553*d4726bddSHONG Yifan                         env: BTreeMap::new(),
554*d4726bddSHONG Yifan                         target: "x86_64-unknown-linux-gnu".into(),
555*d4726bddSHONG Yifan                         crate_type: "rlib".into(),
556*d4726bddSHONG Yifan                     },
557*d4726bddSHONG Yifan                 ])
558*d4726bddSHONG Yifan             );
559*d4726bddSHONG Yifan         }
560*d4726bddSHONG Yifan     }
561*d4726bddSHONG Yifan 
562*d4726bddSHONG Yifan     #[test]
consolidate_proc_macro_prefer_exec()563*d4726bddSHONG Yifan     fn consolidate_proc_macro_prefer_exec() {
564*d4726bddSHONG Yifan         // proc macro crates should prefer the -opt-exec- path which is always generated
565*d4726bddSHONG Yifan         // during builds where it is used, while the fastbuild version would only be built
566*d4726bddSHONG Yifan         // when explicitly building that target.
567*d4726bddSHONG Yifan         let crate_specs = vec![
568*d4726bddSHONG Yifan             CrateSpec {
569*d4726bddSHONG Yifan                 aliases: BTreeMap::new(),
570*d4726bddSHONG Yifan                 crate_id: "ID-myproc_macro.rs".into(),
571*d4726bddSHONG Yifan                 display_name: "myproc_macro".into(),
572*d4726bddSHONG Yifan                 edition: "2018".into(),
573*d4726bddSHONG Yifan                 root_module: "myproc_macro.rs".into(),
574*d4726bddSHONG Yifan                 is_workspace_member: true,
575*d4726bddSHONG Yifan                 deps: BTreeSet::new(),
576*d4726bddSHONG Yifan                 proc_macro_dylib_path: Some(
577*d4726bddSHONG Yifan                     "bazel-out/k8-opt-exec-F005BA11/bin/myproc_macro/libmyproc_macro-12345.so"
578*d4726bddSHONG Yifan                         .into(),
579*d4726bddSHONG Yifan                 ),
580*d4726bddSHONG Yifan                 source: None,
581*d4726bddSHONG Yifan                 cfg: vec!["test".into(), "debug_assertions".into()],
582*d4726bddSHONG Yifan                 env: BTreeMap::new(),
583*d4726bddSHONG Yifan                 target: "x86_64-unknown-linux-gnu".into(),
584*d4726bddSHONG Yifan                 crate_type: "proc_macro".into(),
585*d4726bddSHONG Yifan             },
586*d4726bddSHONG Yifan             CrateSpec {
587*d4726bddSHONG Yifan                 aliases: BTreeMap::new(),
588*d4726bddSHONG Yifan                 crate_id: "ID-myproc_macro.rs".into(),
589*d4726bddSHONG Yifan                 display_name: "myproc_macro".into(),
590*d4726bddSHONG Yifan                 edition: "2018".into(),
591*d4726bddSHONG Yifan                 root_module: "myproc_macro.rs".into(),
592*d4726bddSHONG Yifan                 is_workspace_member: true,
593*d4726bddSHONG Yifan                 deps: BTreeSet::new(),
594*d4726bddSHONG Yifan                 proc_macro_dylib_path: Some(
595*d4726bddSHONG Yifan                     "bazel-out/k8-fastbuild/bin/myproc_macro/libmyproc_macro-12345.so".into(),
596*d4726bddSHONG Yifan                 ),
597*d4726bddSHONG Yifan                 source: None,
598*d4726bddSHONG Yifan                 cfg: vec!["test".into(), "debug_assertions".into()],
599*d4726bddSHONG Yifan                 env: BTreeMap::new(),
600*d4726bddSHONG Yifan                 target: "x86_64-unknown-linux-gnu".into(),
601*d4726bddSHONG Yifan                 crate_type: "proc_macro".into(),
602*d4726bddSHONG Yifan             },
603*d4726bddSHONG Yifan         ];
604*d4726bddSHONG Yifan 
605*d4726bddSHONG Yifan         for perm in crate_specs.into_iter().permutations(2) {
606*d4726bddSHONG Yifan             assert_eq!(
607*d4726bddSHONG Yifan                 consolidate_crate_specs(perm).unwrap(),
608*d4726bddSHONG Yifan                 BTreeSet::from([CrateSpec {
609*d4726bddSHONG Yifan                     aliases: BTreeMap::new(),
610*d4726bddSHONG Yifan                     crate_id: "ID-myproc_macro.rs".into(),
611*d4726bddSHONG Yifan                     display_name: "myproc_macro".into(),
612*d4726bddSHONG Yifan                     edition: "2018".into(),
613*d4726bddSHONG Yifan                     root_module: "myproc_macro.rs".into(),
614*d4726bddSHONG Yifan                     is_workspace_member: true,
615*d4726bddSHONG Yifan                     deps: BTreeSet::new(),
616*d4726bddSHONG Yifan                     proc_macro_dylib_path: Some(
617*d4726bddSHONG Yifan                         "bazel-out/k8-opt-exec-F005BA11/bin/myproc_macro/libmyproc_macro-12345.so"
618*d4726bddSHONG Yifan                             .into()
619*d4726bddSHONG Yifan                     ),
620*d4726bddSHONG Yifan                     source: None,
621*d4726bddSHONG Yifan                     cfg: vec!["test".into(), "debug_assertions".into()],
622*d4726bddSHONG Yifan                     env: BTreeMap::new(),
623*d4726bddSHONG Yifan                     target: "x86_64-unknown-linux-gnu".into(),
624*d4726bddSHONG Yifan                     crate_type: "proc_macro".into(),
625*d4726bddSHONG Yifan                 },])
626*d4726bddSHONG Yifan             );
627*d4726bddSHONG Yifan         }
628*d4726bddSHONG Yifan     }
629*d4726bddSHONG Yifan }
630