Skip to content
Merged
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
19 changes: 19 additions & 0 deletions crates/cargo-util/src/process_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ use std::process::{Command, ExitStatus, Output, Stdio};
pub struct ProcessBuilder {
/// The program to execute.
program: OsString,
/// Best-effort replacement for arg0
arg0: Option<OsString>,
/// A list of arguments to pass to the program.
args: Vec<OsString>,
/// Any environment variables that should be set for the program.
Expand Down Expand Up @@ -75,6 +77,7 @@ impl ProcessBuilder {
pub fn new<T: AsRef<OsStr>>(cmd: T) -> ProcessBuilder {
ProcessBuilder {
program: cmd.as_ref().to_os_string(),
arg0: None,
args: Vec::new(),
cwd: None,
env: BTreeMap::new(),
Expand All @@ -92,6 +95,12 @@ impl ProcessBuilder {
self
}

/// (chainable) Overrides `arg0` for this program.
pub fn arg0<T: AsRef<OsStr>>(&mut self, arg: T) -> &mut ProcessBuilder {
self.arg0 = Some(arg.as_ref().to_os_string());
self
}

/// (chainable) Adds `arg` to the args list.
pub fn arg<T: AsRef<OsStr>>(&mut self, arg: T) -> &mut ProcessBuilder {
self.args.push(arg.as_ref().to_os_string());
Expand Down Expand Up @@ -142,6 +151,11 @@ impl ProcessBuilder {
self.wrappers.last().unwrap_or(&self.program)
}

/// Gets the program arg0.
pub fn get_arg0(&self) -> Option<&OsStr> {
self.arg0.as_deref()
}

/// Gets the program arguments.
pub fn get_args(&self) -> impl Iterator<Item = &OsString> {
self.wrappers
Expand Down Expand Up @@ -483,6 +497,11 @@ impl ProcessBuilder {
cmd.args(iter);
cmd
};
#[cfg(unix)]
if let Some(arg0) = self.get_arg0() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessw

If both lpApplicationName and lpCommandLine are non-NULL, the null-terminated string pointed to by lpApplicationName specifies the module to execute, and the null-terminated string pointed to by lpCommandLine specifies the command line. The new process can use GetCommandLine to retrieve the entire command line. Console processes written in C can use the argc and argv arguments to parse the command line. Because argv[0] is the module name, C programmers generally repeat the module name as the first token in the command line.

Not sure if this means that we can override the first token in lpCommandLine as arg0 overrideen.

use std::os::unix::process::CommandExt as _;
command.arg0(arg0);
}
if let Some(cwd) = self.get_cwd() {
command.current_dir(cwd);
}
Expand Down
7 changes: 7 additions & 0 deletions src/cargo/ops/cargo_run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use std::fmt::Write as _;
use std::iter;
use std::path::Path;

use crate::core::MaybePackage;
use crate::core::compiler::UnitOutput;
use crate::core::{TargetKind, Workspace};
use crate::ops;
Expand Down Expand Up @@ -105,6 +106,12 @@ pub fn run(
let pkg = bins[0].0;
let mut process = compile.target_process(exe, unit.kind, pkg, script_metas.as_ref())?;

if let MaybePackage::Package(pkg) = ws.root_maybe()
&& pkg.manifest().is_embedded()
{
process.arg0(pkg.manifest_path());
}

// Sets the working directory of the child process to the current working
// directory of the parent process.
// Overrides the default working directory of the `ProcessBuilder` returned
Expand Down
4 changes: 4 additions & 0 deletions src/doc/src/reference/unstable.md
Original file line number Diff line number Diff line change
Expand Up @@ -1496,6 +1496,10 @@ Differences between `cargo run --manifest-path <path>` and `cargo <path>`
- `cargo <path>` runs with the config for `<path>` and not the current dir, more like `cargo install --path <path>`
- `cargo <path>` is at a verbosity level below the normal default. Pass `-v` to get normal output.

When running a package with an embedded manifest,
[`arg0`](https://doc.rust-lang.org/std/os/unix/process/trait.CommandExt.html#tymethod.arg0) will be the scripts path.
To get the executable's path, see [`current_exe`](https://doc.rust-lang.org/std/env/fn.current_exe.html).

### Documentation Updates

## Profile `trim-paths` option
Expand Down
107 changes: 88 additions & 19 deletions tests/testsuite/script/cargo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@ use cargo_test_support::str;
const ECHO_SCRIPT: &str = r#"#!/usr/bin/env cargo

fn main() {
let current_exe = std::env::current_exe().unwrap().to_str().unwrap().to_owned();
let mut args = std::env::args_os();
let bin = args.next().unwrap().to_str().unwrap().to_owned();
let arg0 = args.next().unwrap().to_str().unwrap().to_owned();
let args = args.collect::<Vec<_>>();
println!("bin: {bin}");
println!("current_exe: {current_exe}");
println!("arg0: {arg0}");
println!("args: {args:?}");
}

Expand All @@ -34,7 +36,58 @@ fn basic_rs() {
p.cargo("-Zscript -v echo.rs")
.masquerade_as_nightly_cargo(&["script"])
.with_stdout_data(str![[r#"
bin: [ROOT]/home/.cargo/target/[HASH]/debug/echo[EXE]
current_exe: [ROOT]/home/.cargo/target/[HASH]/debug/echo[EXE]
arg0: [..]
args: []

"#]])
.with_stderr_data(str![[r#"
[WARNING] `package.edition` is unspecified, defaulting to `2024`
[COMPILING] echo v0.0.0 ([ROOT]/foo/echo.rs)
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
[RUNNING] `[ROOT]/home/.cargo/target/[HASH]/debug/echo[EXE]`

"#]])
.run();
}

#[cfg(unix)]
#[cargo_test(nightly, reason = "-Zscript is unstable")]
fn arg0() {
let p = cargo_test_support::project()
.file("echo.rs", ECHO_SCRIPT)
.build();

p.cargo("-Zscript -v echo.rs")
.masquerade_as_nightly_cargo(&["script"])
.with_stdout_data(str![[r#"
current_exe: [ROOT]/home/.cargo/target/[HASH]/debug/echo[EXE]
arg0: [ROOT]/foo/echo.rs
args: []

"#]])
.with_stderr_data(str![[r#"
[WARNING] `package.edition` is unspecified, defaulting to `2024`
[COMPILING] echo v0.0.0 ([ROOT]/foo/echo.rs)
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
[RUNNING] `[ROOT]/home/.cargo/target/[HASH]/debug/echo[EXE]`

"#]])
.run();
}

#[cfg(windows)]
#[cargo_test(nightly, reason = "-Zscript is unstable")]
fn arg0() {
let p = cargo_test_support::project()
.file("echo.rs", ECHO_SCRIPT)
.build();

p.cargo("-Zscript -v echo.rs")
.masquerade_as_nightly_cargo(&["script"])
.with_stdout_data(str![[r#"
current_exe: [ROOT]/home/.cargo/target/[HASH]/debug/echo[EXE]
arg0: [ROOT]/home/.cargo/target/[HASH]/debug/echo[EXE]
args: []

"#]])
Expand All @@ -57,7 +110,8 @@ fn basic_path() {
p.cargo("-Zscript -v ./echo")
.masquerade_as_nightly_cargo(&["script"])
.with_stdout_data(str![[r#"
bin: [ROOT]/home/.cargo/target/[HASH]/debug/echo[EXE]
current_exe: [ROOT]/home/.cargo/target/[HASH]/debug/echo[EXE]
arg0: [..]
args: []

"#]])
Expand Down Expand Up @@ -111,7 +165,8 @@ fn manifest_precedence_over_plugins() {
.env("PATH", &path)
.masquerade_as_nightly_cargo(&["script"])
.with_stdout_data(str![[r#"
bin: [ROOT]/home/.cargo/target/[HASH]/debug/echo[EXE]
current_exe: [ROOT]/home/.cargo/target/[HASH]/debug/echo[EXE]
arg0: [..]
args: []

"#]])
Expand Down Expand Up @@ -361,7 +416,8 @@ rustc = "non-existent-rustc"
p.cargo("-Zscript script.rs -NotAnArg")
.masquerade_as_nightly_cargo(&["script"])
.with_stdout_data(str![[r#"
bin: [ROOT]/home/.cargo/target/[HASH]/debug/script[EXE]
current_exe: [ROOT]/home/.cargo/target/[HASH]/debug/script[EXE]
arg0: [..]
args: ["-NotAnArg"]

"#]])
Expand All @@ -371,7 +427,8 @@ args: ["-NotAnArg"]
p.cargo("-Zscript ../script/script.rs -NotAnArg")
.masquerade_as_nightly_cargo(&["script"])
.with_stdout_data(str![[r#"
bin: [ROOT]/home/.cargo/target/[HASH]/debug/script[EXE]
current_exe: [ROOT]/home/.cargo/target/[HASH]/debug/script[EXE]
arg0: [..]
args: ["-NotAnArg"]

"#]])
Expand Down Expand Up @@ -412,7 +469,8 @@ fn default_programmatic_verbosity() {
p.cargo("-Zscript script.rs -NotAnArg")
.masquerade_as_nightly_cargo(&["script"])
.with_stdout_data(str![[r#"
bin: [ROOT]/home/.cargo/target/[HASH]/debug/script[EXE]
current_exe: [ROOT]/home/.cargo/target/[HASH]/debug/script[EXE]
arg0: [..]
args: ["-NotAnArg"]

"#]])
Expand All @@ -430,7 +488,8 @@ fn quiet() {
p.cargo("-Zscript -q script.rs -NotAnArg")
.masquerade_as_nightly_cargo(&["script"])
.with_stdout_data(str![[r#"
bin: [ROOT]/home/.cargo/target/[HASH]/debug/script[EXE]
current_exe: [ROOT]/home/.cargo/target/[HASH]/debug/script[EXE]
arg0: [..]
args: ["-NotAnArg"]

"#]])
Expand Down Expand Up @@ -476,7 +535,8 @@ fn test_escaped_hyphen_arg() {
p.cargo("-Zscript -v -- script.rs -NotAnArg")
.masquerade_as_nightly_cargo(&["script"])
.with_stdout_data(str![[r#"
bin: [ROOT]/home/.cargo/target/[HASH]/debug/script[EXE]
current_exe: [ROOT]/home/.cargo/target/[HASH]/debug/script[EXE]
arg0: [..]
args: ["-NotAnArg"]

"#]])
Expand All @@ -500,7 +560,8 @@ fn test_unescaped_hyphen_arg() {
p.cargo("-Zscript -v script.rs -NotAnArg")
.masquerade_as_nightly_cargo(&["script"])
.with_stdout_data(str![[r#"
bin: [ROOT]/home/.cargo/target/[HASH]/debug/script[EXE]
current_exe: [ROOT]/home/.cargo/target/[HASH]/debug/script[EXE]
arg0: [..]
args: ["-NotAnArg"]

"#]])
Expand All @@ -524,7 +585,8 @@ fn test_same_flags() {
p.cargo("-Zscript -v script.rs --help")
.masquerade_as_nightly_cargo(&["script"])
.with_stdout_data(str![[r#"
bin: [ROOT]/home/.cargo/target/[HASH]/debug/script[EXE]
current_exe: [ROOT]/home/.cargo/target/[HASH]/debug/script[EXE]
arg0: [..]
args: ["--help"]

"#]])
Expand All @@ -548,7 +610,8 @@ fn test_name_has_weird_chars() {
p.cargo("-Zscript -v s-h.w§c!.rs")
.masquerade_as_nightly_cargo(&["script"])
.with_stdout_data(str![[r#"
bin: [ROOT]/home/.cargo/target/[HASH]/debug/s-h-w-c-[EXE]
current_exe: [ROOT]/home/.cargo/target/[HASH]/debug/s-h-w-c-[EXE]
arg0: [..]
args: []

"#]])
Expand All @@ -572,7 +635,8 @@ fn test_name_has_leading_number() {
p.cargo("-Zscript -v 42answer.rs")
.masquerade_as_nightly_cargo(&["script"])
.with_stdout_data(str![[r#"
bin: [ROOT]/home/.cargo/target/[HASH]/debug/answer[EXE]
current_exe: [ROOT]/home/.cargo/target/[HASH]/debug/answer[EXE]
arg0: [..]
args: []

"#]])
Expand All @@ -594,7 +658,8 @@ fn test_name_is_number() {
p.cargo("-Zscript -v 42.rs")
.masquerade_as_nightly_cargo(&["script"])
.with_stdout_data(str![[r#"
bin: [ROOT]/home/.cargo/target/[HASH]/debug/package[EXE]
current_exe: [ROOT]/home/.cargo/target/[HASH]/debug/package[EXE]
arg0: [..]
args: []

"#]])
Expand Down Expand Up @@ -1288,7 +1353,8 @@ fn implicit_target_dir() {
p.cargo("-Zscript -v script.rs")
.masquerade_as_nightly_cargo(&["script"])
.with_stdout_data(str![[r#"
bin: [ROOT]/home/.cargo/target/[HASH]/debug/script[EXE]
current_exe: [ROOT]/home/.cargo/target/[HASH]/debug/script[EXE]
arg0: [..]
args: []

"#]])
Expand All @@ -1315,7 +1381,8 @@ fn no_local_lockfile() {
p.cargo("-Zscript -v script.rs")
.masquerade_as_nightly_cargo(&["script"])
.with_stdout_data(str![[r#"
bin: [ROOT]/home/.cargo/target/[HASH]/debug/script[EXE]
current_exe: [ROOT]/home/.cargo/target/[HASH]/debug/script[EXE]
arg0: [..]
args: []

"#]])
Expand Down Expand Up @@ -1661,7 +1728,8 @@ fn cmd_run_with_embedded() {
p.cargo("-Zscript run --manifest-path script.rs")
.masquerade_as_nightly_cargo(&["script"])
.with_stdout_data(str![[r#"
bin: [ROOT]/home/.cargo/target/[HASH]/debug/script[EXE]
current_exe: [ROOT]/home/.cargo/target/[HASH]/debug/script[EXE]
arg0: [..]
args: []

"#]])
Expand Down Expand Up @@ -1961,7 +2029,8 @@ members = [
p.cargo("-Zscript -v script/echo.rs")
.masquerade_as_nightly_cargo(&["script"])
.with_stdout_data(str![[r#"
bin: [ROOT]/home/.cargo/target/[HASH]/debug/echo[EXE]
current_exe: [ROOT]/home/.cargo/target/[HASH]/debug/echo[EXE]
arg0: [..]
args: []

"#]])
Expand Down