Skip to content

There is no way to set RUSTFLAGS for build scripts (or proc macros) when cross-compiling #4423

Open

Description

@RalfJung

View all comments

The cargo documentation environment-variables.md says

RUSTFLAGS - A space-separated list of custom flags to pass to all compiler invocations that Cargo performs. In contrast with cargo rustc, this is useful for passing a flag to all compiler instances.

This is wrong, unfortunately. The flag is not passed to all compiler instances: When cross-compiling (i.e., when --target is set), the flag is not passed to build scripts. It seems there is currently no way to pass anything to build scripts when --target is set, which clearly is a feature gap. Cargo should provide a way to set flags for build scripts even when cross-compiling.

One may be tempted to set CARGO_TARGET_<target>_RUSTFLAGS or target.<target>.rustflags, but that does not work either.

Also see #9453, #9452.

Activity

  1. alexcrichton commented on Aug 22, 2017

    @alexcrichton
    Member

    This is currently intentional as it is the conservative choice of how to interpret RUSTFLAGS, but it's always possible to expand it!

  2. RalfJung commented on Aug 22, 2017

    @RalfJung
    MemberAuthor

    This is currently intentional as it is the conservative choice of how to interpret RUSTFLAGS, but it's always possible to expand it!

    So the fact that whether RUSTFLAGS applies to build scripts or not depends on whether --target was set or not is how things ought to be? That's a really surprising side-effect of setting a target! The option should be called --target-and-ignore-rustflags-for-buildscripts, then... ;)
    To make matters worse, this behavior is actually stable and hence can probably never be changed :(

    Actually the code says this is just a hack:

        // We *want* to apply RUSTFLAGS only to builds for the
        // requested target architecture, and not to things like build
        // scripts and plugins, which may be for an entirely different
        // architecture. Cargo's present architecture makes it quite
        // hard to only apply flags to things that are not build
        // scripts and plugins though, so we do something more hacky
        // instead to avoid applying the same RUSTFLAGS to multiple targets
        // arches:
    

    It seems that what we really want is two environment variables; one applied to plugins/build scripts and one applied to the "actual" code. (And maybe a third applied to both.) However, that's currently not easy to do because the function setting the flags has no idea whether this is for the host or target. Correct? That's what this comment sounds like.

    Judging from trouble that xargo has wrt. plugins, I assume the problem is that when I just run cargo build, the kind is actually Kind::Host for everything? IOW, cargo build and cargo build --target $host_arch are very different. Not sure why this is, sounds like a bug to me.

    One "easy" fix for this problem would be to double-down on the existing hack, and change env_args such that rather than not passing any arguments when it detects a build script in a cross-build, it just goes looking for a different environment variable, like RUSTFLAGS_HOST or so. That would be enough to solve my problem, but I'm not sure if you'd be willing to accept such a patch -- and anyway, it'd all still be a hack.
    (My problem is described in japaric/xargo#162: I want a way to run cargo build --target <...> such that all compiler invocations have --cap-lint=allow set.)

    The alternative I guess is to fix cargo such that kind actually reliably says whether this is a build script or part of the "real" build. Judging from the fact that nobody did this so far, I expect that to be non-trivial, and there may also be good reasons for why things are the way they are right now.

  3. RalfJung commented on Aug 22, 2017

    @RalfJung
    MemberAuthor

    Browsing through the code, I have found other code using Target::for_host to distinguish builds for plugins from builds for the target. What is the reason that env_args relies on a different signal?

  4. alexcrichton commented on Aug 23, 2017

    @alexcrichton
    Member

    er just to be clear, none of this behavior is a bug today, it's all intentional. RUSTFLAGS is intended to only apply to the "final artifact" which when cross compiling doesn't account for build scripts and such. We left ourselves room to grow other environment variables, though, so we can certainly add some more!

  5. RalfJung commented on Aug 23, 2017

    @RalfJung
    MemberAuthor

    RUSTFLAGS is intended to only apply to the "final artifact" which when cross compiling doesn't account for build scripts and such

    But that's not what happens. If I run just cargo build, RUSTFLAGS is applied to build scripts and the artifact alike.
    The entire behavior in cross-compilation vs. "native" compilation mode is pretty inconsistent. Also see the xargo README which observes that

    When using compiler plugins (e.g. serde_derive) the target triple must be provided even when compiling for the host platform due to the way cargo handles compiler plugins. I.e., use xargo build --target x86_64-unknown-linux-gnu instead of just xargo build. (Surprisingly, these commands are NOT the same to Cargo.)

  6. RalfJung commented on Aug 23, 2017

    @RalfJung
    MemberAuthor

    Well anyway I submitted #4428 which tries to not touch this existing logic, but is just enough to fix my problem. Some other day I'll try to distill my trouble with this logic into a separate bugreport. ;)

  7. gnzlbg commented on May 4, 2018

    @gnzlbg
    Contributor

    @alexcrichton

    RUSTFLAGS is intended to only apply to the "final artifact" which when cross compiling doesn't account for build scripts and such.

    Why does it account for build scripts and plugins when not cross compiling?

    Cross-compiling to the host and compiling to the host are the same thing, therefore I think that cargo build and cargo build --target=$host_arch should be equivalent.

    I find the current behavior of RUSTFLAGS when --target is used to be sane, so I wouldn't mind if that behavior would apply to cargo build as well.


    @RalfJung

    It seems that what we really want is two environment variables; one applied to plugins/build scripts and one applied to the "actual" code.

    We can keep RUSTFLAGS to apply to the code of the "target", and add RUSTFLAGS_BUILD_DEPENDENCIES to apply to all build dependencies like crates, plugins, build.rs, ...

    EDIT: an alternative is to allow finer grained rustflags RUSTFLAGS_BACKTRACE_V_1_0_1_BUILD_RS to apply only to the build.rs of the dependency backtrace version 1.0.1 ...

  8. alexcrichton commented on May 5, 2018

    @alexcrichton
    Member

    @gnzlbg for historical reasons cargo build is subtly different from cargo build --target $host, but it does indeed cause a lot of confusion and was likely a bad decision

  9. gnzlbg commented on May 5, 2018

    @gnzlbg
    Contributor
  10. alexcrichton commented on May 5, 2018

    @alexcrichton
    Member

    Perhaps! No matter what though it needs to be a smooth breaking change, if any.

  11. rbalicki2 commented on Jul 11, 2018

    @rbalicki2

    Could someone clarify if the following is impossible given the above decision?

    I have a dependency which relies on proc_macro2 being compiled with RUSTFLAGS='--cfg procmacro2_semver_exempt', as it uses span().start() and span().end(). I want to use this procedural macro in another library which targets wasm32-unknown-unknown.

    I'd like to run RUSTFLAGS='--cfg procmacro2_semver_exempt --target wasm32-unknown-unknown' cargo +nightly build --verbose, but of course that doesn't work as the RUSTFLAGS is dropped.

    I am open to doing things like: forking proc_macro2 (seems easiest), or manually listing out all of the rustc commands I need to execute (this seems possible, but I tried to execute all of the rustc commands printed when I ran cargo build --verbose, but it did not work. So I am presumably missing something.)

    Thank you for your help in advance!

  12. alexcrichton commented on Jul 11, 2018

    @alexcrichton
    Member

    @rbalicki2 hm I think that may be accidental poor planning on our part! Currently if you're cross compiling you'll want to configure CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUSTFLAGS=... as an environment variable or configure the [target.x86_64-unknown-linux-gnu.rustflags] key in .cargo/config.

  13. 55 remaining items

  14. added 3 commits that reference this issue on Nov 8, 2025
  15. added a commit that references this issue on Aug 31, 2026
  16. added a commit that references this issue on Sep 7, 2026
  17. tgross35 commented on Oct 27, 2026

    @tgross35
    Member

    I think there could be room for somebody to submit an RFC to fix this in the next edition (I'd like to write one myself but unfortunately don't think I have the availability to see it through). Rough sketch of something that may work:

    1. When edition 2027+ is enabled in a manifest, the following rules apply.
    2. RUSTFLAGS apply only to output artifacts, i.e. things that become a part of the final library or binary target.
    3. BUILD_RUSTFLAGS apply only to artifacts that are meant to be run on the host as a step during the build, i.e. build scripts and proc macros. Also includes build scripts for proc macros.
    4. GLOBAL_RUSTFLAGS, if set, is prepended to both of those. (This one is optional, but convenient if the rustflags are just going to be repeated.)
    5. Whether or not --target is passed does not change any of the above.
    6. The existing hierarchy remains unchanged (target-specific env > non-specific env > target-specific config file entry > non-specific config file entry).
    7. Target-specific versions of these flags exist as e.g. CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUSTFLAGS, CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_BUILD_RUSTFLAGS, CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_GLOBAL_RUSTFLAGS. These examples apply when building for the target x86_64-unknown-linux-gnu, regardless of what the host is.

    The behavior delta from the status quo is that RUSTFLAGS now never applies to build scripts or proc macros, and passing --target or not does not change this. BUILD_RUSTFLAGS and GLOBAL_RUSTFLAGS are slightly orthogonal and exist as ways to fill in these gaps.

    Some motivation:

    1. Current behavior is surprising, specifying --target <host triple> should make no difference.
    2. There is no way to set build script rustflags when cross compiling, which is useful (simple example: lint config).
    3. There is no way to set flags only for the artifact that don't need to apply to build scripts.

    Possible complications:

    1. Should proc macros and build scripts always be tied together?
    2. Does this complicate things since deps with different editions now interpret RUSTFLAGS differently? Or should the edition of the top-level crate control how RUSTFLAGS is treated in dependents?
    3. Should opting in/out of this behavior a Cargo config? It does feel a bit weird to tie to a crate's edition. However, this comes with the big downside that many users won't ever get the more logical behavior unless they go out of their way to set a config.
    4. Should this coordinate with the cc crate somehow? BUILD_CFLAGS et al. could work for build scripts that need to build a program on the host separately from the target.
    5. Is there a need for a BUILD_RUSTC? This would be inconsistent with the proposal if RUSTC provides a default
    6. How is this eventually going to interact with build-std? std sometimes needs different flags. (There may be a better answer already in the build-std RFC series. It may also be fine to say that answering this is out of scope.)
    7. What other things have the surprise --target behavior mentioned at There is no way to set RUSTFLAGS for build scripts (or proc macros) when cross-compiling #4423 (comment)?
  18. tgross35 commented on Oct 27, 2026

    @tgross35
    Member

    @rustbot label +I-nominated-to-discuss

    Nominating for @rust-lang/cargo since I think it would be good to have an idea of the direction the team is interested in. My comment above is one proposal, but there are other options.

  19. RalfJung commented on Oct 27, 2026

    @RalfJung
    MemberAuthor

    Whether or not --target is passed does not change any of the above.

    So this proposal also implies that we no longer share dependencies between proc macros and the output artifact? Currently such shared dependencies are built only once (when --target is not set), but then it's not clear which flags should be used for that.

    IOW, you are not just suggesting to change which flags get passed. You are suggesting a change in how often rustc is even invoked on which crates.

    BUILD_RUSTFLAGS apply only to artifacts that are meant to be run on the host as a step during the build, i.e. build scripts and proc macros. Also includes build scripts for proc macros.

    My preferred name would be HOST_RUSTFLAGS. That seems more consistent with the host section of cargo's config.

  20. tgross35 commented on Oct 27, 2026

    @tgross35
    Member

    Whether or not --target is passed does not change any of the above.

    So this proposal also implies that we no longer share dependencies between proc macros and the output artifact? Currently such shared dependencies are built only once (when --target is not set), but then it's not clear which flags should be used for that.
    IOW, you are not just suggesting to change which flags get passed. You are suggesting a change in how often rustc is even invoked on which crates.

    Naively yes, though Cargo should be able to deduplicate the invocations if both are built with the same flags. It does mean that having RUSTFLAGS set and doing the edition upgrade means you'll be getting things built twice. There has to be some kind of tradeoff here, this one can be worked around by setting BUILD_RUSTFLAGS the same if that's desirable.

    The opposite could also work: make RUSTFLAGS the env that applies to everything, across all targets (still eliminate the "--target surprise"), then introduce new BUILD_RUSTFLAGS and TARGET_RUSTFLAGS for controlling only those things. The tradeoff here is that RUSTFLAGS that might be intended for a target will start applying to the host, so users need to migrate somehow.

    BUILD_RUSTFLAGS apply only to artifacts that are meant to be run on the host as a step during the build, i.e. build scripts and proc macros. Also includes build scripts for proc macros.

    My preferred name would be HOST_RUSTFLAGS. That seems more consistent with the host section of cargo's config.

    Personally I find this terminology slightly confusing (in cc as well) because it's not clear whether HOST_* flags get applied to the target when it's the same as the host. But I could live with either, and the consistency is a good point.

  21. epage commented on Oct 27, 2026

    @epage
    Contributor

    RUSTFLAGS is a form of configuration which makes it tricky to apply editions to because it is outside of the manifest where the edition is set. The closest we've come is having a config field from the start have its default be defined by workspace.resolver which may be defaulted by package.edition.

  22. epage commented on Oct 27, 2026

    @epage
    Contributor

    Something I'm not seeing in this thread is use cases (sorry if I missed them). What are people trying to accomplish when setting which RUSTFLAGS on build scripts and proc macros?

    Knowing this can help with understanding the priority and shape the design.

  23. RalfJung commented on Oct 27, 2026

    @RalfJung
    MemberAuthor

    Same as for RUSTFLAGS -- to configure the sysroot, to control lints, to set up --cfg, to configure the linker, ...

  24. tgross35 commented on Oct 27, 2026

    @tgross35
    Member

    I think this is also at least part of the reason bootstrap needs a rustc shim.

    There is some principle of least astonishment violation that whether or not --target <host> is passed means your build behaves differently in the same environment. I have tripped over this more than once. (That flag is often set by scripts but not as much by humans.)

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-build-scriptsArea: build.rs scriptsA-cross-compilingArea: using --target flag for other platformsA-rustflagsArea: rustflagsC-enhancementCategory: enhancementI-nominated-to-discussTo be discussed during issue triage on the next Cargo team meetingS-needs-designStatus: Needs someone to work further on the design for the feature or fix. NOT YET accepted.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions