diff --git a/src/build.rs b/src/build.rs index 4d8c95d..23ef07d 100644 --- a/src/build.rs +++ b/src/build.rs @@ -40,6 +40,7 @@ pub struct BuildBuilder<'a> { krate: &'a Crate, sandbox: SandboxBuilder, patches: Vec, + extra_cargo_args: Vec, } impl BuildBuilder<'_> { @@ -107,6 +108,36 @@ 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> { + /// # 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(vec!["-Zbindeps".into()]) + /// .run(|build| { + /// build.cargo().args(&["test", "--all"]).run()?; + /// Ok(()) + /// })?; + /// # Ok(()) + /// # } + pub fn extra_cargo_args(mut self, args: Vec) -> Self { + self.extra_cargo_args = args; + 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. @@ -131,8 +162,14 @@ impl BuildBuilder<'_> { /// # Ok(()) /// # } pub fn run anyhow::Result>(self, f: F) -> anyhow::Result { - 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, + ) } } @@ -177,6 +214,7 @@ impl BuildDirectory { krate, sandbox, patches: Vec::new(), + extra_cargo_args: Vec::new(), } } @@ -186,6 +224,7 @@ impl BuildDirectory { krate: &Crate, sandbox: SandboxBuilder, patches: Vec, + extra_cargo_args: Vec, f: F, ) -> anyhow::Result { let source_dir = self.source_dir(); @@ -193,7 +232,14 @@ impl BuildDirectory { 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::().is_none() { err.context(PrepareError::Uncategorized) @@ -333,6 +379,7 @@ impl<'ws> Build<'ws> { self.toolchain, &self.host_source_dir(), targets, + &[], ) } } diff --git a/src/prepare.rs b/src/prepare.rs index a4908bc..e8b9aee 100644 --- a/src/prepare.rs +++ b/src/prepare.rs @@ -14,6 +14,7 @@ pub(crate) struct Prepare<'a> { krate: &'a Crate, source_dir: &'a Path, patches: Vec, + extra_cargo_args: Vec, } impl<'a> Prepare<'a> { @@ -23,6 +24,7 @@ impl<'a> Prepare<'a> { krate: &'a Crate, source_dir: &'a Path, patches: Vec, + extra_cargo_args: Vec, ) -> Self { Self { workspace, @@ -30,6 +32,7 @@ impl<'a> Prepare<'a> { krate, source_dir, patches, + extra_cargo_args, } } @@ -57,6 +60,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) .cd(self.source_dir) .log_output(false) .run(); @@ -101,11 +105,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); if !self.workspace.fetch_registry_index_during_builds() { cmd = cmd .args(&["-Zno-index-update"]) @@ -116,7 +118,13 @@ impl<'a> Prepare<'a> { } 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, + ) } } @@ -125,9 +133,11 @@ pub(crate) fn fetch_deps( toolchain: &Toolchain, source_dir: &Path, fetch_build_std_targets: &[&str], + extra_cargo_args: &[String], ) -> anyhow::Result<()> { let mut cmd = Command::new(workspace, toolchain.cargo()) .args(&["fetch", "--manifest-path", "Cargo.toml"]) + .args(extra_cargo_args) .cd(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. diff --git a/tests/buildtest/mod.rs b/tests/buildtest/mod.rs index d27f9bb..8d88e45 100644 --- a/tests/buildtest/mod.rs +++ b/tests/buildtest/mod.rs @@ -247,6 +247,37 @@ fn test_cargo_workspace() { }); } +#[test] +fn test_extra_cargo_args() { + runner::run("hello-world", |run| { + run.build(SandboxBuilder::new().enable_networking(false), |builder| { + builder + .extra_cargo_args(vec!["--quiet".into()]) + .run(|build| { + build.cargo().args(&["run"]).run()?; + Ok(()) + }) + })?; + Ok(()) + }); +} + +#[test] +fn test_extra_cargo_args_invalid() { + runner::run("hello-world", |run| { + let res = run.build(SandboxBuilder::new().enable_networking(false), |builder| { + builder + .extra_cargo_args(vec!["--invalid-flag-that-does-not-exist".into()]) + .run(|_build| Ok(())) + }); + assert!( + res.is_err(), + "expected extra cargo args to cause a prepare failure" + ); + Ok(()) + }); +} + test_prepare_error!( test_missing_cargotoml, "missing-cargotoml",