|
5 | 5 | use std::convert::TryFrom; |
6 | 6 | use std::env; |
7 | 7 | use std::fs; |
| 8 | +use std::process::Command; |
8 | 9 |
|
9 | 10 | use anyhow::bail; |
10 | | -use camino::Utf8Path; |
11 | | -use camino::Utf8PathBuf; |
12 | | -use clap::Args; |
| 11 | +use camino::{Utf8Path, Utf8PathBuf}; |
| 12 | +use clap::Parser; |
13 | 13 | use env_logger::Target; |
14 | 14 | use env_logger::WriteStyle; |
15 | 15 | use gen_rust_project_lib::DiscoverProject; |
16 | 16 | use gen_rust_project_lib::NormalizedProjectString; |
17 | 17 | use gen_rust_project_lib::WORKSPACE_ROOT_FILE_NAMES; |
18 | | -use gen_rust_project_lib::{generate_crate_info, generate_rust_project, Config, RustAnalyzerArg}; |
| 18 | +use gen_rust_project_lib::{generate_crate_info, generate_rust_project, RustAnalyzerArg}; |
19 | 19 | use log::LevelFilter; |
20 | 20 | use std::io::Write; |
21 | 21 |
|
22 | | -#[derive(Debug, Args)] |
23 | | -struct DiscoverProjectArgs { |
24 | | - /// The argument that `rust-analyzer` can pass to the binary. |
25 | | - rust_analyzer_argument: Option<RustAnalyzerArg>, |
26 | | -} |
27 | | - |
28 | 22 | fn discover_rust_project( |
29 | 23 | bazel: &Utf8Path, |
30 | 24 | output_base: &Utf8Path, |
@@ -111,13 +105,9 @@ fn project_discovery() -> anyhow::Result<()> { |
111 | 105 | output_base, |
112 | 106 | bazel, |
113 | 107 | config_group, |
114 | | - specific, |
| 108 | + rust_analyzer_argument, |
115 | 109 | } = Config::parse()?; |
116 | 110 |
|
117 | | - let DiscoverProjectArgs { |
118 | | - rust_analyzer_argument, |
119 | | - } = specific; |
120 | | - |
121 | 111 | log::info!("got rust-analyzer argument: {rust_analyzer_argument:?}"); |
122 | 112 |
|
123 | 113 | let ra_arg = match rust_analyzer_argument { |
@@ -174,3 +164,126 @@ fn main() { |
174 | 164 | discovery_failure(e); |
175 | 165 | } |
176 | 166 | } |
| 167 | + |
| 168 | +#[derive(Debug)] |
| 169 | +pub struct Config { |
| 170 | + /// The path to the Bazel workspace directory. If not specified, uses the result of `bazel info workspace`. |
| 171 | + pub workspace: Utf8PathBuf, |
| 172 | + |
| 173 | + /// The path to the Bazel execution root. If not specified, uses the result of `bazel info execution_root`. |
| 174 | + pub execution_root: Utf8PathBuf, |
| 175 | + |
| 176 | + /// The path to the Bazel output user root. If not specified, uses the result of `bazel info output_base`. |
| 177 | + pub output_base: Utf8PathBuf, |
| 178 | + |
| 179 | + /// The path to a Bazel binary |
| 180 | + pub bazel: Utf8PathBuf, |
| 181 | + |
| 182 | + /// A `--config` directive that gets passed to Bazel to be able to pass custom configurations. |
| 183 | + pub config_group: Option<String>, |
| 184 | + |
| 185 | + /// The argument that `rust-analyzer` can pass to the binary. |
| 186 | + rust_analyzer_argument: Option<RustAnalyzerArg>, |
| 187 | +} |
| 188 | + |
| 189 | +impl Config { |
| 190 | + // Parse the configuration flags and supplement with bazel info as needed. |
| 191 | + pub fn parse() -> anyhow::Result<Self> { |
| 192 | + let ConfigParser { |
| 193 | + mut workspace, |
| 194 | + mut execution_root, |
| 195 | + mut output_base, |
| 196 | + bazel, |
| 197 | + config_group, |
| 198 | + rust_analyzer_argument, |
| 199 | + } = ConfigParser::parse(); |
| 200 | + |
| 201 | + if workspace.is_some() && execution_root.is_some() && output_base.is_some() { |
| 202 | + return Ok(Config { |
| 203 | + workspace: workspace.unwrap(), |
| 204 | + execution_root: execution_root.unwrap(), |
| 205 | + output_base: output_base.unwrap(), |
| 206 | + bazel, |
| 207 | + config_group, |
| 208 | + rust_analyzer_argument, |
| 209 | + }); |
| 210 | + } |
| 211 | + |
| 212 | + // We need some info from `bazel info`. Fetch it now. |
| 213 | + let mut bazel_info_command = Command::new(&bazel); |
| 214 | + |
| 215 | + // Execute bazel info. |
| 216 | + let output = bazel_info_command |
| 217 | + // Switch to the workspace directory if one was provided. |
| 218 | + .current_dir(workspace.as_deref().unwrap_or(Utf8Path::new("."))) |
| 219 | + .env_remove("BAZELISK_SKIP_WRAPPER") |
| 220 | + .env_remove("BUILD_WORKING_DIRECTORY") |
| 221 | + .env_remove("BUILD_WORKSPACE_DIRECTORY") |
| 222 | + // Set the output_base if one was provided. |
| 223 | + .args(output_base.as_ref().map(|s| format!("--output_base={s}"))) |
| 224 | + .arg("info") |
| 225 | + .args(config_group.as_ref().map(|s| format!("--config={s}"))) |
| 226 | + .output()?; |
| 227 | + |
| 228 | + if !output.status.success() { |
| 229 | + let status = output.status; |
| 230 | + let stderr = String::from_utf8_lossy(&output.stderr); |
| 231 | + bail!("Failed to run `bazel info` ({status:?}): {stderr}"); |
| 232 | + } |
| 233 | + |
| 234 | + // Extract the output. |
| 235 | + let output = String::from_utf8(output.stdout)?; |
| 236 | + |
| 237 | + let iter = output |
| 238 | + .trim() |
| 239 | + .split('\n') |
| 240 | + .filter_map(|line| line.split_once(':')) |
| 241 | + .map(|(k, v)| (k, v.trim())); |
| 242 | + |
| 243 | + for (k, v) in iter { |
| 244 | + match k { |
| 245 | + "workspace" => workspace = Some(v.into()), |
| 246 | + "execution_root" => execution_root = Some(v.into()), |
| 247 | + "output_base" => output_base = Some(v.into()), |
| 248 | + _ => continue, |
| 249 | + } |
| 250 | + } |
| 251 | + |
| 252 | + let config = Config { |
| 253 | + workspace: workspace.expect("'workspace' must exist in bazel info"), |
| 254 | + execution_root: execution_root.expect("'execution_root' must exist in bazel info"), |
| 255 | + output_base: output_base.expect("'output_base' must exist in bazel info"), |
| 256 | + bazel, |
| 257 | + config_group, |
| 258 | + rust_analyzer_argument, |
| 259 | + }; |
| 260 | + |
| 261 | + Ok(config) |
| 262 | + } |
| 263 | +} |
| 264 | + |
| 265 | +#[derive(Debug, Parser)] |
| 266 | +struct ConfigParser { |
| 267 | + /// The path to the Bazel workspace directory. If not specified, uses the result of `bazel info workspace`. |
| 268 | + #[clap(long, env = "BUILD_WORKSPACE_DIRECTORY")] |
| 269 | + workspace: Option<Utf8PathBuf>, |
| 270 | + |
| 271 | + /// The path to the Bazel execution root. If not specified, uses the result of `bazel info execution_root`. |
| 272 | + #[clap(long)] |
| 273 | + execution_root: Option<Utf8PathBuf>, |
| 274 | + |
| 275 | + /// The path to the Bazel output user root. If not specified, uses the result of `bazel info output_base`. |
| 276 | + #[clap(long, env = "OUTPUT_BASE")] |
| 277 | + output_base: Option<Utf8PathBuf>, |
| 278 | + |
| 279 | + /// The path to a Bazel binary |
| 280 | + #[clap(long, default_value = "bazel")] |
| 281 | + bazel: Utf8PathBuf, |
| 282 | + |
| 283 | + /// A `--config` directive that gets passed to Bazel to be able to pass custom configurations. |
| 284 | + #[clap(long)] |
| 285 | + config_group: Option<String>, |
| 286 | + |
| 287 | + /// The argument that `rust-analyzer` can pass to the binary. |
| 288 | + rust_analyzer_argument: Option<RustAnalyzerArg>, |
| 289 | +} |
0 commit comments