Brian Silverman | cc09f18 | 2022-03-09 15:40:20 -0800 | [diff] [blame] | 1 | use std::collections::HashMap; |
| 2 | use std::env; |
| 3 | use std::path::PathBuf; |
| 4 | use std::process::Command; |
| 5 | |
| 6 | use anyhow::anyhow; |
Brian Silverman | 5f6f276 | 2022-08-13 19:30:05 -0700 | [diff] [blame] | 7 | use clap::Parser; |
Brian Silverman | cc09f18 | 2022-03-09 15:40:20 -0800 | [diff] [blame] | 8 | use gen_rust_project_lib::generate_crate_info; |
| 9 | use gen_rust_project_lib::write_rust_project; |
Brian Silverman | cc09f18 | 2022-03-09 15:40:20 -0800 | [diff] [blame] | 10 | |
| 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. |
| 14 | fn 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 Silverman | 5f6f276 | 2022-08-13 19:30:05 -0700 | [diff] [blame] | 29 | let output_base = config |
| 30 | .output_base |
| 31 | .as_ref() |
| 32 | .expect("failed to find output base, is -output-base set correctly?"); |
| 33 | |
Brian Silverman | cc09f18 | 2022-03-09 15:40:20 -0800 | [diff] [blame] | 34 | let rules_rust_name = env!("ASPECT_REPOSITORY"); |
| 35 | |
| 36 | // Generate the crate specs. |
| 37 | generate_crate_info( |
| 38 | &config.bazel, |
Adam Snaider | 1c095c9 | 2023-07-08 02:09:58 -0400 | [diff] [blame^] | 39 | workspace_root, |
| 40 | rules_rust_name, |
Brian Silverman | cc09f18 | 2022-03-09 15:40:20 -0800 | [diff] [blame] | 41 | &config.targets, |
| 42 | )?; |
| 43 | |
| 44 | // Use the generated files to write rust-project.json. |
| 45 | write_rust_project( |
| 46 | &config.bazel, |
Adam Snaider | 1c095c9 | 2023-07-08 02:09:58 -0400 | [diff] [blame^] | 47 | workspace_root, |
Brian Silverman | cc09f18 | 2022-03-09 15:40:20 -0800 | [diff] [blame] | 48 | &rules_rust_name, |
| 49 | &config.targets, |
Adam Snaider | 1c095c9 | 2023-07-08 02:09:58 -0400 | [diff] [blame^] | 50 | execution_root, |
| 51 | output_base, |
| 52 | workspace_root.join("rust-project.json"), |
Brian Silverman | cc09f18 | 2022-03-09 15:40:20 -0800 | [diff] [blame] | 53 | )?; |
| 54 | |
| 55 | Ok(()) |
| 56 | } |
| 57 | |
| 58 | // Parse the configuration flags and supplement with bazel info as needed. |
| 59 | fn parse_config() -> anyhow::Result<Config> { |
Brian Silverman | 5f6f276 | 2022-08-13 19:30:05 -0700 | [diff] [blame] | 60 | let mut config = Config::parse(); |
Brian Silverman | cc09f18 | 2022-03-09 15:40:20 -0800 | [diff] [blame] | 61 | |
| 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 Snaider | 1c095c9 | 2023-07-08 02:09:58 -0400 | [diff] [blame^] | 89 | .map(|(k, v)| (k, (v[1..]).trim())) |
Brian Silverman | cc09f18 | 2022-03-09 15:40:20 -0800 | [diff] [blame] | 90 | .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 Silverman | 5f6f276 | 2022-08-13 19:30:05 -0700 | [diff] [blame] | 98 | if config.output_base.is_none() { |
| 99 | config.output_base = bazel_info.get("output_base").map(Into::into); |
| 100 | } |
Brian Silverman | cc09f18 | 2022-03-09 15:40:20 -0800 | [diff] [blame] | 101 | |
| 102 | Ok(config) |
| 103 | } |
| 104 | |
Brian Silverman | 5f6f276 | 2022-08-13 19:30:05 -0700 | [diff] [blame] | 105 | #[derive(Debug, Parser)] |
Brian Silverman | cc09f18 | 2022-03-09 15:40:20 -0800 | [diff] [blame] | 106 | struct Config { |
Brian Silverman | 5f6f276 | 2022-08-13 19:30:05 -0700 | [diff] [blame] | 107 | /// 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 Silverman | cc09f18 | 2022-03-09 15:40:20 -0800 | [diff] [blame] | 109 | workspace: Option<PathBuf>, |
| 110 | |
Brian Silverman | 5f6f276 | 2022-08-13 19:30:05 -0700 | [diff] [blame] | 111 | /// The path to the Bazel execution root. If not specified, uses the result of `bazel info execution_root`. |
| 112 | #[clap(long)] |
Brian Silverman | cc09f18 | 2022-03-09 15:40:20 -0800 | [diff] [blame] | 113 | execution_root: Option<PathBuf>, |
| 114 | |
Brian Silverman | 5f6f276 | 2022-08-13 19:30:05 -0700 | [diff] [blame] | 115 | /// 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 Silverman | cc09f18 | 2022-03-09 15:40:20 -0800 | [diff] [blame] | 121 | bazel: PathBuf, |
| 122 | |
Brian Silverman | 5f6f276 | 2022-08-13 19:30:05 -0700 | [diff] [blame] | 123 | /// Space separated list of target patterns that comes after all other args. |
| 124 | #[clap(default_value = "@//...")] |
Brian Silverman | cc09f18 | 2022-03-09 15:40:20 -0800 | [diff] [blame] | 125 | targets: Vec<String>, |
| 126 | } |