Skip to content

Commit 941da7f

Browse files
author
Bogdan Mircea
committed
let each binary have their own config
1 parent 5e2a2de commit 941da7f

4 files changed

Lines changed: 248 additions & 173 deletions

File tree

tools/rust_analyzer/bin/discover_rust_project.rs

Lines changed: 128 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,26 +5,20 @@
55
use std::convert::TryFrom;
66
use std::env;
77
use std::fs;
8+
use std::process::Command;
89

910
use anyhow::bail;
10-
use camino::Utf8Path;
11-
use camino::Utf8PathBuf;
12-
use clap::Args;
11+
use camino::{Utf8Path, Utf8PathBuf};
12+
use clap::Parser;
1313
use env_logger::Target;
1414
use env_logger::WriteStyle;
1515
use gen_rust_project_lib::DiscoverProject;
1616
use gen_rust_project_lib::NormalizedProjectString;
1717
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};
1919
use log::LevelFilter;
2020
use std::io::Write;
2121

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-
2822
fn discover_rust_project(
2923
bazel: &Utf8Path,
3024
output_base: &Utf8Path,
@@ -111,13 +105,9 @@ fn project_discovery() -> anyhow::Result<()> {
111105
output_base,
112106
bazel,
113107
config_group,
114-
specific,
108+
rust_analyzer_argument,
115109
} = Config::parse()?;
116110

117-
let DiscoverProjectArgs {
118-
rust_analyzer_argument,
119-
} = specific;
120-
121111
log::info!("got rust-analyzer argument: {rust_analyzer_argument:?}");
122112

123113
let ra_arg = match rust_analyzer_argument {
@@ -174,3 +164,126 @@ fn main() {
174164
discovery_failure(e);
175165
}
176166
}
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+
}

tools/rust_analyzer/bin/gen_rust_project.rs

Lines changed: 120 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,10 @@
1+
use std::process::Command;
12
use std::{env, io::ErrorKind};
23

34
use anyhow::bail;
4-
use camino::Utf8Path;
5-
use clap::Args;
6-
use gen_rust_project_lib::{
7-
generate_crate_info, generate_rust_project, Config, NormalizedProjectString,
8-
};
9-
10-
#[derive(Debug, Args)]
11-
struct GenerateProjectArgs {
12-
/// Space separated list of target patterns that comes after all other args.
13-
#[clap(default_value = "@//...")]
14-
targets: Vec<String>,
15-
}
5+
use camino::{Utf8Path, Utf8PathBuf};
6+
use clap::Parser;
7+
use gen_rust_project_lib::{generate_crate_info, generate_rust_project, NormalizedProjectString};
168

179
fn write_rust_project(
1810
bazel: &Utf8Path,
@@ -63,20 +55,17 @@ fn main() -> anyhow::Result<()> {
6355
execution_root,
6456
output_base,
6557
bazel,
66-
config_group,
67-
specific,
58+
targets,
6859
} = Config::parse()?;
6960

70-
let GenerateProjectArgs { targets } = specific;
71-
7261
let rules_rust_name = env!("ASPECT_REPOSITORY");
7362

7463
// Generate the crate specs.
7564
generate_crate_info(
7665
&bazel,
7766
&output_base,
7867
&workspace,
79-
config_group.as_deref(),
68+
None,
8069
rules_rust_name,
8170
&targets,
8271
)?;
@@ -87,11 +76,124 @@ fn main() -> anyhow::Result<()> {
8776
&output_base,
8877
&workspace,
8978
&execution_root,
90-
config_group.as_deref(),
79+
None,
9180
rules_rust_name,
9281
&targets,
9382
&workspace.join("rust-project.json"),
9483
)?;
9584

9685
Ok(())
9786
}
87+
88+
#[derive(Debug)]
89+
pub struct Config {
90+
/// The path to the Bazel workspace directory. If not specified, uses the result of `bazel info workspace`.
91+
pub workspace: Utf8PathBuf,
92+
93+
/// The path to the Bazel execution root. If not specified, uses the result of `bazel info execution_root`.
94+
pub execution_root: Utf8PathBuf,
95+
96+
/// The path to the Bazel output user root. If not specified, uses the result of `bazel info output_base`.
97+
pub output_base: Utf8PathBuf,
98+
99+
/// The path to a Bazel binary
100+
pub bazel: Utf8PathBuf,
101+
102+
/// Space separated list of target patterns that comes after all other args.
103+
targets: Vec<String>,
104+
}
105+
106+
impl Config {
107+
// Parse the configuration flags and supplement with bazel info as needed.
108+
pub fn parse() -> anyhow::Result<Self> {
109+
let ConfigParser {
110+
mut workspace,
111+
mut execution_root,
112+
mut output_base,
113+
bazel,
114+
targets,
115+
} = ConfigParser::parse();
116+
117+
if workspace.is_some() && execution_root.is_some() && output_base.is_some() {
118+
return Ok(Config {
119+
workspace: workspace.unwrap(),
120+
execution_root: execution_root.unwrap(),
121+
output_base: output_base.unwrap(),
122+
bazel,
123+
targets,
124+
});
125+
}
126+
127+
// We need some info from `bazel info`. Fetch it now.
128+
let mut bazel_info_command = Command::new(&bazel);
129+
130+
// Execute bazel info.
131+
let output = bazel_info_command
132+
// Switch to the workspace directory if one was provided.
133+
.current_dir(workspace.as_deref().unwrap_or(Utf8Path::new(".")))
134+
.env_remove("BAZELISK_SKIP_WRAPPER")
135+
.env_remove("BUILD_WORKING_DIRECTORY")
136+
.env_remove("BUILD_WORKSPACE_DIRECTORY")
137+
// Set the output_base if one was provided.
138+
.args(output_base.as_ref().map(|s| format!("--output_base={s}")))
139+
.arg("info")
140+
.output()?;
141+
142+
if !output.status.success() {
143+
let status = output.status;
144+
let stderr = String::from_utf8_lossy(&output.stderr);
145+
bail!("Failed to run `bazel info` ({status:?}): {stderr}");
146+
}
147+
148+
// Extract the output.
149+
let output = String::from_utf8(output.stdout)?;
150+
151+
let iter = output
152+
.trim()
153+
.split('\n')
154+
.filter_map(|line| line.split_once(':'))
155+
.map(|(k, v)| (k, v.trim()));
156+
157+
for (k, v) in iter {
158+
match k {
159+
"workspace" => workspace = Some(v.into()),
160+
"execution_root" => execution_root = Some(v.into()),
161+
"output_base" => output_base = Some(v.into()),
162+
_ => continue,
163+
}
164+
}
165+
166+
let config = Config {
167+
workspace: workspace.expect("'workspace' must exist in bazel info"),
168+
execution_root: execution_root.expect("'execution_root' must exist in bazel info"),
169+
output_base: output_base.expect("'output_base' must exist in bazel info"),
170+
bazel,
171+
targets,
172+
};
173+
174+
Ok(config)
175+
}
176+
}
177+
178+
#[derive(Debug, Parser)]
179+
struct ConfigParser {
180+
/// The path to the Bazel workspace directory. If not specified, uses the result of `bazel info workspace`.
181+
#[clap(long, env = "BUILD_WORKSPACE_DIRECTORY")]
182+
workspace: Option<Utf8PathBuf>,
183+
184+
/// The path to the Bazel execution root. If not specified, uses the result of `bazel info execution_root`.
185+
#[clap(long)]
186+
execution_root: Option<Utf8PathBuf>,
187+
188+
/// The path to the Bazel output user root. If not specified, uses the result of `bazel info output_base`.
189+
#[clap(long, env = "OUTPUT_BASE")]
190+
output_base: Option<Utf8PathBuf>,
191+
192+
/// The path to a Bazel binary
193+
#[clap(long, default_value = "bazel")]
194+
bazel: Utf8PathBuf,
195+
196+
/// Space separated list of target patterns that comes after all other args.
197+
#[clap(default_value = "@//...")]
198+
targets: Vec<String>,
199+
}

0 commit comments

Comments
 (0)