blob: fb7b69e02767eeaa0a6a101990ba1606a54b2661 [file] [log] [blame]
Brian Silvermancc09f182022-03-09 15:40:20 -08001use std::collections::HashMap;
2use std::env;
3use std::path::PathBuf;
4use std::process::Command;
5
6use anyhow::anyhow;
Brian Silverman5f6f2762022-08-13 19:30:05 -07007use clap::Parser;
Brian Silvermancc09f182022-03-09 15:40:20 -08008use gen_rust_project_lib::generate_crate_info;
9use gen_rust_project_lib::write_rust_project;
Brian Silvermancc09f182022-03-09 15:40:20 -080010
11// TODO(david): This shells out to an expected rule in the workspace root //:rust_analyzer that the user must define.
12// It would be more convenient if it could automatically discover all the rust code in the workspace if this target
13// does not exist.
14fn main() -> anyhow::Result<()> {
15 env_logger::init();
16
17 let config = parse_config()?;
18
19 let workspace_root = config
20 .workspace
21 .as_ref()
22 .expect("failed to find workspace root, set with --workspace");
23
24 let execution_root = config
25 .execution_root
26 .as_ref()
27 .expect("failed to find execution root, is --execution-root set correctly?");
28
Brian Silverman5f6f2762022-08-13 19:30:05 -070029 let output_base = config
30 .output_base
31 .as_ref()
32 .expect("failed to find output base, is -output-base set correctly?");
33
Brian Silvermancc09f182022-03-09 15:40:20 -080034 let rules_rust_name = env!("ASPECT_REPOSITORY");
35
36 // Generate the crate specs.
37 generate_crate_info(
38 &config.bazel,
Adam Snaider1c095c92023-07-08 02:09:58 -040039 workspace_root,
40 rules_rust_name,
Brian Silvermancc09f182022-03-09 15:40:20 -080041 &config.targets,
42 )?;
43
44 // Use the generated files to write rust-project.json.
45 write_rust_project(
46 &config.bazel,
Adam Snaider1c095c92023-07-08 02:09:58 -040047 workspace_root,
Brian Silvermancc09f182022-03-09 15:40:20 -080048 &rules_rust_name,
49 &config.targets,
Adam Snaider1c095c92023-07-08 02:09:58 -040050 execution_root,
51 output_base,
52 workspace_root.join("rust-project.json"),
Brian Silvermancc09f182022-03-09 15:40:20 -080053 )?;
54
55 Ok(())
56}
57
58// Parse the configuration flags and supplement with bazel info as needed.
59fn parse_config() -> anyhow::Result<Config> {
Brian Silverman5f6f2762022-08-13 19:30:05 -070060 let mut config = Config::parse();
Brian Silvermancc09f182022-03-09 15:40:20 -080061
62 if config.workspace.is_some() && config.execution_root.is_some() {
63 return Ok(config);
64 }
65
66 // We need some info from `bazel info`. Fetch it now.
67 let mut bazel_info_command = Command::new(&config.bazel);
68 bazel_info_command.arg("info");
69 if let Some(workspace) = &config.workspace {
70 bazel_info_command.current_dir(workspace);
71 }
72
73 // Execute bazel info.
74 let output = bazel_info_command.output()?;
75 if !output.status.success() {
76 return Err(anyhow!(
77 "Failed to run `bazel info` ({:?}): {}",
78 output.status,
79 String::from_utf8_lossy(&output.stderr)
80 ));
81 }
82
83 // Extract the output.
84 let output = String::from_utf8_lossy(output.stdout.as_slice());
85 let bazel_info = output
86 .trim()
87 .split('\n')
88 .map(|line| line.split_at(line.find(':').expect("missing `:` in bazel info output")))
Adam Snaider1c095c92023-07-08 02:09:58 -040089 .map(|(k, v)| (k, (v[1..]).trim()))
Brian Silvermancc09f182022-03-09 15:40:20 -080090 .collect::<HashMap<_, _>>();
91
92 if config.workspace.is_none() {
93 config.workspace = bazel_info.get("workspace").map(Into::into);
94 }
95 if config.execution_root.is_none() {
96 config.execution_root = bazel_info.get("execution_root").map(Into::into);
97 }
Brian Silverman5f6f2762022-08-13 19:30:05 -070098 if config.output_base.is_none() {
99 config.output_base = bazel_info.get("output_base").map(Into::into);
100 }
Brian Silvermancc09f182022-03-09 15:40:20 -0800101
102 Ok(config)
103}
104
Brian Silverman5f6f2762022-08-13 19:30:05 -0700105#[derive(Debug, Parser)]
Brian Silvermancc09f182022-03-09 15:40:20 -0800106struct Config {
Brian Silverman5f6f2762022-08-13 19:30:05 -0700107 /// The path to the Bazel workspace directory. If not specified, uses the result of `bazel info workspace`.
108 #[clap(long, env = "BUILD_WORKSPACE_DIRECTORY")]
Brian Silvermancc09f182022-03-09 15:40:20 -0800109 workspace: Option<PathBuf>,
110
Brian Silverman5f6f2762022-08-13 19:30:05 -0700111 /// The path to the Bazel execution root. If not specified, uses the result of `bazel info execution_root`.
112 #[clap(long)]
Brian Silvermancc09f182022-03-09 15:40:20 -0800113 execution_root: Option<PathBuf>,
114
Brian Silverman5f6f2762022-08-13 19:30:05 -0700115 /// The path to the Bazel output user root. If not specified, uses the result of `bazel info output_base`.
116 #[clap(long, env = "OUTPUT_BASE")]
117 output_base: Option<PathBuf>,
118
119 /// The path to a Bazel binary
120 #[clap(long, default_value = "bazel")]
Brian Silvermancc09f182022-03-09 15:40:20 -0800121 bazel: PathBuf,
122
Brian Silverman5f6f2762022-08-13 19:30:05 -0700123 /// Space separated list of target patterns that comes after all other args.
124 #[clap(default_value = "@//...")]
Brian Silvermancc09f182022-03-09 15:40:20 -0800125 targets: Vec<String>,
126}