Skip to content

feat: add ToolCommand, a command whose Debug output hides the environment - #2098

Open
waltuov wants to merge 1 commit into
rust-lang:mainfrom
waltuov:feat/tool-command
Open

waltuov wants to merge 1 commit into
rust-lang:mainfrom
waltuov:feat/tool-command

Conversation

@waltuov

@waltuov waltuov commented Nov 11, 2026

Copy link
Copy Markdown
Contributor

For #2072.

Since 1.6.0 cc sets the whole environment on the commands it makes, also on the ones it hands out through Tool::to_command, Build::get_archiver and Build::get_ranlib. On Unix, Debug for such a Command prints env -i followed by every variable and its value. A build script that prints the command, as libz-sys did with println!("running {:?}", cmd), puts the environment into its output, which Cargo keeps under target/ (GHSA-8p3x-q3q3-q3v5). On Windows, std's Debug prints only the program and the arguments.

This adds cc::ToolCommand, the wrapper discussed in #2072. It owns the Command and derefs to it, so it is set up and run like one, and Command::from gives the Command back. Its Debug output is the program and the arguments only, written by the helper behind cc's own running: lines. These functions return it:

  • Tool::command
  • Build::get_archiver_command and Build::try_get_archiver_command
  • Build::get_ranlib_command and Build::try_get_ranlib_command

Each one wraps the result of the existing function, so the command itself is the same. A build script that prints the command usually only has to change to_command() to command(), or get_archiver() to get_archiver_command().

The five existing functions keep their signatures and return the same Command as before, and the environment handling is unchanged. Their docs now say that the Debug output lists the environment on Unix and point to the new functions. The logger docs say the same about the Command passed as extra.

It is a named type so that a helper can take &mut cc::ToolCommand and keep that Debug. Returning impl DerefMut<Target = Command> + Debug would also work if you prefer a smaller API.

Limits:

  • Printing the inner Command with {:?} still lists the environment on Unix: after Command::from(cmd), through &mut *cmd, in a function that takes &mut Command, or as the &mut Command that cmd.arg(..) returns. The type docs say so.
  • Nothing is deprecated, so existing callers print the environment until they switch. Deprecating the old functions in favour of new ones was one of the options in Command from Tool::to_command and friends carry whole environment #2072. #[deprecated] on to_command would warn when a crate that calls it is built in its own workspace (Cargo caps lints for dependencies) and fail that build under -D warnings. I can add it here or in a later release if you want it.

Testing

  • src/tool.rs: Tool::command matches to_command in program, arguments and environment, also after Command::from. Debug is exactly "cc" "-c" "foo.c" after an argument added through deref, while the snapshot holds a marker variable.
  • tests/get_commands.rs: the same comparison and Debug check for both archiver and both ranlib functions. A failing check there doesn't print the environment.
  • On Unix the tests also check that {:?} of the plain Command does contain the marker, so they show the difference.
  • On Linux (cargo test in a CI run in a private repository) the suite passes with and without --features parallel,compile-commands. With Debug forwarding to the inner Command there, tool_command_debug_shows_program_and_args_only, archiver_command_matches_archiver and ranlib_command_matches_ranlib fail and the rest passes. On Windows that change can't fail a test, since std's Debug leaves the environment out there. Not run on macOS.
  • Checked on Windows with cargo test (default features and parallel,compile-commands), clippy for the Windows and x86_64-unknown-linux-gnu targets and rustdoc with -D warnings.

@waltuov

waltuov commented Nov 11, 2026

Copy link
Copy Markdown
Contributor Author

CI is red for an unrelated reason. Test (linux32) fails in "Install g++-multilib" before anything is built, because the runner can't resolve api.launchpad.net, and fail-fast cancels the rest of the matrix. The Ubuntu archive hosts fail to resolve on a runner the same way right now, and #2097's run stopped at the same step earlier today. I'll push to re-run once that recovers.

…ment

Since cc sets the whole environment on the commands it makes, the Debug
output of the Command that Tool::to_command, get_archiver and get_ranlib
return lists every variable and its value on Unix. Build scripts that print
such a command put the environment, secrets included, into build output
that Cargo keeps under target/.

ToolCommand owns the Command and derefs to it, so it is set up and run the
same way, and its Debug output shows only the program and the arguments,
like cc's own running: lines. Tool::command, Build::get_archiver_command,
Build::try_get_archiver_command, Build::get_ranlib_command and
Build::try_get_ranlib_command return it. The existing functions keep
returning the same Command as before, and their docs point to the new ones.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant