You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
There is no way to set RUSTFLAGS for build scripts (or proc macros) when cross-compiling #4423
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.
Reacted by GameKyuubi, LunNova, Robert Balicki, Behnam Esfahbod, Aymeric Beaumet, yuri, Esteban Kuber, Sander Maijers, William A. Kennington III, Chris Beck and 23 more
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.
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?
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!
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.)
Reacted by LunNova, Rasmus Thomsen, Sander Maijers, Chris Beck, Jay Freeman (saurik), Matthew James Briggs, Jon Gjengset and Sergey "Shnatsel" Davidoff
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. ;)
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.
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 ...
@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
Could something be done about it in the 2018 edition? Probably would need
an RFC, but that’s only worth writing if this can be technically changed
without “too much hassle”.
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.)
@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.
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:
When edition 2027+ is enabled in a manifest, the following rules apply.
RUSTFLAGS apply only to output artifacts, i.e. things that become a part of the final library or binary target.
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.
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.)
Whether or not --target is passed does not change any of the above.
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:
Current behavior is surprising, specifying --target <host triple> should make no difference.
There is no way to set build script rustflags when cross compiling, which is useful (simple example: lint config).
There is no way to set flags only for the artifact that don't need to apply to build scripts.
Possible complications:
Should proc macros and build scripts always be tied together?
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?
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.
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.
Is there a need for a BUILD_RUSTC? This would be inconsistent with the proposal if RUSTC provides a default
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.)
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.
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.
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.
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.
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.
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.)
Activity
alexcrichton commentedon Aug 22, 2017
This is currently intentional as it is the conservative choice of how to interpret
RUSTFLAGS, but it's always possible to expand it!RalfJung commentedon Aug 22, 2017
So the fact that whether
RUSTFLAGSapplies to build scripts or not depends on whether--targetwas 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:
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, thekindis actuallyKind::Hostfor everything? IOW,cargo buildandcargo build --target $host_archare 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_argssuch 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, likeRUSTFLAGS_HOSTor 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=allowset.)The alternative I guess is to fix cargo such that
kindactually 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.RalfJung commentedon Aug 22, 2017
Browsing through the code, I have found other code using
Target::for_hostto distinguish builds for plugins from builds for the target. What is the reason thatenv_argsrelies on a different signal?alexcrichton commentedon Aug 23, 2017
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!
RalfJung commentedon Aug 23, 2017
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
RalfJung commentedon Aug 23, 2017
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. ;)
gnzlbg commentedon May 4, 2018
@alexcrichton
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 buildandcargo build --target=$host_archshould be equivalent.I find the current behavior of
RUSTFLAGSwhen--targetis used to be sane, so I wouldn't mind if that behavior would apply tocargo buildas well.@RalfJung
We can keep
RUSTFLAGSto apply to the code of the "target", and addRUSTFLAGS_BUILD_DEPENDENCIESto 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_RSto apply only to the build.rs of the dependency backtrace version 1.0.1 ...alexcrichton commentedon May 5, 2018
@gnzlbg for historical reasons
cargo buildis subtly different fromcargo build --target $host, but it does indeed cause a lot of confusion and was likely a bad decisiongnzlbg commentedon May 5, 2018
alexcrichton commentedon May 5, 2018
Perhaps! No matter what though it needs to be a smooth breaking change, if any.
rbalicki2 commentedon Jul 11, 2018
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!
alexcrichton commentedon Jul 11, 2018
@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.55 remaining items
Rollup merge of rust-lang#144027 - RalfJung:clippy, r=Mark-Simulacrum
Rollup merge of rust-lang#144027 - RalfJung:clippy, r=Mark-Simulacrum
Rollup merge of #144027 - RalfJung:clippy, r=Mark-Simulacrum
Rollup merge of #144027 - RalfJung:clippy, r=Mark-Simulacrum
Rollup merge of rust-lang#144027 - RalfJung:clippy, r=Mark-Simulacrum
Rollup merge of rust-lang#144027 - RalfJung:clippy, r=Mark-Simulacrum
tgross35 commentedon Oct 27, 2026
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:
RUSTFLAGSapply only to output artifacts, i.e. things that become a part of the final library or binary target.BUILD_RUSTFLAGSapply 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.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.)--targetis passed does not change any of the above.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
RUSTFLAGSnow never applies to build scripts or proc macros, and passing--targetor not does not change this.BUILD_RUSTFLAGSandGLOBAL_RUSTFLAGSare slightly orthogonal and exist as ways to fill in these gaps.Some motivation:
--target <host triple>should make no difference.Possible complications:
RUSTFLAGSdifferently? Or should the edition of the top-level crate control howRUSTFLAGSis treated in dependents?cccrate somehow?BUILD_CFLAGSet al. could work for build scripts that need to build a program on the host separately from the target.BUILD_RUSTC? This would be inconsistent with the proposal ifRUSTCprovides a defaultstdsometimes 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.)--targetbehavior mentioned at There is no way to set RUSTFLAGS for build scripts (or proc macros) when cross-compiling #4423 (comment)?tgross35 commentedon Oct 27, 2026
@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.
RalfJung commentedon Oct 27, 2026
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
--targetis 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.
My preferred name would be
HOST_RUSTFLAGS. That seems more consistent with thehostsection of cargo's config.tgross35 commentedon Oct 27, 2026
Naively yes, though Cargo should be able to deduplicate the invocations if both are built with the same flags. It does mean that having
RUSTFLAGSset 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 settingBUILD_RUSTFLAGSthe same if that's desirable.The opposite could also work: make
RUSTFLAGSthe env that applies to everything, across all targets (still eliminate the "--targetsurprise"), then introduce newBUILD_RUSTFLAGSandTARGET_RUSTFLAGSfor controlling only those things. The tradeoff here is thatRUSTFLAGSthat might be intended for a target will start applying to the host, so users need to migrate somehow.Personally I find this terminology slightly confusing (in
ccas well) because it's not clear whetherHOST_*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.epage commentedon Oct 27, 2026
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.resolverwhich may be defaulted bypackage.edition.epage commentedon Oct 27, 2026
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.
RalfJung commentedon Oct 27, 2026
Same as for RUSTFLAGS -- to configure the sysroot, to control lints, to set up
--cfg, to configure the linker, ...tgross35 commentedon Oct 27, 2026
I think this is also at least part of the reason bootstrap needs a
rustcshim.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.)