diff --git a/src/cargo/core/compiler/build_context/target_info.rs b/src/cargo/core/compiler/build_context/target_info.rs index 6c5da38dde4..32f767ea718 100644 --- a/src/cargo/core/compiler/build_context/target_info.rs +++ b/src/cargo/core/compiler/build_context/target_info.rs @@ -144,7 +144,7 @@ impl TargetInfo { &rustc.host, None, kind, - "RUSTFLAGS", + Flags::Rust, )?; let extra_fingerprint = kind.fingerprint_hash(); let mut process = rustc.workspace_process(); @@ -241,7 +241,7 @@ impl TargetInfo { &rustc.host, Some(&cfg), kind, - "RUSTFLAGS", + Flags::Rust, )?, rustdocflags: env_args( config, @@ -249,7 +249,7 @@ impl TargetInfo { &rustc.host, Some(&cfg), kind, - "RUSTDOCFLAGS", + Flags::Rustdoc, )?, cfg, supports_split_debuginfo, @@ -554,6 +554,28 @@ fn output_err_info(cmd: &ProcessBuilder, stdout: &str, stderr: &str) -> String { result } +#[derive(Debug, Copy, Clone)] +enum Flags { + Rust, + Rustdoc, +} + +impl Flags { + fn as_key(self) -> &'static str { + match self { + Flags::Rust => "rustflags", + Flags::Rustdoc => "rustdocflags", + } + } + + fn as_env(self) -> &'static str { + match self { + Flags::Rust => "RUSTFLAGS", + Flags::Rustdoc => "RUSTDOCFLAGS", + } + } +} + /// Acquire extra flags to pass to the compiler from various locations. /// /// The locations are: @@ -561,82 +583,105 @@ fn output_err_info(cmd: &ProcessBuilder, stdout: &str, stderr: &str) -> String { /// - the `CARGO_ENCODED_RUSTFLAGS` environment variable /// - the `RUSTFLAGS` environment variable /// -/// then if this was not found +/// then if none of those were found /// /// - `target.*.rustflags` from the config (.cargo/config) /// - `target.cfg(..).rustflags` from the config +/// - `host.*.rustflags` from the config if compiling a host artifact or without `--target` /// -/// then if neither of these were found +/// then if none of those were found /// /// - `build.rustflags` from the config /// -/// Note that if a `target` is specified, no args will be passed to host code (plugins, build -/// scripts, ...), even if it is the same as the target. +/// The behavior differs slightly when cross-compiling (or, specifically, when `--target` is +/// provided) for artifacts that are always built for the host (plugins, build scripts, ...). +/// For those artifacts, _only_ `host.*.rustflags` is respected, and no other configuration +/// sources, _regardless of the value of `target-applies-to-host`_. This is counterintuitive, but +/// necessary to retain bacwkards compatibility with older versions of Cargo. fn env_args( config: &Config, requested_kinds: &[CompileKind], host_triple: &str, target_cfg: Option<&[Cfg]>, kind: CompileKind, - name: &str, + flags: Flags, ) -> CargoResult> { - // 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: - // - // 1) If --target is not specified we just apply RUSTFLAGS to - // all builds; they are all going to have the same target. - // - // 2) If --target *is* specified then we only apply RUSTFLAGS - // to compilation units with the Target kind, which indicates - // it was chosen by the --target flag. + let target_applies_to_host = config.target_applies_to_host()?; + + // Host artifacts should not generally pick up rustflags from anywhere except [host]. // - // This means that, e.g., even if the specified --target is the - // same as the host, build scripts in plugins won't get - // RUSTFLAGS. - if requested_kinds != [CompileKind::Host] && kind.is_host() { - // This is probably a build script or plugin and we're - // compiling with --target. In this scenario there are - // no rustflags we can apply. - return Ok(Vec::new()); + // The one exception to this is if `target-applies-to-host = true`, which opts into a + // particular (inconsistent) past Cargo behavior where host artifacts _do_ pick up rustflags + // set elsewhere when `--target` isn't passed. + if kind.is_host() { + if target_applies_to_host && requested_kinds == [CompileKind::Host] { + // This is the past Cargo behavior where we fall back to the same logic as for other + // artifacts without --target. + } else { + // In all other cases, host artifacts just get flags from [host], regardless of + // --target. Or, phrased differently, no `--target` behaves the same as `--target + // `, and host artifacts are always "special" (they don't pick up `RUSTFLAGS` for + // example). + return Ok(rustflags_from_host(config, flags, host_triple)?.unwrap_or_else(Vec::new)); + } } + // All other artifacts pick up the RUSTFLAGS, [target.*], and [build], in that order. + // NOTE: It is impossible to have a [host] section and reach this logic with kind.is_host(), + // since [host] implies `target-applies-to-host = false`, which always early-returns above. + + if let Some(rustflags) = rustflags_from_env(flags) { + Ok(rustflags) + } else if let Some(rustflags) = + rustflags_from_target(config, host_triple, target_cfg, kind, flags)? + { + Ok(rustflags) + } else if let Some(rustflags) = rustflags_from_build(config, flags)? { + Ok(rustflags) + } else { + Ok(Vec::new()) + } +} + +fn rustflags_from_env(flags: Flags) -> Option> { // First try CARGO_ENCODED_RUSTFLAGS from the environment. // Prefer this over RUSTFLAGS since it's less prone to encoding errors. - if let Ok(a) = env::var(format!("CARGO_ENCODED_{}", name)) { + if let Ok(a) = env::var(format!("CARGO_ENCODED_{}", flags.as_env())) { if a.is_empty() { - return Ok(Vec::new()); + return Some(Vec::new()); } - return Ok(a.split('\x1f').map(str::to_string).collect()); + return Some(a.split('\x1f').map(str::to_string).collect()); } // Then try RUSTFLAGS from the environment - if let Ok(a) = env::var(name) { + if let Ok(a) = env::var(flags.as_env()) { let args = a .split(' ') .map(str::trim) .filter(|s| !s.is_empty()) .map(str::to_string); - return Ok(args.collect()); + return Some(args.collect()); } + // No rustflags to be collected from the environment + None +} + +fn rustflags_from_target( + config: &Config, + host_triple: &str, + target_cfg: Option<&[Cfg]>, + kind: CompileKind, + flag: Flags, +) -> CargoResult>> { let mut rustflags = Vec::new(); - let name = name - .chars() - .flat_map(|c| c.to_lowercase()) - .collect::(); // Then the target.*.rustflags value... let target = match &kind { CompileKind::Host => host_triple, CompileKind::Target(target) => target.short_name(), }; - let key = format!("target.{}.{}", target, name); + let key = format!("target.{}.{}", target, flag.as_key()); if let Some(args) = config.get::>(&key)? { rustflags.extend(args.as_slice().iter().cloned()); } @@ -656,22 +701,37 @@ fn env_args( }); } - if !rustflags.is_empty() { - return Ok(rustflags); + if rustflags.is_empty() { + Ok(None) + } else { + Ok(Some(rustflags)) } +} +fn rustflags_from_host( + config: &Config, + flag: Flags, + host_triple: &str, +) -> CargoResult>> { + let target_cfg = config.host_cfg_triple(host_triple)?; + let list = match flag { + Flags::Rust => &target_cfg.rustflags, + Flags::Rustdoc => { + // host.rustdocflags is not a thing, since it does not make sense + return Ok(None); + } + }; + Ok(list.as_ref().map(|l| l.val.as_slice().to_vec())) +} + +fn rustflags_from_build(config: &Config, flag: Flags) -> CargoResult>> { // Then the `build.rustflags` value. let build = config.build_config()?; - let list = if name == "rustflags" { - &build.rustflags - } else { - &build.rustdocflags + let list = match flag { + Flags::Rust => &build.rustflags, + Flags::Rustdoc => &build.rustdocflags, }; - if let Some(list) = list { - return Ok(list.as_slice().to_vec()); - } - - Ok(Vec::new()) + Ok(list.as_ref().map(|l| l.as_slice().to_vec())) } /// Collection of information about `rustc` and the host and target. diff --git a/src/doc/src/reference/unstable.md b/src/doc/src/reference/unstable.md index aa439016f14..327d5640872 100644 --- a/src/doc/src/reference/unstable.md +++ b/src/doc/src/reference/unstable.md @@ -509,14 +509,32 @@ CLI paths are relative to the current working directory. * Original Pull Request: [#9322](https://github.com/rust-lang/cargo/pull/9322) * Tracking Issue: [#9453](https://github.com/rust-lang/cargo/issues/9453) -The `target-applies-to-host` key in a config file can be used set the desired -behavior for passing target config flags to build scripts. - -It requires the `-Ztarget-applies-to-host` command-line option. - -The current default for `target-applies-to-host` is `true`, which will be -changed to `false` in the future, if `-Zhost-config` is used the new `false` -default will be set for `target-applies-to-host`. +Historically, Cargo's behavior for whether the `linker` and `rustflags` +configuration options from environment variables and `[target]` are +respected for build scripts, plugins, and other artifacts that are +_always_ built for the host platform has been somewhat inconsistent. +When `--target` is _not_ passed, Cargo respects the same `linker` and +`rustflags` for build scripts as for all other compile artifacts. When +`--target` _is_ passed, however, Cargo respects `linker` from +`[target.]`, and does not pick up any `rustflags` +configuration. This dual behavior is confusing, but also makes it +difficult to correctly configure builds where the host triple and the +target triple happen to be the same, but artifacts intended to run on +the build host should still be configured differently. + +`-Ztarget-applies-to-host` enables the top-level +`target-applies-to-host` setting in Cargo configuration files which +allows users to opt into different (and more consistent) behavior for +these properties. When `target-applies-to-host` is unset, or set to +`true`, in the configuration file, the existing Cargo behavior is +preserved (though see `-Zhost-config`, which changes that default). When +it is set to `false`, no options from `[target.]`, +`RUSTFLAGS`, or `[build]` are respected for host artifacts regardless of +whether `--target` is passed to Cargo. To customize artifacts intended +to be run on the host, use `[host]` ([`host-config`](#host-config)). + +In the future, `target-applies-to-host` may end up defaulting to `false` +to provide more sane and consistent default behavior. ```toml # config.toml @@ -536,8 +554,9 @@ such as build scripts that must run on the host system instead of the target system when cross compiling. It supports both generic and host arch specific tables. Matching host arch tables take precedence over generic host tables. -It requires the `-Zhost-config` and `-Ztarget-applies-to-host` command-line -options to be set. +It requires the `-Zhost-config` and `-Ztarget-applies-to-host` +command-line options to be set, and that `target-applies-to-host = +false` is set in the Cargo configuration file. ```toml # config.toml @@ -545,6 +564,7 @@ options to be set. linker = "/path/to/host/linker" [host.x86_64-unknown-linux-gnu] linker = "/path/to/host/arch/linker" +rustflags = ["-Clink-arg=--verbose"] [target.x86_64-unknown-linux-gnu] linker = "/path/to/target/linker" ``` diff --git a/tests/testsuite/build_script.rs b/tests/testsuite/build_script.rs index 40d8067d548..d0ef3a2e727 100644 --- a/tests/testsuite/build_script.rs +++ b/tests/testsuite/build_script.rs @@ -355,16 +355,7 @@ fn custom_build_env_var_rustc_linker_bad_host_target() { target ), ) - .file( - "build.rs", - r#" - use std::env; - - fn main() { - assert!(env::var("RUSTC_LINKER").unwrap().ends_with("/path/to/linker")); - } - "#, - ) + .file("build.rs", "fn main() {}") .file("src/lib.rs", "") .build(); @@ -411,12 +402,10 @@ fn custom_build_env_var_rustc_linker_host_target() { // no crate type set => linker never called => build succeeds if and // only if build.rs succeeds, despite linker binary not existing. - if cargo_test_support::is_nightly() { - p.cargo("build -Z target-applies-to-host --target") - .arg(&target) - .masquerade_as_nightly_cargo() - .run(); - } + p.cargo("build -Z target-applies-to-host --target") + .arg(&target) + .masquerade_as_nightly_cargo() + .run(); } #[cargo_test] @@ -448,13 +437,11 @@ fn custom_build_env_var_rustc_linker_host_target_env() { // no crate type set => linker never called => build succeeds if and // only if build.rs succeeds, despite linker binary not existing. - if cargo_test_support::is_nightly() { - p.cargo("build -Z target-applies-to-host --target") - .env("CARGO_TARGET_APPLIES_TO_HOST", "false") - .arg(&target) - .masquerade_as_nightly_cargo() - .run(); - } + p.cargo("build -Z target-applies-to-host --target") + .env("CARGO_TARGET_APPLIES_TO_HOST", "false") + .arg(&target) + .masquerade_as_nightly_cargo() + .run(); } #[cargo_test] @@ -471,43 +458,31 @@ fn custom_build_invalid_host_config_feature_flag() { target ), ) - .file( - "build.rs", - r#" - use std::env; - - fn main() { - assert!(env::var("RUSTC_LINKER").unwrap().ends_with("/path/to/linker")); - } - "#, - ) + .file("build.rs", "fn main() {}") .file("src/lib.rs", "") .build(); // build.rs should fail due to -Zhost-config being set without -Ztarget-applies-to-host - if cargo_test_support::is_nightly() { - p.cargo("build -Z host-config --target") - .arg(&target) - .masquerade_as_nightly_cargo() - .with_status(101) - .with_stderr_contains( - "\ + p.cargo("build -Z host-config --target") + .arg(&target) + .masquerade_as_nightly_cargo() + .with_status(101) + .with_stderr_contains( + "\ error: the -Zhost-config flag requires the -Ztarget-applies-to-host flag to be set ", - ) - .run(); - } + ) + .run(); } #[cargo_test] -fn custom_build_env_var_rustc_linker_host_target_with_bad_host_config() { +fn custom_build_linker_host_target_with_bad_host_config() { let target = rustc_host(); let p = project() .file( ".cargo/config", &format!( r#" - target-applies-to-host = true [host] linker = "/path/to/host/linker" [target.{}] @@ -516,38 +491,27 @@ fn custom_build_env_var_rustc_linker_host_target_with_bad_host_config() { target ), ) - .file( - "build.rs", - r#" - use std::env; - - fn main() { - assert!(env::var("RUSTC_LINKER").unwrap().ends_with("/path/to/target/linker")); - } - "#, - ) + .file("build.rs", "fn main() {}") .file("src/lib.rs", "") .build(); - // build.rs should fail due to bad target linker being set - if cargo_test_support::is_nightly() { - p.cargo("build -Z target-applies-to-host -Z host-config --verbose --target") + // build.rs should fail due to bad host linker being set + p.cargo("build -Z target-applies-to-host -Z host-config --verbose --target") .arg(&target) .masquerade_as_nightly_cargo() .with_status(101) .with_stderr_contains( "\ [COMPILING] foo v0.0.1 ([CWD]) -[RUNNING] `rustc --crate-name build_script_build build.rs [..]--crate-type bin [..]-C linker=[..]/path/to/target/linker [..]` -[ERROR] linker `[..]/path/to/target/linker` not found +[RUNNING] `rustc --crate-name build_script_build build.rs [..]--crate-type bin [..]-C linker=[..]/path/to/host/linker [..]` +[ERROR] linker `[..]/path/to/host/linker` not found " ) .run(); - } } #[cargo_test] -fn custom_build_env_var_rustc_linker_bad_host() { +fn custom_build_linker_bad_host() { let target = rustc_host(); let p = project() .file( @@ -562,22 +526,12 @@ fn custom_build_env_var_rustc_linker_bad_host() { target ), ) - .file( - "build.rs", - r#" - use std::env; - - fn main() { - assert!(env::var("RUSTC_LINKER").unwrap().ends_with("/path/to/target/linker")); - } - "#, - ) + .file("build.rs", "fn main() {}") .file("src/lib.rs", "") .build(); // build.rs should fail due to bad host linker being set - if cargo_test_support::is_nightly() { - p.cargo("build -Z target-applies-to-host -Z host-config --verbose --target") + p.cargo("build -Z target-applies-to-host -Z host-config --verbose --target") .arg(&target) .masquerade_as_nightly_cargo() .with_status(101) @@ -589,11 +543,10 @@ fn custom_build_env_var_rustc_linker_bad_host() { " ) .run(); - } } #[cargo_test] -fn custom_build_env_var_rustc_linker_bad_host_with_arch() { +fn custom_build_linker_bad_host_with_arch() { let target = rustc_host(); let p = project() .file( @@ -610,22 +563,12 @@ fn custom_build_env_var_rustc_linker_bad_host_with_arch() { target, target ), ) - .file( - "build.rs", - r#" - use std::env; - - fn main() { - assert!(env::var("RUSTC_LINKER").unwrap().ends_with("/path/to/target/linker")); - } - "#, - ) + .file("build.rs", "fn main() {}") .file("src/lib.rs", "") .build(); // build.rs should fail due to bad host linker being set - if cargo_test_support::is_nightly() { - p.cargo("build -Z target-applies-to-host -Z host-config --verbose --target") + p.cargo("build -Z target-applies-to-host -Z host-config --verbose --target") .arg(&target) .masquerade_as_nightly_cargo() .with_status(101) @@ -637,7 +580,6 @@ fn custom_build_env_var_rustc_linker_bad_host_with_arch() { " ) .run(); - } } #[cargo_test] @@ -670,17 +612,16 @@ fn custom_build_env_var_rustc_linker_cross_arch_host() { .file("src/lib.rs", "") .build(); - // build.rs should fail due to bad host linker being set - if cargo_test_support::is_nightly() { - p.cargo("build -Z target-applies-to-host -Z host-config --verbose --target") - .arg(&target) - .masquerade_as_nightly_cargo() - .run(); - } + // build.rs should be built fine since cross target != host target. + // assertion should succeed since it's still passed the target linker + p.cargo("build -Z target-applies-to-host -Z host-config --verbose --target") + .arg(&target) + .masquerade_as_nightly_cargo() + .run(); } #[cargo_test] -fn custom_build_env_var_rustc_linker_bad_cross_arch_host() { +fn custom_build_linker_bad_cross_arch_host() { let target = rustc_host(); let cross_target = cross_compile::alternate(); let p = project() @@ -698,22 +639,12 @@ fn custom_build_env_var_rustc_linker_bad_cross_arch_host() { cross_target, target ), ) - .file( - "build.rs", - r#" - use std::env; - - fn main() { - assert!(env::var("RUSTC_LINKER").unwrap().ends_with("/path/to/target/linker")); - } - "#, - ) + .file("build.rs", "fn main() {}") .file("src/lib.rs", "") .build(); // build.rs should fail due to bad host linker being set - if cargo_test_support::is_nightly() { - p.cargo("build -Z target-applies-to-host -Z host-config --verbose --target") + p.cargo("build -Z target-applies-to-host -Z host-config --verbose --target") .arg(&target) .masquerade_as_nightly_cargo() .with_status(101) @@ -725,7 +656,6 @@ fn custom_build_env_var_rustc_linker_bad_cross_arch_host() { " ) .run(); - } } #[cargo_test] diff --git a/tests/testsuite/rustflags.rs b/tests/testsuite/rustflags.rs index e1880cea137..e16b8ea005e 100644 --- a/tests/testsuite/rustflags.rs +++ b/tests/testsuite/rustflags.rs @@ -55,7 +55,6 @@ fn env_rustflags_normal_source() { fn env_rustflags_build_script() { // RUSTFLAGS should be passed to rustc for build scripts // when --target is not specified. - // In this test if --cfg foo is passed the build will fail. let p = project() .file( "Cargo.toml", @@ -70,9 +69,7 @@ fn env_rustflags_build_script() { .file( "build.rs", r#" - fn main() { } - #[cfg(not(foo))] - fn main() { } + fn main() { assert!(cfg!(foo)); } "#, ) .build(); @@ -243,7 +240,6 @@ fn env_rustflags_normal_source_with_target() { fn env_rustflags_build_script_with_target() { // RUSTFLAGS should not be passed to rustc for build scripts // when --target is specified. - // In this test if --cfg foo is passed the build will fail. let p = project() .file( "Cargo.toml", @@ -258,9 +254,7 @@ fn env_rustflags_build_script_with_target() { .file( "build.rs", r#" - fn main() { } - #[cfg(foo)] - fn main() { } + fn main() { assert!(!cfg!(foo)); } "#, ) .build(); @@ -272,6 +266,44 @@ fn env_rustflags_build_script_with_target() { .run(); } +#[cargo_test] +fn env_rustflags_build_script_with_target_doesnt_apply_to_host_kind() { + // RUSTFLAGS should *not* be passed to rustc for build scripts when --target is specified as the + // host triple even if target-applies-to-host-kind is enabled, to match legacy Cargo behavior. + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + build = "build.rs" + "#, + ) + .file("src/lib.rs", "") + .file( + "build.rs", + r#" + fn main() { assert!(!cfg!(foo)); } + "#, + ) + .file( + ".cargo/config.toml", + r#" + target-applies-to-host = true + "#, + ) + .build(); + + let host = rustc_host(); + p.cargo("build --target") + .masquerade_as_nightly_cargo() + .arg(host) + .arg("-Ztarget-applies-to-host") + .env("RUSTFLAGS", "--cfg foo") + .run(); +} + #[cargo_test] fn env_rustflags_build_script_dep_with_target() { // RUSTFLAGS should not be passed to rustc for build scripts @@ -478,7 +510,6 @@ fn build_rustflags_normal_source() { fn build_rustflags_build_script() { // RUSTFLAGS should be passed to rustc for build scripts // when --target is not specified. - // In this test if --cfg foo is passed the build will fail. let p = project() .file( "Cargo.toml", @@ -493,9 +524,7 @@ fn build_rustflags_build_script() { .file( "build.rs", r#" - fn main() { } - #[cfg(not(foo))] - fn main() { } + fn main() { assert!(cfg!(foo)); } "#, ) .file( @@ -664,7 +693,7 @@ fn build_rustflags_normal_source_with_target() { let host = &rustc_host(); - // Use RUSTFLAGS to pass an argument that will generate an error + // Use build.rustflags to pass an argument that will generate an error p.cargo("build --lib --target") .arg(host) .with_status(101) @@ -696,7 +725,6 @@ fn build_rustflags_normal_source_with_target() { fn build_rustflags_build_script_with_target() { // RUSTFLAGS should not be passed to rustc for build scripts // when --target is specified. - // In this test if --cfg foo is passed the build will fail. let p = project() .file( "Cargo.toml", @@ -711,9 +739,7 @@ fn build_rustflags_build_script_with_target() { .file( "build.rs", r#" - fn main() { } - #[cfg(foo)] - fn main() { } + fn main() { assert!(!cfg!(foo)); } "#, ) .file( @@ -993,6 +1019,187 @@ fn target_rustflags_normal_source() { .run(); } +#[cargo_test] +fn target_rustflags_also_for_build_scripts() { + let p = project() + .file("src/lib.rs", "") + .file( + "build.rs", + r#" + fn main() { assert!(cfg!(foo)); } + "#, + ) + .file( + ".cargo/config", + &format!( + " + [target.{}] + rustflags = [\"--cfg=foo\"] + ", + rustc_host() + ), + ) + .build(); + + p.cargo("build").run(); +} + +#[cargo_test] +fn target_rustflags_not_for_build_scripts_with_target() { + let host = rustc_host(); + let p = project() + .file("src/lib.rs", "") + .file( + "build.rs", + r#" + fn main() { assert!(!cfg!(foo)); } + "#, + ) + .file( + ".cargo/config", + &format!( + " + [target.{}] + rustflags = [\"--cfg=foo\"] + ", + host + ), + ) + .build(); + + p.cargo("build --target").arg(host).run(); + + // Enabling -Ztarget-applies-to-host should not make a difference without the config setting + p.cargo("build --target") + .arg(host) + .masquerade_as_nightly_cargo() + .arg("-Ztarget-applies-to-host") + .run(); + + // Even with the setting, the rustflags from `target.` should not apply, to match the legacy + // Cargo behavior. + p.change_file( + ".cargo/config", + &format!( + " + target-applies-to-host = true + + [target.{}] + rustflags = [\"--cfg=foo\"] + ", + host + ), + ); + p.cargo("build --target") + .arg(host) + .masquerade_as_nightly_cargo() + .arg("-Ztarget-applies-to-host") + .run(); +} + +#[cargo_test] +fn build_rustflags_for_build_scripts() { + let host = rustc_host(); + let p = project() + .file("src/lib.rs", "") + .file( + "build.rs", + r#" + fn main() { assert!(cfg!(foo)); } + "#, + ) + .file( + ".cargo/config", + " + [build] + rustflags = [\"--cfg=foo\"] + ", + ) + .build(); + + // With "legacy" behavior, build.rustflags should apply to build scripts without --target + p.cargo("build").run(); + + // But should _not_ apply _with_ --target + p.cargo("build --target") + .arg(host) + .with_status(101) + .with_stderr_contains("[..]assertion failed[..]") + .run(); + + // Enabling -Ztarget-applies-to-host should not make a difference without the config setting + p.cargo("build") + .masquerade_as_nightly_cargo() + .arg("-Ztarget-applies-to-host") + .run(); + p.cargo("build --target") + .arg(host) + .masquerade_as_nightly_cargo() + .arg("-Ztarget-applies-to-host") + .with_status(101) + .with_stderr_contains("[..]assertion failed[..]") + .run(); + + // When set to false though, the "proper" behavior where host artifacts _only_ pick up on + // [host] should be applied. + p.change_file( + ".cargo/config", + " + target-applies-to-host = false + + [build] + rustflags = [\"--cfg=foo\"] + ", + ); + p.cargo("build") + .masquerade_as_nightly_cargo() + .arg("-Ztarget-applies-to-host") + .with_status(101) + .with_stderr_contains("[..]assertion failed[..]") + .run(); + p.cargo("build --target") + .arg(host) + .masquerade_as_nightly_cargo() + .arg("-Ztarget-applies-to-host") + .with_status(101) + .with_stderr_contains("[..]assertion failed[..]") + .run(); +} + +#[cargo_test] +fn host_rustflags_for_build_scripts() { + let host = rustc_host(); + let p = project() + .file("src/lib.rs", "") + .file( + "build.rs", + r#" + // Ensure that --cfg=foo is passed. + fn main() { assert!(cfg!(foo)); } + "#, + ) + .file( + ".cargo/config", + &format!( + " + target-applies-to-host = false + + [host.{}] + rustflags = [\"--cfg=foo\"] + ", + host + ), + ) + .build(); + + p.cargo("build --target") + .arg(host) + .masquerade_as_nightly_cargo() + .arg("-Ztarget-applies-to-host") + .arg("-Zhost-config") + .run(); +} + // target.{}.rustflags takes precedence over build.rustflags #[cargo_test] fn target_rustflags_precedence() { @@ -1445,3 +1652,22 @@ fn remap_path_prefix_works() { .with_stdout("/foo/home/.cargo/registry/src/[..]/bar-0.1.0/src/lib.rs") .run(); } + +#[cargo_test] +fn host_config_rustflags_with_target() { + // regression test for https://github.com/rust-lang/cargo/issues/10206 + let p = project() + .file("src/lib.rs", "") + .file("build.rs.rs", "fn main() { assert!(cfg!(foo)); }") + .file(".cargo/config.toml", "target-applies-to-host = false") + .build(); + + p.cargo("build") + .masquerade_as_nightly_cargo() + .arg("-Zhost-config") + .arg("-Ztarget-applies-to-host") + .arg("-Zunstable-options") + .arg("--config") + .arg("host.rustflags=[\"--cfg=foo\"]") + .run(); +}