Skip to content

Commit 24b9765

Browse files
illicitonionttiurani
authored andcommitted
cargo_build_scripts can be run from custom directories (bazelbuild#2152)
While running from CARGO_MANIFEST_DIR is the simplest thing for compatibility, there are cases where a cargo build script may be easier to run from the exec root as most bazel actions do (e.g. where a C++ toolchain specifies in-repo include paths). This mirrors the `rundir` attribute of `go_test`, which has similar concerns: https://github.com/bazelbuild/rules_go/blob/master/docs/go/core/rules.md#go_test-rundir
1 parent 28c82de commit 24b9765

File tree

224 files changed

+4782
-3925
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

224 files changed

+4782
-3925
lines changed

bindgen/3rdparty/crates/BUILD.bindgen-0.65.1.bazel

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

bindgen/3rdparty/crates/defs.bzl

Lines changed: 31 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cargo/cargo_build_script_runner/bin.rs

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use cargo_build_script_output_parser::{BuildScriptOutput, CompileAndLinkFlags};
2020
use std::collections::BTreeMap;
2121
use std::env;
2222
use std::fs::{create_dir_all, read_to_string, write};
23-
use std::path::Path;
23+
use std::path::{Path, PathBuf};
2424
use std::process::Command;
2525

2626
fn run_buildrs() -> Result<(), String> {
@@ -45,6 +45,7 @@ fn run_buildrs() -> Result<(), String> {
4545
output_dep_env_path,
4646
stdout_path,
4747
stderr_path,
48+
rundir,
4849
input_dep_env_paths,
4950
} = parse_args()?;
5051

@@ -77,9 +78,11 @@ fn run_buildrs() -> Result<(), String> {
7778
let target_env_vars =
7879
get_target_env_vars(&rustc_env).expect("Error getting target env vars from rustc");
7980

81+
let working_directory = resolve_rundir(&rundir, &exec_root, &manifest_dir)?;
82+
8083
let mut command = Command::new(exec_root.join(progname));
8184
command
82-
.current_dir(&manifest_dir)
85+
.current_dir(&working_directory)
8386
.envs(target_env_vars)
8487
.env("OUT_DIR", out_dir_abs)
8588
.env("CARGO_MANIFEST_DIR", manifest_dir)
@@ -223,6 +226,23 @@ fn symlink_if_not_exists(original: &Path, link: &Path) -> Result<(), String> {
223226
.map_err(|err| format!("Failed to create symlink: {err}"))
224227
}
225228

229+
fn resolve_rundir(rundir: &str, exec_root: &Path, manifest_dir: &Path) -> Result<PathBuf, String> {
230+
if rundir.is_empty() {
231+
return Ok(manifest_dir.to_owned());
232+
}
233+
let rundir_path = Path::new(rundir);
234+
if rundir_path.is_absolute() {
235+
return Err(format!("rundir must be empty (to run in manifest path) or relative path (relative to exec root), but was {:?}", rundir));
236+
}
237+
if rundir_path
238+
.components()
239+
.any(|c| c == std::path::Component::ParentDir)
240+
{
241+
return Err(format!("rundir must not contain .. but was {:?}", rundir));
242+
}
243+
Ok(exec_root.join(rundir_path))
244+
}
245+
226246
fn swallow_already_exists(err: std::io::Error) -> std::io::Result<()> {
227247
if err.kind() == std::io::ErrorKind::AlreadyExists {
228248
Ok(())
@@ -243,6 +263,7 @@ struct Options {
243263
output_dep_env_path: String,
244264
stdout_path: String,
245265
stderr_path: String,
266+
rundir: String,
246267
input_dep_env_paths: Vec<String>,
247268
}
248269

@@ -251,7 +272,7 @@ fn parse_args() -> Result<Options, String> {
251272
let mut args = env::args().skip(1);
252273

253274
// TODO: we should consider an alternative to positional arguments.
254-
match (args.next(), args.next(), args.next(), args.next(), args.next(), args.next(), args.next(), args.next(), args.next(), args.next()) {
275+
match (args.next(), args.next(), args.next(), args.next(), args.next(), args.next(), args.next(), args.next(), args.next(), args.next(), args.next()) {
255276
(
256277
Some(progname),
257278
Some(crate_links),
@@ -263,6 +284,7 @@ fn parse_args() -> Result<Options, String> {
263284
Some(output_dep_env_path),
264285
Some(stdout_path),
265286
Some(stderr_path),
287+
Some(rundir),
266288
) => {
267289
Ok(Options{
268290
progname,
@@ -275,6 +297,7 @@ fn parse_args() -> Result<Options, String> {
275297
output_dep_env_path,
276298
stdout_path,
277299
stderr_path,
300+
rundir,
278301
input_dep_env_paths: args.collect(),
279302
})
280303
}

cargo/private/cargo_build_script.bzl

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,7 @@ def _cargo_build_script_impl(ctx):
226226
args.add(dep_env_out)
227227
args.add(streams.stdout)
228228
args.add(streams.stderr)
229+
args.add(ctx.attr.rundir)
229230

230231
build_script_inputs = []
231232
for dep in ctx.attr.link_deps:
@@ -305,6 +306,16 @@ cargo_build_script = rule(
305306
"links": attr.string(
306307
doc = "The name of the native library this crate links against.",
307308
),
309+
"rundir": attr.string(
310+
default = "",
311+
doc = dedent("""\
312+
A directory to cd to before the cargo_build_script is run. This should be a path relative to the exec root.
313+
314+
The default behaviour (and the behaviour if rundir is set to the empty string) is to change to the relative path corresponding to the cargo manifest directory, which replicates the normal behaviour of cargo so it is easy to write compatible build scripts.
315+
316+
If set to `.`, the cargo build script will run in the exec root.
317+
"""),
318+
),
308319
"rustc_flags": attr.string_list(
309320
doc = dedent("""\
310321
List of compiler flags passed to `rustc`.

cargo/private/cargo_build_script_wrapper.bzl

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ def cargo_build_script(
1818
data = [],
1919
tools = [],
2020
links = None,
21+
rundir = None,
2122
rustc_env = {},
2223
rustc_flags = [],
2324
visibility = None,
@@ -93,6 +94,11 @@ def cargo_build_script(
9394
data (list, optional): Files needed by the build script.
9495
tools (list, optional): Tools (executables) needed by the build script.
9596
links (str, optional): Name of the native library this crate links against.
97+
rundir (str, optional): A directory to `cd` to before the cargo_build_script is run. This should be a path relative to the exec root.
98+
99+
The default behaviour (and the behaviour if rundir is set to the empty string) is to change to the relative path corresponding to the cargo manifest directory, which replicates the normal behaviour of cargo so it is easy to write compatible build scripts.
100+
101+
If set to `.`, the cargo build script will run in the exec root.
96102
rustc_env (dict, optional): Environment variables to set in rustc when compiling the build script.
97103
rustc_flags (list, optional): List of compiler flags passed to `rustc`.
98104
visibility (list of label, optional): Visibility to apply to the generated build script output.
@@ -150,6 +156,7 @@ def cargo_build_script(
150156
link_deps = link_deps,
151157
data = data,
152158
tools = tools,
159+
rundir = rundir,
153160
rustc_flags = rustc_flags,
154161
visibility = visibility,
155162
tags = tags,

0 commit comments

Comments
 (0)