Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 55 additions & 4 deletions src/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ use crate::{
},
prepare::Prepare,
};
use std::path::PathBuf;
use std::vec::Vec;
use std::{cell::RefCell, rc::Rc};
use std::{ffi::OsString, path::PathBuf};

#[derive(Clone)]
pub(crate) enum CratePatch {
Expand Down Expand Up @@ -46,6 +46,7 @@ pub struct BuildBuilder<'a> {
krate: &'a Crate,
sandbox: SandboxBuilder,
patches: Vec<CratePatch>,
extra_cargo_args: Vec<OsString>,
}

/// Output of a completed build together with build-level statistics.
Expand Down Expand Up @@ -131,6 +132,40 @@ impl BuildBuilder<'_> {
self
}

/// Add extra arguments passed to cargo commands during the prepare phase
/// (manifest validation, lockfile generation, dependency fetching).
///
/// This is useful for passing unstable cargo flags (e.g. `-Zbindeps`) that
/// are required for cargo to parse the crate's manifest.
///
/// # Example
///
/// ```no_run
/// # use rustwide::{WorkspaceBuilder, Toolchain, Crate, cmd::SandboxBuilder};
/// # use std::error::Error;
/// # fn main() -> anyhow::Result<(), Box<dyn Error>> {
/// # let workspace = WorkspaceBuilder::new("".as_ref(), "").init()?;
/// # let toolchain = Toolchain::dist("");
/// # let krate = Crate::local("".as_ref());
/// # let sandbox = SandboxBuilder::new();
/// let mut build_dir = workspace.build_dir("foo");
/// build_dir.build(&toolchain, &krate, sandbox)
/// .extra_cargo_args(["-Zbindeps"])
/// .run(|build| {
/// build.cargo().args(&["test", "--all"]).run()?;
/// Ok(())
/// })?;
/// # Ok(())
/// # }
pub fn extra_cargo_args<S: Into<OsString>>(
mut self,
args: impl IntoIterator<Item = S>,
) -> Self {
self.extra_cargo_args
.extend(args.into_iter().map(Into::into));
self
}

/// Run a sandboxed build of the provided crate with the provided toolchain. The closure will
/// be provided an instance of [`Build`](struct.Build.html) that allows spawning new processes
/// inside the sandbox.
Expand Down Expand Up @@ -162,8 +197,14 @@ impl BuildBuilder<'_> {
self,
f: F,
) -> anyhow::Result<BuildResult<R>> {
self.build_dir
.run(self.toolchain, self.krate, self.sandbox, self.patches, f)
self.build_dir.run(
self.toolchain,
self.krate,
self.sandbox,
self.patches,
self.extra_cargo_args,
f,
)
}
}

Expand Down Expand Up @@ -208,6 +249,7 @@ impl BuildDirectory {
krate,
sandbox,
patches: Vec::new(),
extra_cargo_args: Vec::new(),
}
}

Expand All @@ -229,14 +271,22 @@ impl BuildDirectory {
krate: &Crate,
sandbox: SandboxBuilder,
patches: Vec<CratePatch>,
extra_cargo_args: Vec<OsString>,
f: F,
) -> anyhow::Result<BuildResult<R>> {
let source_dir = self.source_dir();
if source_dir.exists() {
crate::utils::remove_dir_all(&source_dir)?;
}

let mut prepare = Prepare::new(&self.workspace, toolchain, krate, &source_dir, patches);
let mut prepare = Prepare::new(
&self.workspace,
toolchain,
krate,
&source_dir,
patches,
extra_cargo_args,
);
prepare.prepare().map_err(|err| {
if err.downcast_ref::<PrepareError>().is_none() {
err.context(PrepareError::Uncategorized)
Expand Down Expand Up @@ -407,6 +457,7 @@ impl<'ws> Build<'ws> {
self.toolchain,
&self.host_source_dir(),
targets,
&[],
)
}
}
24 changes: 17 additions & 7 deletions src/prepare.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::cmd::{Command, CommandError, ProcessLinesActions};
use crate::{Crate, Toolchain, Workspace, build::CratePatch};
use anyhow::Context as _;
use log::info;
use std::path::Path;
use std::{ffi::OsString, path::Path};
use toml::{
Value,
value::{Array, Table},
Expand All @@ -14,6 +14,7 @@ pub(crate) struct Prepare<'a> {
krate: &'a Crate,
source_dir: &'a Path,
patches: Vec<CratePatch>,
extra_cargo_args: Vec<OsString>,
}

impl<'a> Prepare<'a> {
Expand All @@ -23,13 +24,15 @@ impl<'a> Prepare<'a> {
krate: &'a Crate,
source_dir: &'a Path,
patches: Vec<CratePatch>,
extra_cargo_args: Vec<OsString>,
) -> Self {
Self {
workspace,
toolchain,
krate,
source_dir,
patches,
extra_cargo_args,
}
}

Expand Down Expand Up @@ -70,6 +73,7 @@ impl<'a> Prepare<'a> {

let res = Command::new(self.workspace, self.toolchain.cargo())
.args(["metadata", "--manifest-path", "Cargo.toml", "--no-deps"])
.args(self.extra_cargo_args.iter().cloned())
.current_directory(self.source_dir)
.log_output(false)
.run();
Expand Down Expand Up @@ -117,11 +121,9 @@ impl<'a> Prepare<'a> {
return Ok(());
}

let mut cmd = Command::new(self.workspace, self.toolchain.cargo()).args([
"generate-lockfile",
"--manifest-path",
"Cargo.toml",
]);
let mut cmd = Command::new(self.workspace, self.toolchain.cargo())
.args(["generate-lockfile", "--manifest-path", "Cargo.toml"])
.args(self.extra_cargo_args.iter().cloned());
if !self.workspace.fetch_registry_index_during_builds() {
cmd = cmd
.args(["-Zno-index-update"])
Expand All @@ -133,7 +135,13 @@ impl<'a> Prepare<'a> {

#[cfg_attr(feature = "tracing", tracing::instrument(skip_all))]
fn fetch_deps(&mut self) -> anyhow::Result<()> {
fetch_deps(self.workspace, self.toolchain, self.source_dir, &[])
fetch_deps(
self.workspace,
self.toolchain,
self.source_dir,
&[],
&self.extra_cargo_args,
)
}
}

Expand All @@ -153,9 +161,11 @@ pub(crate) fn fetch_deps(
toolchain: &Toolchain,
source_dir: &Path,
fetch_build_std_targets: &[&str],
extra_cargo_args: &[OsString],
) -> anyhow::Result<()> {
let mut cmd = Command::new(workspace, toolchain.cargo())
.args(["fetch", "--manifest-path", "Cargo.toml"])
.args(extra_cargo_args.iter().cloned())
.current_directory(source_dir);
// Pass `-Zbuild-std` in case a build in the sandbox wants to use it;
// build-std has to have the source for libstd's dependencies available.
Expand Down
7 changes: 7 additions & 0 deletions tests/buildtest/crates/extra-cargo-args/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[package]
name = "extra-cargo-args"
version = "0.1.0"
edition = "2021"

[dependencies]
extra-cargo-args-dep = { path = "extra-cargo-args-dep" }
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[package]
name = "extra-cargo-args-dep"
version = "0.1.0"
edition = "2021"
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub fn hello() {}
3 changes: 3 additions & 0 deletions tests/buildtest/crates/extra-cargo-args/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
fn main() {
extra_cargo_args_dep::hello();
}
54 changes: 53 additions & 1 deletion tests/buildtest/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,58 @@ fn test_cargo_workspace() {
});
}

#[test]
fn test_extra_cargo_args() {
runner::run("extra-cargo-args", |run| {
let storage = rustwide::logging::LogStorage::new(LevelFilter::Info);
rustwide::logging::capture(&storage, || -> anyhow::Result<_> {
run.build(SandboxBuilder::new().enable_networking(false), |builder| {
builder.extra_cargo_args(["--quiet"]).run(|build| {
build.cargo().args(["run"]).run()?;
Ok(())
})
})?;
Ok(())
})?;

let output = storage.to_string();
assert!(
output.contains("generate-lockfile")
&& output.contains("--quiet")
&& !output.contains("Locking 1 package"),
"output: {output:?}"
);
Ok(())
});
}

#[test]
fn test_extra_cargo_args_invalid() {
runner::run("hello-world", |run| {
let storage = rustwide::logging::LogStorage::new(LevelFilter::Info);
let res = rustwide::logging::capture(&storage, || {
run.build(SandboxBuilder::new().enable_networking(false), |builder| {
builder
.extra_cargo_args(["--invalid-flag-that-does-not-exist"])
.run(|_build| Ok(()))
})
});

match res.err().and_then(|err| err.downcast().ok()) {
Some(rustwide::PrepareError::InvalidCargoTomlSyntax) => {}
Some(other) => panic!("expected InvalidCargoTomlSyntax, got {other:?}"),
None => panic!("expected InvalidCargoTomlSyntax, got Ok"),
}

let output = storage.to_string();
assert!(
output.contains("metadata") && output.contains("--invalid-flag-that-does-not-exist"),
"output: {output:?}"
);
Ok(())
});
}

test_prepare_error!(
test_missing_cargotoml,
"missing-cargotoml",
Expand Down Expand Up @@ -433,7 +485,7 @@ test_prepare_error_stderr!(
test_missing_deps_typo,
"missing-deps-typo",
MissingDependencies,
"error: no matching package found"
"error: no matching package named `build_rs` found"
);

test_prepare_error_stderr!(
Expand Down