Skip to content

$CARGO passed to external subcommands can point to the wrong cargo if already set #15099

@Alexendoo

Description

@Alexendoo
Member

Problem

Observed in rust-lang/rust-clippy#14045, if cargo +nightly clippy is ran by a process with CARGO pointing to the stable cargo then nightly clippy-driver will be ran by stable cargo

In this case that resulted in a confusing warning

Steps

  1. Put the following into your PATH as cargo-foo

    #!/usr/bin/env bash
    
    echo "$CARGO"
  2. With stable cargo use cargo run on

    use std::process::Command;
    
    fn main() {
        Command::new("cargo")
            .args(["+nightly", "foo"])
            .status()
            .unwrap();
    }
  3. The output will be .../.rustup/toolchains/stable-.../bin/cargo

Possible Solution(s)

Looks to be due to #11285, perhaps there could be an exception for when the current exe really is cargo

Notes

No response

Version

cargo 1.84.0 (66221abde 2024-11-19)

Activity

added
C-bugCategory: bug
S-triageStatus: This issue is waiting on initial triage.
on Jan 24, 2025
weihanglo

weihanglo commented on Jan 26, 2025

@weihanglo
Member

perhaps there could be an exception for when the current exe really is cargo

How to achieve that? I mean isn't it be like a revert of #11285?

weihanglo

weihanglo commented on Jan 26, 2025

@weihanglo
Member

Feel like one remote cause is the cargo-clippy is, while "official", still an external command, so need to rely on $CARGO.

Alexendoo

Alexendoo commented on Jan 26, 2025

@Alexendoo
MemberAuthor

How to achieve that? I mean isn't it be like a revert of #11285?

Looking at just #10119 Cargo the binary could set a flag that disables the forwarding, that would keep #11285 working for commands that use Cargo as a library

For #10113 though I'm not sure

smoelius

smoelius commented on Jan 26, 2025

@smoelius
Contributor

If one invokes cargo +nightly <anything>, it's pretty clear one wants to run the cargo proxy because of the +nightly.

Are there cases where one would want the cargo proxy to preserve the CARGO environment variable? I'm wondering if the proxy should clear it.

If I'm reading #10119 right, that issue is about using Cargo as a library. So I don't think clearing the CARGO environment variable in the proxy would impact that issue's fix.

smoelius

smoelius commented on Jan 28, 2025

@smoelius
Contributor

I'm going to boldly assume #15099 (comment) is not crazy and cc some rustup maintainers. @ChrisDenton @djc @rami3l

My question comes down to: should the cargo proxy clear the CARGO environment variable? Or are there legitimate reasons for the proxy to preserve the environment variable?

For completeness, @Alexendoo poses a different solution in #15099 (comment).

rami3l

rami3l commented on Jan 29, 2025

@rami3l
Member

@smoelius Hmmm there doesn't seem to be any mention of $CARGO in our codebase so I might assume it's currently not handled at all. Where is that variable usually read then? Maybe it's better to handle this logic over there if possible? (Otherwise I imagine it'd be difficult for rustup-less setups.)

djc

djc commented on Jan 29, 2025

@djc
Contributor

I think we should fail the invocation if the +-specified toolchain and $CARGO don't agree -- seems like PEBKAC to me.

smoelius

smoelius commented on Jan 29, 2025

@smoelius
Contributor

@rami3l

@smoelius Hmmm there doesn't seem to be any mention of $CARGO in our codebase so I might assume it's currently not handled at all.

That the variable is not handled at all (by the proxy) is my takeaway as well.

Where is that variable usually read then?

It looks like it is read by many tools: https://github.com/search?q=var%28%22CARGO%22%29&type=code

This specific issue arose because Clippy reads the variable: https://github.com/rust-lang/rust-clippy/blob/e02c8857e83e9113c29e8bd2b429f62dfaba04a7/src/main.rs#L110

Maybe it's better to handle this logic over there if possible?

Please see my response to djc below.

(Otherwise I imagine it'd be difficult for rustup-less setups.)

I may be misunderstanding what you mean. But I think the issue is specifically about cargo +<toolchain> invocations, which implies rustup setups.


@djc

I think we should fail the invocation if the +-specified toolchain and $CARGO don't agree

I think failing the invocation would be better than the current situation (i.e., allowing the command to proceed with the wrong cargo).

seems like PEBKAC to me.

In fairness, the problem is subtle. As @Alexendoo explains in rust-lang/rust-clippy#14045 (comment):

  • The cargo proxy runs, selects the default cargo, and sets the CARGO environment variable to default toolchain's cargo.
  • That cargo invocations runs some other code, which invokes cargo +<toolchain> <subcommand>.
  • <subcommand> reads the CARGO environment variable and runs the default toolchain's cargo rather than <toolchain>'s cargo.

@rami3l Your question ("Maybe it's better to handle this logic over there if possible?") is fair, and may be the right answer. But please notice that <subcommand> above could be just about anything.


Thank you both for your responses. It looks like there are currently two options:

  • Clear the CARGO environment variable in the proxy.
  • Fail the invocation when the +-specified toolchain and $CARGO don't agree.

Are there other options? Do folks have strong opinions regarding these two?

djc

djc commented on Jan 29, 2025

@djc
Contributor

I suppose the question is if there are good use cases for having a CARGO set which you want to override using the +toolchain. Having rustup decide to prioritize the +toolchain over the environment-specified one feels a little opinionated which I tend to dislike, but on the other hand explicit overrides taking priority over environment-specified ones is usually sane.

rami3l

rami3l commented on Jan 29, 2025

@rami3l
Member

@smoelius Thanks a lot for your clarification! This looks like a cargo-calling-rustup issue so indeed it's specific to rustup.

And, from my perspective, it looks like rustup needs to start following some external convention. If it's already established somewhere and our not responding (such as sending a warning instead of actually doing anything) is not an option, I guess we would have to add more magic (such as wiping $CARGO) following the current direction, because, as a user, I have no idea why calling cargo in cargo run would make things difficult for rustup, so I expect everything to "just work" as if cargo run were not setting $CARGO at all.

@weihanglo This is a bit off-topic, but recursive calls are also exactly why it's so difficult for me to add a proper lock to rustup in rust-lang/rustup#988, where a common locking scheme can also be broken by considering rustup calls in cargo run . Given all this, I'm afraid to open a can of worms trying to fix all the edge cases related to recursive calls while keeping rustup mostly transparent to our end users. Maybe we should be less transparent and take @djc's approach, concluding that it's up to the user to prevent such complexities?

14 remaining items

smoelius

smoelius commented on Feb 5, 2025

@smoelius
Contributor

What if we only emitted a warning, but the message said something like:

warn: In a future version of the cargo proxy, 'CARGO' will be cleared.

Would that be acceptable to everyone?

smoelius

smoelius commented on Feb 6, 2025

@smoelius
Contributor

I updated the PR, though I softened the language from "will be cleared" to "may be cleared".

epage

epage commented on Feb 18, 2025

@epage
Contributor

So I'm working to catch up on this.

It sounds like there are the following care abouts

  1. Subprocesses of Cargo should inherit the same toolchain as the their parent Cargo process via CARGO
  2. Bins using cargo-as-a-library should not be put in CARGO (CARGO is not Cargo when build script is invoked through cargo used as a library #10119)
  3. Ld can mess with CURRENT_EXE (among other things) (CARGO env var set incorrectly when Cargo invoked through ld #10113)

#11285 broke the (1) to fix (2) and (3). It did so by setting the precedence in GlobalContext to

  1. from_env
  2. from_current_exe
  3. from_argv

I'm uncomfortable with a rustup solution because this isn't inherently a rustup problem This can happen in other cases like cargo script invoked via rustup spawning the deb build process which spawns Debian-built cargo which spawns CARGO, wanting Debian's cargo and not rustups.

In the Cargo team meeting, someone brought up users manually unsetting CARGO but I worry about how error prone that is and how difficult to debug.

Building off of the idea in #15099 (comment), my proposed solution is that cargo-the-bin opts-in to different behavior than the default.

default-behavior (when using cargo-the-library):

  1. from_env
  2. from_current_exe
  3. from_argv

opt-in behavior (set by cargo-the-bin):

  1. from_current_exe if its cargo[EXE_SUFFIX]
  2. from_argv if its cargo[EXE_SUFFIX]
  3. from_env
  4. from_current_exe
  5. from_argv

Characteristics

  • No change in behavior for cargo-the-library
  • We don't try set CARGO=ld
  • We don't fix this issue for ld but at least we still work
  • We don't fix this issue for when users have an alternative name for the Cargo binary (or on some platforms, an alternative symlink name)
  • We fix this issue for regular use of cargo
smoelius

smoelius commented on Feb 18, 2025

@smoelius
Contributor

@epage Thank you very much for your reply.

Just to be sure I understand:

opt-in behavior (set by cargo-the-bin):

  1. from_current_exe if its cargo[EXE_SUFFIX]
  2. from_argv if its cargo[EXE_SUFFIX]
  3. from_env
  4. from_current_exe
  5. from_argv

You're saying cargo-the-bin should set the CARGO environment variable based on these rules?

So, for example, if nightly cargo-the-bin is run and finds that CARGO is already set (say, to stable cargo-the-bin), it will nonetheless set CARGO because the current exe satisfies condition 1?

Should a warning be issued in that case, or should the override happen silently?

epage

epage commented on Feb 18, 2025

@epage
Contributor

imo overriding CARGO would be intended behavior and wouldn't be reasonable to warn about.

Cargo also tends to avoid warnings that can't be reasonably silenced. We hope to one day have #12235 resolved but that will be project focused and not invocation focused. We do not have plans atm for invocation-focused lint control.

weihanglo

weihanglo commented on Feb 28, 2025

@weihanglo
Member

Fixed via #15208

azat

azat commented on Mar 18, 2025

@azat

After #15208 it is not possible to override CARGO for build.rs, right?

The context is as follow - I was trying to force cbindgen (who calls cargo metadata via build.rs) to use cargo --offline, directly it is not supported yet (mozilla/cbindgen#630), and after #15208 it is not possible, I've tried the following:

  • putting a script cargo and place /usr/bin/cargo --offline "$@" there and putting it into PATH first
    • PATH=/path/to/wrapper:$PATH CARGO=/path/to/wrapper/cargo cargo
  • cargo --config build.cargo=/path/to/cargo-wrapper
  • cargo --config env.CARGO=/path/to/cargo-wrapper

Is it possible to support i.e. build.cargo (same as rustc) ?

weihanglo

weihanglo commented on Mar 18, 2025

@weihanglo
Member

@azat, feel free to open a new issue if there is no pre-existing one (I am not aware of any).

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-custom-subcommandsArea: custom 3rd party subcommand pluginsA-environment-variablesArea: environment variablesC-bugCategory: bugS-needs-team-inputStatus: Needs input from team on whether/how to proceed.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Participants

      @epage@djc@azat@Alexendoo@weihanglo

      Issue actions

        `$CARGO` passed to external subcommands can point to the wrong `cargo` if already set · Issue #15099 · rust-lang/cargo