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

Enable propagating host rustflags to build scripts #10395

Merged
merged 15 commits into from
Feb 28, 2022

Conversation

jonhoo
Copy link
Contributor

@jonhoo jonhoo commented Feb 17, 2022

What does this PR try to resolve?

This PR primarily fixes #10206, but in doing so it also slightly modifies the interface for the unstable target-applies-to-host feature (#9453), and adds the unstable target-applies-to-host-kind flag to mirror target-applies-to-host for build scripts and other host artifacts.

The commit messages have more in-depth discussion.

How should we test and review this PR?

The test case from #10206 now works rather than producing an error. It has also been added a regression test case. A few additional test cases have also been added to handle the expected behavior around rustflags for build scripts with and without target-applies-to-host-kind enabled.

Additional information

  1. This changes the interface for target-applies-to-host so that it does not need to be specified twice to be used. And it can still be set through configuration files using the [unstable] table. However, we may(?) want to pick a stable format for in-file configuration of this setting unless we intend for it to only ever be a command-line flag.
  2. It may be that target-applies-to-host-kind is never behavior we want users to turn on, and that it should therefore simply be removed and hard-coded as being false.
  3. It's not entirely clear how target-applies-to-host-kind should interact with -Zmultitarget. If, for example, requested_kinds = [HostTarget, SomeOtherTarget] and kind.is_host(), should RUSTFLAGS take effect or not? For the time being I've just hard-coded the behavior for single targets, and the answer would be "no".

@rust-highfive
Copy link

r? @alexcrichton

(rust-highfive has picked a reviewer for you, use r? to override)

@jonhoo
Copy link
Contributor Author

jonhoo commented Feb 17, 2022

PR failures are due to new clap 3.1.0 deprecations combined with -Dwarnings. Fixing separately in #10396.

Jon Gjengset added 2 commits February 17, 2022 10:31
The tests in question all tail to even build the build script, so the
contents of `main` don't matter, and make the tests seem more complex
than they really are.
@jonhoo
Copy link
Contributor Author

jonhoo commented Feb 17, 2022

/cc @jameshilliard too since you contributed the original feature :)

@jameshilliard
Copy link
Contributor

  1. This changes the interface for target-applies-to-host so that it does not need to be specified twice to be used. And it can still be set through configuration files using the [unstable] table. However, we may(?) want to pick a stable format for in-file configuration of this setting unless we intend for it to only ever be a command-line flag.

Why did you remove the stable format? The only reason you have to specify target-applies-to-host twice is because the nightly feature is not yet stabilized.

@jameshilliard
Copy link
Contributor

3. It's not entirely clear how target-applies-to-host-kind should interact with -Zmultitarget. If, for example, requested_kinds = [HostTarget, SomeOtherTarget] and kind.is_host(), should RUSTFLAGS take effect or not? For the time being I've just hard-coded the behavior for single targets, and the answer would be "no".

Target configs are always triple specific, so setting the config flag target-applies-to-host = true is just intended to use/apply the target specific triple configs as if they were also host triple specific configs, the idea is you would basically get equivalent behavior by copying the target triple configs manually as host triple configs(this is effectively cargo's current default behavior). Setting target-applies-to-host = true should never apply a target triple config unless the host triple being built matches the target triple config.

Copy link
Contributor

@jameshilliard jameshilliard left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's intentional that target-applies-to-host is a top level configuration setting as we need that for many cross compiling scenarios since we want to be able to unconditionally set target-applies-to-host = false for all targets being built. We really don't want the target-applies-to-host = false behavior to be gated behind a nightly config option as doing so forces us to set hacks like __CARGO_TEST_CHANNEL_OVERRIDE_DO_NOT_USE_THIS="nightly" when cross compiling with a stable toolchain. The reason for the stable flag as opposed to just changing the default is to allow for gradual transition away from the broken target-applies-to-host = true behavior without causing issues for users that currently depend on that. There's potentially some users that may want to retain target-applies-to-host = true behavior as it would allow for smaller configs in the event they want target and host configs to be the same.

@jonhoo
Copy link
Contributor Author

jonhoo commented Feb 18, 2022

I gave the original discussion for the feature another reading, and I agree with you that having the top-level configuration option is probably the way to go. It has a similar flavor to resolver = "2", where we may want to flip the default in the future. So I've restored the past logic in that regard 👍

The reason this is hairy is because the behavior of Cargo today is inconsistent. Only linker and runner from [target.<host triple>] are applied to host artifacts, but not rustflags. The way I've represented that in the latest revision of this PR that I just pushed is that target-applies-to-host really has three states: unset, true, and false. When unset, the current (inconsistent) behavior is preserved. When set to true, everything from [target.<host triple>] is respected for host artifacts. And when set to true, [target.<host triple>] is ignored for host artifacts. That strikes the right balance to me at least.

I'm personally also not in favor of having -Zhost-config change the default for target-applies-to-host. Instead, I'd prefer for -Zhost-config to produce an error if target-applies-to-host isn't set to false. That way we start to incentivize affected users to put target-applies-to-host = false in their configuration files. But I've left that logic as-is for now.

@jonhoo jonhoo force-pushed the fix-10206 branch 3 times, most recently from b9273b3 to 2cc438c Compare February 18, 2022 18:03
This patch implements more complete logic for applying rustflags to
build scripts and other host artifacts. In the default configuration, it
only makes build scripts (and plugins and whatnot) pick up on
`rustflags` from `[host]`, which fixes rust-lang#10206 but otherwise preserves
existing behavior in all its inconsistent glory. The same is the case if
`target-applies-to-host` is explicitly set to `false`.

When `target-applies-to-host` is explicitly set to `true`, rustflags
will start to be applied in the same way that `linker` and `runner` are
today -- namely they'll be applied from `[target.<host triple>]` and
from `RUSTFLAGS`/`build.rustflags` if `--target <host triple>` is given.
@jameshilliard
Copy link
Contributor

The reason this is hairy is because the behavior of Cargo today is inconsistent.

Yeah, it's def a hairy mess.

Only linker and runner from [target.] are applied to host artifacts, but not rustflags.

Ugh, that's pretty bad, I only tested the linkers myself since that's what was causing build errors for us. That should probably just be fixed outright as it's clearly a bug.

When unset, the current (inconsistent) behavior is preserved.

IMO the best behavior when unset would be to apply target triples to host if --target is not set but don't apply if it is set. In addition if a host table is present without --target set I would have it override that and disable apply target configs to host.

So defaults like this for cases where target-applies-to-host is not explicitly specified is probably the least likely to break configurations IMO:

  1. if --target unset && no host table present => target-applies-to-host = true
  2. if --target unset && host table present => target-applies-to-host = false
  3. if --target set => target-applies-to-host = false

And when set to true, [target.<host triple>] is ignored for host artifacts.

I assume you mean when set to false here right?

I'm personally also not in favor of having -Zhost-config change the default for target-applies-to-host. Instead, I'd prefer for -Zhost-config to produce an error if target-applies-to-host isn't set to false.

Well the main reason for having it change the default was mostly to minimize the amount of combinations(branch conditions essentially) to test, I only wanted the host table feature to be tested with the new defaults for target-applies-to-host. By having it change the default it cuts down the amount of combination testing needing as you then no longer have to test the host table combined with old defaults.

@jonhoo
Copy link
Contributor Author

jonhoo commented Feb 18, 2022

Only linker and runner from [target.] are applied to host artifacts, but not rustflags.

Ugh, that's pretty bad, I only tested the linkers myself since that's what was causing build errors for us. That should probably just be fixed outright as it's clearly a bug.

When unset, the current (inconsistent) behavior is preserved.

IMO the best behavior when unset would be to apply target triples to host if --target is not set but don't apply if it is set.

I don't think we can just fix it, sadly, as that would likely break a bunch of cross-compiling users. Hence the "legacy" mode this PR implements. I think we almost certainly have to preserve backwards compatibility here, or cause a bunch of unnecessary churn for users. Though if the Cargo team is okay with the breakage, I'm okay with removing the legacy mode. I just assumed they wouldn't be.

In addition if a host table is present without --target set I would have it override that and disable apply target configs to host.

So defaults like this for cases where target-applies-to-host is not explicitly specified is probably the least likely to break configurations IMO:

1. if `--target` unset && no host table present => `target-applies-to-host = true`

2. if `--target` unset && host table present => `target-applies-to-host = false`

3. if `--target` set => `target-applies-to-host = false`

I really dislike dynamic defaults, since they tend to be hard to discover and hard to debug. I'd much rather (as this PR implements) have -Zhost-config require target-applies-to-host = false. I also don't think we can change today's behavior with respect to --target being set/unset except at something like an edition boundary, since a fair number of users probably rely on the current behavior (though here, too, if the Cargo team is okay with the breakage, we should do it). Which then leads to the choices this PR makes:

  1. If -Zhost-config and target-applies-to-host != false, error.
  2. If target-applies-to-host is true, pick up linker, runner, and rustflags (or, really, all keys) from target.<host> for host artifacts.
  3. If target-applies-to-host is false, do not pick up target.<host> for host artifacts.
  4. If target-applies-to-host is unset (which implies no host table), pick up linker, runner, and any future fields from target.<host> for host artifacts. Pick up rustflags only if --target isn't passed.

Then, come next edition, we make target-applies-to-host = "false" the default. And maybe the edition after we remove target-applies-to-host as a user-visible configuration option.

And when set to true, [target.<host triple>] is ignored for host artifacts.

I assume you mean when set to false here right?

Sorry, yes, false.

I'm personally also not in favor of having -Zhost-config change the default for target-applies-to-host. Instead, I'd prefer for -Zhost-config to produce an error if target-applies-to-host isn't set to false.

Well the main reason for having it change the default was mostly to minimize the amount of combinations(branch conditions essentially) to test, I only wanted the host table feature to be tested with the new defaults for target-applies-to-host. By having it change the default it cuts down the amount of combination testing needing as you then no longer have to test the host table combined with old defaults.

The number of combinations should be the same, since = true is just outright disallowed with -Zhost-config.

@jameshilliard
Copy link
Contributor

I don't think we can just fix it, sadly, as that would likely break a bunch of cross-compiling users.

We're talking about fixing the rustflags bug in the nightly only host-config feature right? Isn't the entire point of the feature being nightly only to allow breaking changes/fixes?

Hence the "legacy" mode this PR implements. I think we almost certainly have to preserve backwards compatibility here, or cause a bunch of unnecessary churn for users. Though if the Cargo team is okay with the breakage, I'm okay with removing the legacy mode. I just assumed they wouldn't be.

I'm highly skeptical changing the target-applies-to-host defaults would be all that problematic mostly since the target-applies-to-host = false default behavior already effectively exists for target triple != host triple cross compilation scenarios.

But this target-applies-to-host default change is an entirely separate issue AFAIU from fixing the host-config rustflags bug, I think it's probably best to fix the rustflags bug separately then worry about the defaults/stabilization path for the host-config and target-applies-to-host features after that's taken care of.

Build scripts typically don't usually need custom build flags at all to function(we don't even need/use the host-config feature in buildroot, we only use the target-applies-to-host feature to override the existing default buggy target-applies-to-host = true behavior so that our x86_64 cross builds don't break due to linker errors) AFAIU in most cases build scripts just need to be able to execute on the host and cargo doesn't even have a way to provide a host artifact specific configs without enabling -Zhost-config anyways, only triple specific configs are supported in stable toolchains which are inherently buggy for host artifact cross compilation configuration.

Arguably anyone cross compiling who has a config that changing this default breaks has a buggy config already, they just happen to have things working by accident, either way the point of the target-applies-to-host config flag is to provide an escape hatch to restore the previous buggy cross compilation behavior for anyone who actually depends on it.

From my understanding the Cargo team is fine with this approach provided there is a transition period before changing the default(although I personally don't actually think this is necessary since fixing the buggy behavior is only likely to break obscure configurations anyways).

I really dislike dynamic defaults, since they tend to be hard to discover and hard to debug.

I mean, if the goal is to have host triple == target triple cross compilation work as expected by default while breaking the least amount of existing configs then dynamic defaults here is probably the least terrible option.

I'd much rather (as this PR implements) have -Zhost-config require target-applies-to-host = false.

What are you trying to do here? Requiring a config option that's intended to be stabilized in order to enable setting a nightly gate for a feature also intended to be stabilized makes no sense to me.

Setting -Zhost-config isn't something that will do anything when the host config feature is stabilized, you would use the host config options once stabilized just by adding a host config table, -Zhost-config is a nightly feature gate option that will be removed.

I also don't think we can change today's behavior with respect to --target being set/unset except at something like an edition boundary, since a fair number of users probably rely on the current behavior (though here, too, if the Cargo team is okay with the breakage, we should do it).

Either way this seems to be something separate from the host.rustflags fix. Can we not fix the host.rustflags bug in the nightly host-config feature and then worry about --target behavior separately? The host/target split logic in cargo is rather complex/confusing in general and having all these changes intertwined makes things difficult to review here.

Which then leads to the choices this PR makes:

1. If `-Zhost-config` and `target-applies-to-host != false`, error.

This doesn't make sense since -Zhost-config is just a nightly feature gate flag that will not even exist when running builds with stable toolchains.

2. If `target-applies-to-host` is `true`, pick up `linker`, `runner`, and `rustflags` (or, really, all keys) from `target.<host>` for host artifacts.

This sounds fine.

3. If `target-applies-to-host` is `false`, do not pick up `target.<host>` for host artifacts.

This sounds fine.

4. If `target-applies-to-host` is unset (which implies no host table), pick up `linker`, `runner`, and any future fields from `target.<host>` for host artifacts. Pick up `rustflags` only if `--target` _isn't_ passed.

This is extremely confusing, AFAIU isn't not even possible to set host artifact specific rustflags at all without this fix, or any host artifact specific config options at all(only triple specific flags seem possible without nightly features) for that matter without enabling the nightly -Zhost-config feature gate.

The report #10206 that this fixes seems to be entirely referencing a bug in the nightly host-config feature and not something applicable to a stabilized feature, which should be able to be fixed without a migration period as that feature is not stabilized yet.

Is there also a different bug with rustflags for target triple config flags(ie without -Zhost-config being used at all).

Then, come next edition, we make target-applies-to-host = "false" the default. And maybe the edition after we remove target-applies-to-host as a user-visible configuration option.

I think the important part is just changing the default before we stabilize the host-config feature to reduce the amount of potential branch conditions we have to handle/test.

The number of combinations should be the same, since = true is just outright disallowed with -Zhost-config.

We want to test the host-config feature behavior as if it were already stabilized. You appear to be effectively trying to test the -Zhost-config nightly feature gating logic which isn't particularly useful as nightly feature gating isn't applicable to features once stabilized at all.

@alexcrichton
Copy link
Member

Ok I think I understand what you mean by the tri-state behavior, I never fully understood that but seeing the test cases now and how they're working clarifies this for me at least. My main priority in reviewing all of this rustflags/target/host related stuff is to ensure that breakage is quanitified and either mitigated or deemed acceptable. In that sense I'm pretty confident that this PR doesn't change the current behavior, which is good in the sense that it mitigates possible breakage. For the future, though, I think we'll want the tri-state idea but not necessarily super-well-supported.

I don't think anyone will really argue that today's behavior is correct and should be codified into Cargo for all time. Today's behavior is the amalgamation of growing over years as PRs touched various portions and we as Cargo reviewers didn't do a great job of seeing how everything fits together. What's done is done though, we move past it. This I think means that the stabilization story for target-applies-to-host may be a bit different though. In my mind the "tri-state" is "not configured", true, and false. The "not configured" case is whatever Cargo does today to prevent breaking any existing builds. In the future I was assuming we would stabilize as true and then in the future switch to false at some point later. I believe, though, that at least in my desired interpretation of true then stabilizing as true would be a breaking change because as noted here some tests aren't behaving as I'd expect (where when you say target-applies-to-host that doesn't actually happen). Instead this may mean that we need to bite the bullet on breakage and simply stabilize the false option first (and maybe even just switch internal defaults and leave the target-applies-to-host option unstable).

Basically what I'm trying to do is to chart a course from where we are to where we want to be with minimal breakage to the ecosystem. Ideally this would mean that at any time if someone hits a breakage they can flip a switch to go back to some previous behavior or something like that. I've been working under the assumption that this is possible but it may simply not be possible. This may all simply mean that breakage is unavoidable and if the breakage doesn't work for someone all we can say is "sorry you can hold back on upgrading Cargo for a bit while the breakage is worked through".

@jonhoo
Copy link
Contributor Author

jonhoo commented Feb 25, 2022

I completely agree — that matches my thinking on the subject.

I think the right question then becomes is "how hard is mitigation". If target-applies-to-host = false is made the default, then that will break users with no recourse until [host] is stabilized — they'll be unable to set the rustflags and linker for build scripts when --target is passed. Which seems untenable to me. We could perhaps (as you suggested) group target-applies-to-host and host-config together under a single -Z so they land together, but I'll leave that up to you. In the meantime, I actually wonder if the right thing to do short-term is to change the default to = true so that Cargo's behavior is at least consistent, and so that there is a way to accomplish what users might want, even if it's not exactly the behavior we think is least likely to cause problems.

In any case, I also agree with you that this PR doesn't change current behavior beyond fixing #10206, and so should be possible to safely land.

@alexcrichton
Copy link
Member

@bors: r+

Ok well we can get all this sorted out during the stabilization of the features here. I agree this is fine to go for now. Personally I continue to feel that these unstable features are also gated on the fact that cargo build is "all host" rather than the expected "some host some target" builds, because if we were to stabilize target-applies-to-host = false, which I think is what we want, then RUSTFLAGS="" cargo build wouldn't actually apply any flags anywhere.

@bors
Copy link
Contributor

bors commented Feb 28, 2022

📌 Commit 56db829 has been approved by alexcrichton

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Feb 28, 2022
@bors
Copy link
Contributor

bors commented Feb 28, 2022

⌛ Testing commit 56db829 with merge 3d6970d...

@bors
Copy link
Contributor

bors commented Feb 28, 2022

☀️ Test successful - checks-actions
Approved by: alexcrichton
Pushing 3d6970d to master...

@bors bors merged commit 3d6970d into rust-lang:master Feb 28, 2022
@jonhoo jonhoo deleted the fix-10206 branch February 28, 2022 20:41
matthiaskrgr pushed a commit to matthiaskrgr/rust that referenced this pull request Mar 1, 2022
11 changes in
d6cdde584a1f15ea086bae922e20fd27f7165431..3d6970d50e30e797b8e26b2b9b1bdf92dc381f34
2022-02-22 19:55:51 +0000 to 2022-02-28 19:29:07 +0000:

 - rust-lang/cargo#10395
 - rust-lang/cargo#10425
 - rust-lang/cargo#10428
 - rust-lang/cargo#10388
 - rust-lang/cargo#10167
 - rust-lang/cargo#10429
 - rust-lang/cargo#10426
 - rust-lang/cargo#10372
 - rust-lang/cargo#10420
 - rust-lang/cargo#10416
 - rust-lang/cargo#10417
matthiaskrgr added a commit to matthiaskrgr/rust that referenced this pull request Mar 1, 2022
Update cargo

11 changes in
d6cdde584a1f15ea086bae922e20fd27f7165431..3d6970d50e30e797b8e26b2b9b1bdf92dc381f34
2022-02-22 19:55:51 +0000 to 2022-02-28 19:29:07 +0000:

 - rust-lang/cargo#10395
 - rust-lang/cargo#10425
 - rust-lang/cargo#10428
 - rust-lang/cargo#10388
 - rust-lang/cargo#10167
 - rust-lang/cargo#10429
 - rust-lang/cargo#10426
 - rust-lang/cargo#10372
 - rust-lang/cargo#10420
 - rust-lang/cargo#10416
 - rust-lang/cargo#10417
@jonhoo
Copy link
Contributor Author

jonhoo commented Mar 1, 2022

It's a good point. I wonder if the way to go here is to take a lesson from rust-lang/cc-rs#9 and add support for RUSTFLAGS_$TARGET and the like. That way we can say that RUSTFLAGS applies to all targets (including the host), TARGET_RUSTFLAGS applies only to the target(s), HOST_RUSTFLAGS applies only to the host, RUSTFLAGS_$TARGET applies only to $TARGET, etc. What do you think?

@alexcrichton
Copy link
Member

Maybe? That's something I think needs discussion outside of just this PR and input from others as well.

@jonhoo
Copy link
Contributor Author

jonhoo commented Mar 1, 2022

Oh, yes, definitely. I just don't see a way around doing that, or something like it, if we want cargo build to work like cargo build --target <host>.

@ehuss ehuss added this to the 1.61.0 milestone Apr 7, 2022
arnout pushed a commit to buildroot/buildroot that referenced this pull request Apr 11, 2023
In Cargo, it is quite typical for "build scripts" to be written in Rust
and therefore they need to be compiled as part of the overall build. In
cross-compilation, that means a mixed host and target build.

Unfortunately, by default Cargo makes no distinction between the
RUSTFLAGS used for the host and the target. There is, however, an
unstable feature to make this distinction [1][2].

We already have CARGO_TARGET_APPLIES_TO_HOST="false". This makes sure
that any configuration that we make for the target doesn't automatically
apply to the host as well. However, this only applies for per-target
configuration, for example the setting of "cc" in the config.toml
generated by package/rust/rust.mk. Flags that are passed with RUSTFLAGS
still apply to both host and target. Therefore, we need to use the
CARGO_TARGET_<tuple>_RUSTFLAGS environment variable instead of plain
RUSTFLAGS.

This, however, doesn't allow us to specify flags that apply only to the
host. We could use CARGO_TARGET_<hosttuple>_RUSTFLAGS for that, but that
doesn't work in case the host and target tuple are the same. For this,
we need another unstable feature, enabled with
CARGO_UNSTABLE_HOST_CONFIG="true". With this enabled, we can specify
flags that apply only for the host build using CARGO_HOST_RUSTFLAGS.

Currently, we don't have any such flags, but we really should: we should
pass the proper link flags to point to $(HOST_DIR)/lib. Therefore, add
CARGO_HOST_RUSTFLAGS doing exactly that.

[1] https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#host-config
[2] rust-lang/cargo#10395

Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
Signed-off-by: Arnout Vandecappelle <arnout@mind.be>
hiroshiyui pushed a commit to hiroshiyui/buildroot that referenced this pull request Apr 16, 2023
In Cargo, it is quite typical for "build scripts" to be written in Rust
and therefore they need to be compiled as part of the overall build. In
cross-compilation, that means a mixed host and target build.

Unfortunately, by default Cargo makes no distinction between the
RUSTFLAGS used for the host and the target. There is, however, an
unstable feature to make this distinction [1][2].

We already have CARGO_TARGET_APPLIES_TO_HOST="false". This makes sure
that any configuration that we make for the target doesn't automatically
apply to the host as well. However, this only applies for per-target
configuration, for example the setting of "cc" in the config.toml
generated by package/rust/rust.mk. Flags that are passed with RUSTFLAGS
still apply to both host and target. Therefore, we need to use the
CARGO_TARGET_<tuple>_RUSTFLAGS environment variable instead of plain
RUSTFLAGS.

This, however, doesn't allow us to specify flags that apply only to the
host. We could use CARGO_TARGET_<hosttuple>_RUSTFLAGS for that, but that
doesn't work in case the host and target tuple are the same. For this,
we need another unstable feature, enabled with
CARGO_UNSTABLE_HOST_CONFIG="true". With this enabled, we can specify
flags that apply only for the host build using CARGO_HOST_RUSTFLAGS.

Currently, we don't have any such flags, but we really should: we should
pass the proper link flags to point to $(HOST_DIR)/lib. Therefore, add
CARGO_HOST_RUSTFLAGS doing exactly that.

[1] https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#host-config
[2] rust-lang/cargo#10395

Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
Signed-off-by: Arnout Vandecappelle <arnout@mind.be>
arnout pushed a commit to buildroot/buildroot that referenced this pull request Apr 23, 2023
In Cargo, it is quite typical for "build scripts" to be written in Rust
and therefore they need to be compiled as part of the overall build. In
cross-compilation, that means a mixed host and target build.

Unfortunately, by default Cargo makes no distinction between the
RUSTFLAGS used for the host and the target. There is, however, an
unstable feature to make this distinction [1][2].

We already have CARGO_TARGET_APPLIES_TO_HOST="false". This makes sure
that any configuration that we make for the target doesn't automatically
apply to the host as well. However, this only applies for per-target
configuration, for example the setting of "cc" in the config.toml
generated by package/rust/rust.mk. Flags that are passed with RUSTFLAGS
still apply to both host and target. Therefore, we need to use the
CARGO_TARGET_<tuple>_RUSTFLAGS environment variable instead of plain
RUSTFLAGS.

This, however, doesn't allow us to specify flags that apply only to the
host. We could use CARGO_TARGET_<hosttuple>_RUSTFLAGS for that, but that
doesn't work in case the host and target tuple are the same. For this,
we need another unstable feature, enabled with
CARGO_UNSTABLE_HOST_CONFIG="true". With this enabled, we can specify
flags that apply only for the host build using CARGO_HOST_RUSTFLAGS.

Currently, we don't have any such flags, but we really should: we should
pass the proper link flags to point to $(HOST_DIR)/lib. Therefore, add
CARGO_HOST_RUSTFLAGS doing exactly that.

[1] https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#host-config
[2] rust-lang/cargo#10395

Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
Signed-off-by: Arnout Vandecappelle <arnout@mind.be>
(cherry picked from commit b40a2cc)
Signed-off-by: Peter Korsgaard <peter@korsgaard.com>
citral23 pushed a commit to citral23/buildroot that referenced this pull request Sep 18, 2023
In Cargo, it is quite typical for "build scripts" to be written in Rust
and therefore they need to be compiled as part of the overall build. In
cross-compilation, that means a mixed host and target build.

Unfortunately, by default Cargo makes no distinction between the
RUSTFLAGS used for the host and the target. There is, however, an
unstable feature to make this distinction [1][2].

We already have CARGO_TARGET_APPLIES_TO_HOST="false". This makes sure
that any configuration that we make for the target doesn't automatically
apply to the host as well. However, this only applies for per-target
configuration, for example the setting of "cc" in the config.toml
generated by package/rust/rust.mk. Flags that are passed with RUSTFLAGS
still apply to both host and target. Therefore, we need to use the
CARGO_TARGET_<tuple>_RUSTFLAGS environment variable instead of plain
RUSTFLAGS.

This, however, doesn't allow us to specify flags that apply only to the
host. We could use CARGO_TARGET_<hosttuple>_RUSTFLAGS for that, but that
doesn't work in case the host and target tuple are the same. For this,
we need another unstable feature, enabled with
CARGO_UNSTABLE_HOST_CONFIG="true". With this enabled, we can specify
flags that apply only for the host build using CARGO_HOST_RUSTFLAGS.

Currently, we don't have any such flags, but we really should: we should
pass the proper link flags to point to $(HOST_DIR)/lib. Therefore, add
CARGO_HOST_RUSTFLAGS doing exactly that.

[1] https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#host-config
[2] rust-lang/cargo#10395

Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
Signed-off-by: Arnout Vandecappelle <arnout@mind.be>
@gmorenz
Copy link
Contributor

gmorenz commented May 8, 2024

@rustbot label: +Z-target-applies-to-host

@rustbot rustbot added the Z-target-applies-to-host Nightly: target-applies-to-host label May 8, 2024
bors added a commit that referenced this pull request Jul 4, 2024
…ihanglo

Pass rustflags to artifacts built with implicit targets when using target-applies-to-host

## What this PR does

This fixes #10744, a long-standing bug with `target-applies-to-host=false` where `RUSTFLAGS` are not being passed to artifact-units when built with `cargo build` (as opposed to `cargo build --target <host>`).

It doesn't fix a similar problem with `linker` and `links`. If the architecture in this PR is accepted, I expect I will be able to fix `linker` and `links` in the same way in a subsequent PR.

Below is a hopefully useful table of what flags are passed when, with new behavior bolded (without these changes the flag is not passed). I've only changed values in the top right cell, I've included the whole table for completeness and to hopefully make clear what exactly this feature is doing (which took me awhile to understand).

The table was generated with a host of x86_64-unknown-linux-gnu. "Flag" refers to values in RUSTFLAGS. "Linker" refers to the value of `--config target.<host>.linker` . The table was built with [this repo](https://github.com/gmorenz/target_application_to_host_matrix) and then hand modify to bold changed values.

| | target_applies_to_host=true | target_applies_to_host=false |
|-|-|-|
| no --target flag | Flag passed to bin<br/>Flag passed to shared dep from bin<br/>Linker passed to bin<br/><br/>Flag passed to proc macro<br/>Flag passed to shared dep from proc macro<br/>Linker passed to proc macro<br/><br/>Built with 5 invocations<br/>Without rustflags, built with 5 invocations<br/> | **Flag passed to bin**<br/>**Flag passed to shared dep from bin**<br/>Linker not passed to bin<br/><br/>Flag not passed to proc macro<br/>Flag not passed to shared dep from proc macro<br/>Linker not passed to proc macro<br/><br/>**Built with 6 invocations**<br/>Without rustflags, built with 5 invocations<br/> |
| --target x86_64-unknown-linux-gnu | Flag passed to bin<br/>Flag passed to shared dep from bin<br/>Linker passed to bin<br/><br/>Flag not passed to proc macro<br/>Flag not passed to shared dep from proc macro<br/>Linker passed to proc macro<br/><br/>Built with 6 invocations<br/>Without rustflags, built with 6 invocations<br/> | Flag passed to bin<br/>Flag passed to shared dep from bin<br/>Linker passed to bin<br/><br/>Flag not passed to proc macro<br/>Flag not passed to shared dep from proc macro<br/>Linker not passed to proc macro<br/><br/>Built with 6 invocations<br/>Without rustflags, built with 6 invocations<br/> |
| --target i686-unknown-linux-gnu | Flag passed to bin<br/>Flag passed to shared dep from bin<br/>Linker not passed to bin<br/><br/>Flag not passed to proc macro<br/>Flag not passed to shared dep from proc macro<br/>Linker passed to proc macro<br/><br/>Built with 6 invocations<br/>Without rustflags, built with 6 invocations<br/> | Flag passed to bin<br/>Flag passed to shared dep from bin<br/>Linker not passed to bin<br/><br/>Flag not passed to proc macro<br/>Flag not passed to shared dep from proc macro<br/>Linker not passed to proc macro<br/><br/>Built with 6 invocations<br/>Without rustflags, built with 6 invocations<br/> |

 ## How it is implemented

There are two stages in the `UnitGraph`'s life. It gets built, with correct `CompileKind`: `CompileKind::Host` for host units, `CompileKind::Target(HostTriple)` for artifact units. Then it gets rebuilt with artifact units that are intended for the host  having their kind changed to `CompileKind::Host`.

This rebuilding step serves to de-duplicate host and artifact units. A step that we want to maintain where possible. Because de-duplicating host and artifact dependencies is only possible if their `rustflags` don't differ we must calculate `rustflags` for units before this step, and this step necessarily throws away the information necessary to calculate them.

The possible rustflags have already been determined before creating units. "Calculating rustflags for units" just means determining which set of rustflags go with a particular unit, and storing that somewhere. The obvious place to do that is in the unit itself, so that's what this PR does, which has the convenient side affect of also handing the de-duplication logic for us. If the rustflags are the same, two units can be merged, if they differ, they cannot.

In the above table that's why it takes 5 invocations to build the repo without rustflags, but 6 with them. The shared_dependency merges when it is built without rustflags, and not when it is built with rustflags.

 ## Related PRs, comments and issues

fixes #10744

#9453 - Tracking issue

#9753 - Stabilization PR

#9634 - I believe this was another attempt (going down another implementation route) to fix the same issue

Comments from Alex Crichton noting that this must be fixed [1](#10395 (comment)) [2](#9753 (comment))

[My own comment describing exactly how I ran into this](#9753 (comment))

[Documentation for the feature](https://doc.rust-lang.org/cargo/reference/unstable.html#target-applies-to-host)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. Z-target-applies-to-host Nightly: target-applies-to-host
Projects
None yet
Development

Successfully merging this pull request may close these issues.

-Zhost-config doesn't apply host.rustflags to build-script-builds
9 participants