Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
RalfJung opened this issue Aug 22, 2017 · 35 comments
Labels
A-build-scripts Area: build.rs scripts A-cross-compiling Area: using --target flag for other platforms A-rustflags Area: rustflags C-enhancement Category: enhancement S-needs-design Status: Needs someone to work further on the design for the feature or fix. NOT YET accepted.

Comments

@RalfJung
Copy link
Member

RalfJung commented Aug 22, 2017

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.

@alexcrichton
Copy link
Member

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

@RalfJung
Copy link
Member Author

RalfJung commented 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!

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.

@RalfJung
Copy link
Member Author

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?

@alexcrichton
Copy link
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!

@RalfJung
Copy link
Member Author

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.)

@RalfJung
Copy link
Member Author

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
Copy link
Contributor

gnzlbg commented May 4, 2018

@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 ...

@alexcrichton
Copy link
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

@gnzlbg
Copy link
Contributor

gnzlbg commented May 5, 2018 via email

@alexcrichton
Copy link
Member

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

@rbalicki2
Copy link

rbalicki2 commented 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
Copy link
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.

@rbalicki2
Copy link

Thanks! I will try that.

@mbrubeck
Copy link
Contributor

mbrubeck commented Jul 23, 2018

@Boscop noticed that build.rustflags being passed to build scripts when --target omitted, but not when it is passed, causes extra rebuilds when building for multiple targets.

For example, if you have build.rustflags set in .cargo/config, then running cargo build; cargo build --target $OTHER; cargo build will cause all build scripts (and anything that depends on them) to be rebuilt every time. In practice this can mean that the third build command rebuilds nearly the entire dependency tree, even though everything should be cached from the first build.

A workaround is to run cargo build --target $host when not cross-compiling.

@Boscop
Copy link

Boscop commented Jul 23, 2018

[...] will cause all build scripts (and anything that depends on them) to be rebuilt.

(proc-macros and their deps, too.)

A workaround is to run cargo build --target $host when not cross-compiling.

That avoids the rebuild issue but it seems the rustflags aren't getting applied, so it's not really a working workaround. The resulting exe still links to the crt dynamically when I have this .cargo/config (so I guess the target-cpu setting is also ignored):

[build] 
target = "x86_64-pc-windows-msvc" 

[target.'cfg(all(target_arch = "x86_64", not(debug_assertions)))'] 
rustflags = ["-Ctarget-feature=+crt-static", "-Ctarget-cpu=haswell"]

So it has the same effect as no rustflags setting, that's why it "fixes" the rebuild issue.

Due to this (#5777) issue it doesn't seem to be possible right now to set different rustflags in release mode, so I switched to setting them in my justfile as env var in my release build recipe for now..

@mbrubeck
Copy link
Contributor

The workaround also works for cases where rustflags are applied, e.g.

[build] 
target = "x86_64-pc-windows-msvc" 

[target.'cfg(target_arch = "x86_64")']
rustflags = ["-Ctarget-feature=+crt-static", "-Ctarget-cpu=haswell"]

The fact that there's no way to set rustflags for release builds only is a separate issue, as you note, but the workaround prevents rebuilds whether you hit that bug or not.

@Boscop
Copy link

Boscop commented Jul 25, 2018

Ah yes, originally I wanted to use those rust flags only for release, but I guess it's ok if they are also used for debug, but do they make the build take longer?

But for this use case I'd prefer to build my release build into target/release instead of a different folder and I had a justfile for post build packaging anyway so I'm keeping the env var solution for now.
(But I'm keeping the workaround in mind for the future.. )

I hope it will soon be possible to use different rustflags for different profiles..

jschwe added a commit to corrosion-rs/corrosion that referenced this issue Apr 12, 2022
For Rust targets, Rust will invoke the linker and add (among others) the
 -lSystem flag. This works fine for the default linker, but if we use a
  custom linker (e.g. g++, or even /usr/bin/ld), then we must provide
  that path to the System library.

Adding -L<path> to RUSTFLAGS works fine for regular libraries, but fails
 for build-scripts (See rust-lang/cargo#4423 )
because there is no way to set RUSTFLAGS for build scripts when
cross-compiling. This is mostly an issue because we always specify the
target, even when building for host, but that is already sufficient for
the RUSTFLAGS to not apply to the build-script-build.

Target specific RUSTFLAGS (i.e CARGO_TARGET_<triple>_RUSTFLAGS also
don't seem to affect buildscripts.

Setting LIBRARY_PATH=<path> applies to normal targets and build-scripts
too, and seems to fix the issue.

My previous fix probably only fixed the case where a rust library gets
linked into a foreign library or executable.
jschwe added a commit to corrosion-rs/corrosion that referenced this issue Apr 12, 2022
For Rust targets, Rust will invoke the linker and add (among others) the
 -lSystem flag. This works fine for the default linker, but if we use a
  custom linker (e.g. g++, or even /usr/bin/ld), then we must provide
  that path to the System library.

Adding -L<path> to RUSTFLAGS works fine for regular libraries, but fails
 for build-scripts (See rust-lang/cargo#4423 )
because there is no way to set RUSTFLAGS for build scripts when
cross-compiling. This is mostly an issue because we always specify the
target, even when building for host, but that is already sufficient for
the RUSTFLAGS to not apply to the build-script-build.

Target specific RUSTFLAGS (i.e CARGO_TARGET_<triple>_RUSTFLAGS also
don't seem to affect buildscripts.

Setting LIBRARY_PATH=<path> applies to normal targets and build-scripts
too, and seems to fix the issue.

My previous fix probably only fixed the case where a rust library gets
linked into a foreign library or executable.
Shnatsel added a commit to rust-secure-code/cargo-auditable that referenced this issue Jul 30, 2022
tatsuya6502 added a commit to moka-rs/moka that referenced this issue Oct 24, 2022
… nightly

To workaround the following Cargo issue with cross compiling, force enable
rustc_version crate when testing Linux corss targets:
rust-lang/cargo#4423
tarcieri added a commit to dalek-cryptography/curve25519-dalek that referenced this issue Nov 14, 2022
The `RUSTFLAGS` were getting applied to build scripts, which caused them
to crash with SIGILL.

According to this issue, RUSTFLAGS won't be applied to build scripts
when cross-compiling by passing the `--target` attribute:

rust-lang/cargo#4423

This attempts to work around the problem by explicitly passing:

    --target x86_64-unknown-linux-gnu
rozbb pushed a commit to dalek-cryptography/curve25519-dalek that referenced this issue Nov 14, 2022
The `RUSTFLAGS` were getting applied to build scripts, which caused them
to crash with SIGILL.

According to this issue, RUSTFLAGS won't be applied to build scripts
when cross-compiling by passing the `--target` attribute:

rust-lang/cargo#4423

This attempts to work around the problem by explicitly passing:

    --target x86_64-unknown-linux-gnu
twistedfall added a commit to twistedfall/opencv-rust that referenced this issue May 9, 2023
It's meant to be only applied to the final artifacts, so it's problematic during cross-compile: rust-lang/cargo#4423
@epage epage added the S-needs-design Status: Needs someone to work further on the design for the feature or fix. NOT YET accepted. label Oct 17, 2023
@RalfJung RalfJung changed the title There is no way to set RUSTFLAGS for build scripts when cross-compiling There is no way to set RUSTFLAGS for build scripts (or proc macros) when cross-compiling Nov 5, 2024
@RalfJung
Copy link
Member Author

RalfJung commented Nov 5, 2024

Cargo already supports CARGO_TARGET__RUSTFLAGS

We see conflicting information regarding this env var above...

Could some check and confirm what the actual current behavior is here?

@RalfJung
Copy link
Member Author

RalfJung commented Nov 5, 2024

https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#target-applies-to-host seems to confirm that target.x86_64-unknown-linux-musl.rustflags and the corresponding env var do indeed not work.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-build-scripts Area: build.rs scripts A-cross-compiling Area: using --target flag for other platforms A-rustflags Area: rustflags C-enhancement Category: enhancement S-needs-design Status: Needs someone to work further on the design for the feature or fix. NOT YET accepted.
Projects
None yet
Development

Successfully merging a pull request may close this issue.