diff --git a/src/cargo/core/compiler/build_context/target_info.rs b/src/cargo/core/compiler/build_context/target_info.rs index f36fc173bcc..5d34b8aacba 100644 --- a/src/cargo/core/compiler/build_context/target_info.rs +++ b/src/cargo/core/compiler/build_context/target_info.rs @@ -953,6 +953,16 @@ impl<'gctx> RustcTargetData<'gctx> { res.merge_compile_kind(kind)?; } + // TODO: Although `-Zsandbox need this target platform to exist, + // if there is no build script involved in the entire dependency tree, + // there is no need for doing this target pre-fetch. + if ws.gctx().cli_unstable().sandbox { + if let Some(target) = gctx.sandbox_config()?.target.as_ref() { + let kind = CompileKind::Target(CompileTarget::new(target)?); + res.merge_compile_kind(kind)?; + } + } + Ok(res) } diff --git a/src/cargo/core/compiler/compilation.rs b/src/cargo/core/compiler/compilation.rs index 5ecf77d4c6a..8f28355aa02 100644 --- a/src/cargo/core/compiler/compilation.rs +++ b/src/cargo/core/compiler/compilation.rs @@ -227,13 +227,23 @@ impl<'gctx> Compilation<'gctx> { cmd: T, pkg: &Package, ) -> CargoResult { - self.fill_env( - ProcessBuilder::new(cmd), - pkg, - None, - CompileKind::Host, - ToolKind::HostProcess, - ) + let builder = if self.gctx.cli_unstable().sandbox { + let sandbox = self.gctx.sandbox_config()?; + match &sandbox.runner { + Some(runner) => { + let program = runner.path.resolve_program(self.gctx); + let mut builder = ProcessBuilder::new(program); + builder.args(&runner.args); + builder.arg(cmd); + builder + } + None => ProcessBuilder::new(cmd), + } + } else { + ProcessBuilder::new(cmd) + }; + + self.fill_env(builder, pkg, None, CompileKind::Host, ToolKind::HostProcess) } pub fn target_runner(&self, kind: CompileKind) -> Option<&(PathBuf, Vec)> { diff --git a/src/cargo/core/compiler/custom_build.rs b/src/cargo/core/compiler/custom_build.rs index f36e741c65b..d8e9f38d91b 100644 --- a/src/cargo/core/compiler/custom_build.rs +++ b/src/cargo/core/compiler/custom_build.rs @@ -32,10 +32,10 @@ //! [instructions]: https://doc.rust-lang.org/cargo/reference/build-scripts.html#outputs-of-the-build-script use super::{fingerprint, BuildRunner, Job, Unit, Work}; -use crate::core::compiler::artifact; use crate::core::compiler::build_runner::Metadata; use crate::core::compiler::fingerprint::DirtyReason; use crate::core::compiler::job_queue::JobState; +use crate::core::compiler::{artifact, FileFlavor}; use crate::core::{profiles::ProfileRoot, PackageId, Target}; use crate::util::errors::CargoResult; use crate::util::internal; @@ -265,7 +265,19 @@ fn build_work(build_runner: &mut BuildRunner<'_, '_>, unit: &Unit) -> CargoResul } // Building the command to execute - let to_exec = script_dir.join(unit.target.name()); + let to_exec = if build_runner.bcx.gctx.cli_unstable().sandbox { + // XXX: is the `expect` statement here really the truth? + let outputs = build_runner.outputs(build_script_unit)?; + let mut outputs = outputs + .iter() + .filter(|output| output.flavor == FileFlavor::Normal); + let (Some(output), None) = (outputs.next(), outputs.next()) else { + panic!("a build script to produce the one and only executable"); + }; + output.bin_dst().as_os_str().to_os_string() + } else { + script_dir.join(unit.target.name()).into_os_string() + }; // Start preparing the process to execute, starting out with some // environment variables. Note that the profile-related environment @@ -274,7 +286,6 @@ fn build_work(build_runner: &mut BuildRunner<'_, '_>, unit: &Unit) -> CargoResul // NOTE: if you add any profile flags, be sure to update // `Profiles::get_profile_run_custom_build` so that those flags get // carried over. - let to_exec = to_exec.into_os_string(); let mut cmd = build_runner.compilation.host_process(to_exec, &unit.pkg)?; let debug = unit.profile.debuginfo.is_turned_on(); cmd.env("OUT_DIR", &script_out_dir) diff --git a/src/cargo/core/compiler/unit_dependencies.rs b/src/cargo/core/compiler/unit_dependencies.rs index 59e356fc682..54d0aa017ac 100644 --- a/src/cargo/core/compiler/unit_dependencies.rs +++ b/src/cargo/core/compiler/unit_dependencies.rs @@ -481,8 +481,16 @@ fn compute_deps_custom_build( &unit.pkg, &unit.target, script_unit_for, - // Build scripts always compiled for the host. - CompileKind::Host, + // Build scripts always compiled for the host, + // unless `sandbox.target` is set. + if state.gctx.cli_unstable().sandbox { + match state.gctx.sandbox_config()?.target.as_ref() { + Some(target) => CompileKind::Target(super::CompileTarget::new(target)?), + None => CompileKind::Host, + } + } else { + CompileKind::Host + }, CompileMode::Build, IS_NO_ARTIFACT_DEP, )?; diff --git a/src/cargo/core/features.rs b/src/cargo/core/features.rs index 2638ff95234..c4224ebaacf 100644 --- a/src/cargo/core/features.rs +++ b/src/cargo/core/features.rs @@ -785,6 +785,7 @@ unstable_cli_options!( publish_timeout: bool = ("Enable the `publish.timeout` key in .cargo/config.toml file"), rustdoc_map: bool = ("Allow passing external documentation mappings to rustdoc"), rustdoc_scrape_examples: bool = ("Allows Rustdoc to scrape code examples from reverse-dependencies"), + sandbox: bool = ("Enable sandbox build"), script: bool = ("Enable support for single-file, `.rs` packages"), separate_nightlies: bool, skip_rustdoc_fingerprint: bool, @@ -1289,6 +1290,7 @@ impl CliUnstable { "publish-timeout" => self.publish_timeout = parse_empty(k, v)?, "rustdoc-map" => self.rustdoc_map = parse_empty(k, v)?, "rustdoc-scrape-examples" => self.rustdoc_scrape_examples = parse_empty(k, v)?, + "sandbox" => self.sandbox = parse_empty(k, v)?, "separate-nightlies" => self.separate_nightlies = parse_empty(k, v)?, "checksum-freshness" => self.checksum_freshness = parse_empty(k, v)?, "skip-rustdoc-fingerprint" => self.skip_rustdoc_fingerprint = parse_empty(k, v)?, diff --git a/src/cargo/util/context/mod.rs b/src/cargo/util/context/mod.rs index c38fd6fd55a..c5be78c3888 100644 --- a/src/cargo/util/context/mod.rs +++ b/src/cargo/util/context/mod.rs @@ -228,6 +228,7 @@ pub struct GlobalContext { doc_extern_map: LazyCell, progress_config: ProgressConfig, env_config: LazyCell, + sandbox_config: LazyCell, /// This should be false if: /// - this is an artifact of the rustc distribution process for "stable" or for "beta" /// - this is an `#[test]` that does not opt in with `enable_nightly_features` @@ -322,6 +323,7 @@ impl GlobalContext { doc_extern_map: LazyCell::new(), progress_config: ProgressConfig::default(), env_config: LazyCell::new(), + sandbox_config: LazyCell::new(), nightly_features_allowed: matches!(&*features::channel(), "nightly" | "dev"), ws_roots: RefCell::new(HashMap::new()), global_cache_tracker: LazyCell::new(), @@ -1891,6 +1893,15 @@ impl GlobalContext { .try_borrow_with(|| self.get::("doc.extern-map")) } + /// Returns the config table of `[sandbox]` (unstable). + /// + /// TODO: currently access `sandbox.runner` from config for convenience. + /// Need to find a better configuration design. + pub fn sandbox_config(&self) -> CargoResult<&CargoSandboxConfig> { + self.sandbox_config + .try_borrow_with(|| self.get::("sandbox")) + } + /// Returns true if the `[target]` table should be applied to host targets. pub fn target_applies_to_host(&self) -> CargoResult { target::get_target_applies_to_host(self) @@ -2724,6 +2735,15 @@ pub enum ProgressWhen { Always, } +#[derive(Debug, Default, Deserialize)] +#[serde(rename_all = "kebab-case")] +pub struct CargoSandboxConfig { + /// Target platform sandboxed build scripts built for. + pub target: Option, + /// Runner that executes artifacts of build scripts. + pub runner: Option, +} + fn progress_or_string<'de, D>(deserializer: D) -> Result, D::Error> where D: serde::de::Deserializer<'de>, diff --git a/tests/testsuite/cargo/z_help/stdout.term.svg b/tests/testsuite/cargo/z_help/stdout.term.svg index a429e92d58b..3f7179b887c 100644 --- a/tests/testsuite/cargo/z_help/stdout.term.svg +++ b/tests/testsuite/cargo/z_help/stdout.term.svg @@ -1,4 +1,4 @@ - +