Skip to content

Commit 4f41727

Browse files
author
Jon Gjengset
committed
Enable propagating rustflags to build scripts
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 #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.
1 parent fb47df7 commit 4f41727

File tree

6 files changed

+310
-104
lines changed

6 files changed

+310
-104
lines changed

Diff for: src/cargo/core/compiler/build_context/target_info.rs

+109-74
Original file line numberDiff line numberDiff line change
@@ -573,17 +573,23 @@ fn output_err_info(cmd: &ProcessBuilder, stdout: &str, stderr: &str) -> String {
573573
/// - the `CARGO_ENCODED_RUSTFLAGS` environment variable
574574
/// - the `RUSTFLAGS` environment variable
575575
///
576-
/// then if this was not found
576+
/// then if none of those were found
577577
///
578578
/// - `target.*.rustflags` from the config (.cargo/config)
579579
/// - `target.cfg(..).rustflags` from the config
580+
/// - `host.*.rustflags` from the config if compiling a host artifact or without `--target`
580581
///
581-
/// then if neither of these were found
582+
/// then if none of those were found
582583
///
583584
/// - `build.rustflags` from the config
584585
///
585-
/// Note that if a `target` is specified, no args will be passed to host code (plugins, build
586-
/// scripts, ...), even if it is the same as the target.
586+
/// The behavior differs slightly when cross-compiling (or, specifically, when `--target` is
587+
/// provided) for artifacts that are always built for the host (plugins, build scripts, ...).
588+
/// For those artifacts, the behavior depends on `target-applies-to-host`. In the default
589+
/// configuration (where `target-applies-to-host` is unset), and if `target-applies-to-host =
590+
/// false`, host artifacts _only_ respect `host.*.rustflags`, and no other configuration sources.
591+
/// If `target-applies-to-host = true`, host artifacts also respect `target.<host triple>`, and if
592+
/// `<target triple> == <host triple>` `RUSTFLAGS` and `CARGO_ENCODED_RUSTFLAGS`.
587593
fn env_args(
588594
config: &Config,
589595
requested_kinds: &[CompileKind],
@@ -592,49 +598,67 @@ fn env_args(
592598
kind: CompileKind,
593599
name: &str,
594600
) -> CargoResult<Vec<String>> {
595-
// We *want* to apply RUSTFLAGS only to builds for the
596-
// requested target architecture, and not to things like build
597-
// scripts and plugins, which may be for an entirely different
598-
// architecture. Cargo's present architecture makes it quite
599-
// hard to only apply flags to things that are not build
600-
// scripts and plugins though, so we do something more hacky
601-
// instead to avoid applying the same RUSTFLAGS to multiple targets
602-
// arches:
601+
let target_applies_to_host = config.target_applies_to_host()?;
602+
603+
// Include untargeted configuration sources (like `RUSTFLAGS`) if
603604
//
604-
// 1) If --target is not specified we just apply RUSTFLAGS to
605-
// all builds; they are all going to have the same target.
605+
// - we're compiling artifacts for the target platform; or
606+
// - we're not cross-compiling; or
607+
// - we're compiling host artifacts, the requested target matches the host, and the user has
608+
// requested that the host pick up target configurations.
606609
//
607-
// 2) If --target *is* specified then we only apply RUSTFLAGS
608-
// to compilation units with the Target kind, which indicates
609-
// it was chosen by the --target flag.
610+
// The rationale for the third condition is that `RUSTFLAGS` is intended to affect compilation
611+
// for the target, and `target-applies-to-host` makes it so that the host is affected by its
612+
// target's config, so if `--target <host triple>` then `RUSTFLAGS` should also apply to the
613+
// host.
614+
let include_generic = !kind.is_host()
615+
|| requested_kinds == [CompileKind::Host]
616+
|| (target_applies_to_host == Some(true)
617+
&& requested_kinds == [CompileKind::Target(CompileTarget::new(host_triple)?)]);
618+
// Include targeted configuration sources (like `target.*.rustflags`) if
610619
//
611-
// This means that, e.g., even if the specified --target is the
612-
// same as the host, build scripts in plugins won't get
613-
// RUSTFLAGS.
614-
if requested_kinds != [CompileKind::Host] && kind.is_host() {
615-
// This is probably a build script or plugin and we're
616-
// compiling with --target. In this scenario there are
617-
// no rustflags we can apply.
618-
return Ok(Vec::new());
619-
}
620-
621-
// First try CARGO_ENCODED_RUSTFLAGS from the environment.
622-
// Prefer this over RUSTFLAGS since it's less prone to encoding errors.
623-
if let Ok(a) = env::var(format!("CARGO_ENCODED_{}", name)) {
624-
if a.is_empty() {
625-
return Ok(Vec::new());
620+
// - we're compiling artifacts for the target platform; or
621+
// - we're not cross-compiling; or
622+
// - we're compiling host artifacts and the user has requested that the host pick up target
623+
// configurations.
624+
//
625+
// The middle condition here may seem counter-intuitive. If `target-applies-to-host-kind` is
626+
// disabled, then `target.*.rustflags` should arguably not apply to host artifacts. However,
627+
// this is likely surprising to users who set `target.<host triple>.rustflags` and run `cargo
628+
// build` (with no `--target`). So, we respect `target.<host triple>`.rustflags` when
629+
// `--target` _isn't_ supplied, which arguably matches the mental model established by
630+
// respecting `RUSTFLAGS` when `--target` isn't supplied.
631+
let include_for_target = !kind.is_host()
632+
|| requested_kinds == [CompileKind::Host]
633+
|| target_applies_to_host == Some(true);
634+
// Include host-based configuration sources (like `host.*.rustflags`) if
635+
//
636+
// - we're compiling host artifacts; or
637+
// - we're not cross-compiling
638+
//
639+
// Note that we do _not_ read `host.*.rustflags` just because the host's target is the same as
640+
// the requested target, as that's the whole point of the `host` section in the first place.
641+
let include_for_host = kind.is_host() || requested_kinds == [CompileKind::Host];
642+
643+
if include_generic {
644+
// First try CARGO_ENCODED_RUSTFLAGS from the environment.
645+
// Prefer this over RUSTFLAGS since it's less prone to encoding errors.
646+
if let Ok(a) = env::var(format!("CARGO_ENCODED_{}", name)) {
647+
if a.is_empty() {
648+
return Ok(Vec::new());
649+
}
650+
return Ok(a.split('\x1f').map(str::to_string).collect());
626651
}
627-
return Ok(a.split('\x1f').map(str::to_string).collect());
628-
}
629652

630-
// Then try RUSTFLAGS from the environment
631-
if let Ok(a) = env::var(name) {
632-
let args = a
633-
.split(' ')
634-
.map(str::trim)
635-
.filter(|s| !s.is_empty())
636-
.map(str::to_string);
637-
return Ok(args.collect());
653+
// Then try RUSTFLAGS from the environment
654+
if let Ok(a) = env::var(name) {
655+
let args = a
656+
.split(' ')
657+
.map(str::trim)
658+
.filter(|s| !s.is_empty())
659+
.map(str::to_string);
660+
return Ok(args.collect());
661+
}
638662
}
639663

640664
let mut rustflags = Vec::new();
@@ -643,44 +667,55 @@ fn env_args(
643667
.chars()
644668
.flat_map(|c| c.to_lowercase())
645669
.collect::<String>();
646-
// Then the target.*.rustflags value...
647-
let target = match &kind {
648-
CompileKind::Host => host_triple,
649-
CompileKind::Target(target) => target.short_name(),
650-
};
651-
let key = format!("target.{}.{}", target, name);
652-
if let Some(args) = config.get::<Option<StringList>>(&key)? {
653-
rustflags.extend(args.as_slice().iter().cloned());
670+
if include_for_target {
671+
// Then the target.*.rustflags value...
672+
let target = match &kind {
673+
CompileKind::Host => host_triple,
674+
CompileKind::Target(target) => target.short_name(),
675+
};
676+
let key = format!("target.{}.{}", target, name);
677+
if let Some(args) = config.get::<Option<StringList>>(&key)? {
678+
rustflags.extend(args.as_slice().iter().cloned());
679+
}
680+
// ...including target.'cfg(...)'.rustflags
681+
if let Some(target_cfg) = target_cfg {
682+
config
683+
.target_cfgs()?
684+
.iter()
685+
.filter_map(|(key, cfg)| {
686+
cfg.rustflags
687+
.as_ref()
688+
.map(|rustflags| (key, &rustflags.val))
689+
})
690+
.filter(|(key, _rustflags)| CfgExpr::matches_key(key, target_cfg))
691+
.for_each(|(_key, cfg_rustflags)| {
692+
rustflags.extend(cfg_rustflags.as_slice().iter().cloned());
693+
});
694+
}
654695
}
655-
// ...including target.'cfg(...)'.rustflags
656-
if let Some(target_cfg) = target_cfg {
657-
config
658-
.target_cfgs()?
659-
.iter()
660-
.filter_map(|(key, cfg)| {
661-
cfg.rustflags
662-
.as_ref()
663-
.map(|rustflags| (key, &rustflags.val))
664-
})
665-
.filter(|(key, _rustflags)| CfgExpr::matches_key(key, target_cfg))
666-
.for_each(|(_key, cfg_rustflags)| {
667-
rustflags.extend(cfg_rustflags.as_slice().iter().cloned());
668-
});
696+
697+
if include_for_host {
698+
let target_cfg = config.host_cfg_triple(host_triple)?;
699+
if let Some(rf) = target_cfg.rustflags {
700+
rustflags.extend(rf.val.as_slice().iter().cloned());
701+
}
669702
}
670703

671704
if !rustflags.is_empty() {
672705
return Ok(rustflags);
673706
}
674707

675-
// Then the `build.rustflags` value.
676-
let build = config.build_config()?;
677-
let list = if name == "rustflags" {
678-
&build.rustflags
679-
} else {
680-
&build.rustdocflags
681-
};
682-
if let Some(list) = list {
683-
return Ok(list.as_slice().to_vec());
708+
if include_generic {
709+
// Then the `build.rustflags` value.
710+
let build = config.build_config()?;
711+
let list = if name == "rustflags" {
712+
&build.rustflags
713+
} else {
714+
&build.rustdocflags
715+
};
716+
if let Some(list) = list {
717+
return Ok(list.as_slice().to_vec());
718+
}
684719
}
685720

686721
Ok(Vec::new())
@@ -718,7 +753,7 @@ impl<'cfg> RustcTargetData<'cfg> {
718753
let mut target_info = HashMap::new();
719754
let target_applies_to_host = config.target_applies_to_host()?;
720755
let host_info = TargetInfo::new(config, requested_kinds, &rustc, CompileKind::Host)?;
721-
let host_config = if target_applies_to_host {
756+
let host_config = if target_applies_to_host != Some(false) {
722757
config.target_cfg_triple(&rustc.host)?
723758
} else {
724759
config.host_cfg_triple(&rustc.host)?

Diff for: src/cargo/util/config/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1551,7 +1551,7 @@ impl Config {
15511551
}
15521552

15531553
/// Returns true if the `[target]` table should be applied to host targets.
1554-
pub fn target_applies_to_host(&self) -> CargoResult<bool> {
1554+
pub fn target_applies_to_host(&self) -> CargoResult<Option<bool>> {
15551555
target::get_target_applies_to_host(self)
15561556
}
15571557

Diff for: src/cargo/util/config/target.rs

+19-10
Original file line numberDiff line numberDiff line change
@@ -66,20 +66,29 @@ pub(super) fn load_target_cfgs(config: &Config) -> CargoResult<Vec<(String, Targ
6666
}
6767

6868
/// Returns true if the `[target]` table should be applied to host targets.
69-
pub(super) fn get_target_applies_to_host(config: &Config) -> CargoResult<bool> {
69+
pub(super) fn get_target_applies_to_host(config: &Config) -> CargoResult<Option<bool>> {
7070
if config.cli_unstable().target_applies_to_host {
71-
if let Ok(target_applies_to_host) = config.get::<bool>("target-applies-to-host") {
72-
Ok(target_applies_to_host)
73-
} else {
74-
Ok(!config.cli_unstable().host_config)
71+
if let Ok(target_applies_to_host) = config.get::<Option<bool>>("target-applies-to-host") {
72+
if config.cli_unstable().host_config {
73+
match target_applies_to_host {
74+
Some(true) => {
75+
anyhow::bail!(
76+
"the -Zhost-config flag requires target-applies-to-host = false"
77+
);
78+
}
79+
Some(false) => {}
80+
None => {
81+
// -Zhost-config automatically disables target-applies-to-host
82+
return Ok(Some(false));
83+
}
84+
}
85+
}
86+
return Ok(target_applies_to_host);
7587
}
7688
} else if config.cli_unstable().host_config {
77-
anyhow::bail!(
78-
"the -Zhost-config flag requires the -Ztarget-applies-to-host flag to be set"
79-
);
80-
} else {
81-
Ok(true)
89+
anyhow::bail!("the -Zhost-config flag requires -Ztarget-applies-to-host");
8290
}
91+
Ok(None)
8392
}
8493

8594
/// Loads a single `[host]` table for the given triple.

Diff for: src/doc/src/reference/unstable.md

+31-12
Original file line numberDiff line numberDiff line change
@@ -508,14 +508,30 @@ CLI paths are relative to the current working directory.
508508
* Original Pull Request: [#9322](https://github.com/rust-lang/cargo/pull/9322)
509509
* Tracking Issue: [#9453](https://github.com/rust-lang/cargo/issues/9453)
510510

511-
The `target-applies-to-host` key in a config file can be used set the desired
512-
behavior for passing target config flags to build scripts.
513-
514-
It requires the `-Ztarget-applies-to-host` command-line option.
515-
516-
The current default for `target-applies-to-host` is `true`, which will be
517-
changed to `false` in the future, if `-Zhost-config` is used the new `false`
518-
default will be set for `target-applies-to-host`.
511+
Historically, Cargo has respected the `linker` and `runner` options (but
512+
_not_ `rustflags`) of `[target.<host triple>]` configuration sections
513+
when building and running build scripts, plugins, and other artifacts
514+
that are _always_ built for the host platform.
515+
`-Ztarget-applies-to-host` enables the top-level
516+
`target-applies-to-host` setting in Cargo configuration files which
517+
allows users to opt into different (and more consistent) behavior for
518+
these properties.
519+
520+
When `target-applies-to-host` is unset in the configuration file, the
521+
existing Cargo behavior is preserved (though see `-Zhost-config`, which
522+
changes that default). When it is set to `true`, _all_ options from
523+
`[target.<host triple>]` are respected for host artifacts. When it is
524+
set to `false`, _no_ options from `[target.<host triple>]` are respected
525+
for host artifacts.
526+
527+
In the specific case of `rustflags`, this settings also affects whether
528+
`--target <host-triple>` picks up the same configuration as if
529+
`--target` was not supplied in the first place.
530+
531+
In the future, `target-applies-to-host` may end up defaulting to `false`
532+
to provide more sane and consistent default behavior. When set to
533+
`false`, `host-config` can be used to customize the behavior for host
534+
artifacts.
519535

520536
```toml
521537
# config.toml
@@ -535,15 +551,17 @@ such as build scripts that must run on the host system instead of the target
535551
system when cross compiling. It supports both generic and host arch specific
536552
tables. Matching host arch tables take precedence over generic host tables.
537553

538-
It requires the `-Zhost-config` and `-Ztarget-applies-to-host` command-line
539-
options to be set.
554+
It requires the `-Zhost-config` and `-Ztarget-applies-to-host`
555+
command-line options to be set, and that `target-applies-to-host =
556+
false` is set in the Cargo configuration file.
540557

541558
```toml
542559
# config.toml
543560
[host]
544561
linker = "/path/to/host/linker"
545562
[host.x86_64-unknown-linux-gnu]
546563
linker = "/path/to/host/arch/linker"
564+
rustflags = ["-Clink-arg=--verbose"]
547565
[target.x86_64-unknown-linux-gnu]
548566
linker = "/path/to/target/linker"
549567
```
@@ -552,8 +570,9 @@ The generic `host` table above will be entirely ignored when building on a
552570
`x86_64-unknown-linux-gnu` host as the `host.x86_64-unknown-linux-gnu` table
553571
takes precedence.
554572

555-
Setting `-Zhost-config` changes the default for `target-applies-to-host` to
556-
`false` from `true`.
573+
Setting `-Zhost-config` changes the default value of
574+
`target-applies-to-host` to `false`, and will error if
575+
`target-applies-to-host` is set to `true`.
557576

558577
```console
559578
cargo +nightly -Ztarget-applies-to-host -Zhost-config build --target x86_64-unknown-linux-gnu

Diff for: tests/testsuite/build_script.rs

+4-5
Original file line numberDiff line numberDiff line change
@@ -469,7 +469,7 @@ fn custom_build_invalid_host_config_feature_flag() {
469469
.with_status(101)
470470
.with_stderr_contains(
471471
"\
472-
error: the -Zhost-config flag requires the -Ztarget-applies-to-host flag to be set
472+
error: the -Zhost-config flag requires -Ztarget-applies-to-host
473473
",
474474
)
475475
.run();
@@ -483,7 +483,6 @@ fn custom_build_env_var_rustc_linker_host_target_with_bad_host_config() {
483483
".cargo/config",
484484
&format!(
485485
r#"
486-
target-applies-to-host = true
487486
[host]
488487
linker = "/path/to/host/linker"
489488
[target.{}]
@@ -496,16 +495,16 @@ fn custom_build_env_var_rustc_linker_host_target_with_bad_host_config() {
496495
.file("src/lib.rs", "")
497496
.build();
498497

499-
// build.rs should fail due to bad target linker being set
498+
// build.rs should fail due to bad host linker being set
500499
p.cargo("build -Z target-applies-to-host -Z host-config --verbose --target")
501500
.arg(&target)
502501
.masquerade_as_nightly_cargo()
503502
.with_status(101)
504503
.with_stderr_contains(
505504
"\
506505
[COMPILING] foo v0.0.1 ([CWD])
507-
[RUNNING] `rustc --crate-name build_script_build build.rs [..]--crate-type bin [..]-C linker=[..]/path/to/target/linker [..]`
508-
[ERROR] linker `[..]/path/to/target/linker` not found
506+
[RUNNING] `rustc --crate-name build_script_build build.rs [..]--crate-type bin [..]-C linker=[..]/path/to/host/linker [..]`
507+
[ERROR] linker `[..]/path/to/host/linker` not found
509508
"
510509
)
511510
.run();

0 commit comments

Comments
 (0)