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