Auto merge of #9322 - jameshilliard:split-host, r=joshtriplett

Configure hosts separately from targets when --target is specified.

This prevents target configs from accidentally being picked up when cross compiling from hosts that have the same architecture as their targets.

closes #3349
This commit is contained in:
bors 2021-06-01 20:09:13 +00:00
commit 0cecbd6732
6 changed files with 514 additions and 9 deletions

View File

@ -682,10 +682,15 @@ impl<'cfg> RustcTargetData<'cfg> {
) -> CargoResult<RustcTargetData<'cfg>> {
let config = ws.config();
let rustc = config.load_global_rustc(Some(ws))?;
let host_config = config.target_cfg_triple(&rustc.host)?;
let host_info = TargetInfo::new(config, requested_kinds, &rustc, CompileKind::Host)?;
let mut target_config = HashMap::new();
let mut target_info = HashMap::new();
let target_applies_to_host = config.target_applies_to_host()?;
let host_info = TargetInfo::new(config, requested_kinds, &rustc, CompileKind::Host)?;
let host_config = if target_applies_to_host {
config.target_cfg_triple(&rustc.host)?
} else {
config.host_cfg_triple(&rustc.host)?
};
// This is a hack. The unit_dependency graph builder "pretends" that
// `CompileKind::Host` is `CompileKind::Target(host)` if the
@ -695,8 +700,8 @@ impl<'cfg> RustcTargetData<'cfg> {
if requested_kinds.iter().any(CompileKind::is_host) {
let ct = CompileTarget::new(&rustc.host)?;
target_info.insert(ct, host_info.clone());
target_config.insert(ct, host_config.clone());
}
target_config.insert(ct, config.target_cfg_triple(&rustc.host)?);
};
let mut res = RustcTargetData {
rustc,

View File

@ -600,6 +600,8 @@ unstable_cli_options!(
namespaced_features: bool = ("Allow features with `dep:` prefix"),
no_index_update: bool = ("Do not update the registry index even if the cache is outdated"),
panic_abort_tests: bool = ("Enable support to run tests with -Cpanic=abort"),
host_config: bool = ("Enable the [host] section in the .cargo/config.toml file"),
target_applies_to_host: bool = ("Enable the `target-applies-to-host` key in the .cargo/config.toml file"),
patch_in_config: bool = ("Allow `[patch]` sections in .cargo/config.toml files"),
rustdoc_map: bool = ("Allow passing external documentation mappings to rustdoc"),
separate_nightlies: bool = (HIDDEN),
@ -787,6 +789,8 @@ impl CliUnstable {
"panic-abort-tests" => self.panic_abort_tests = parse_empty(k, v)?,
"jobserver-per-rustc" => self.jobserver_per_rustc = parse_empty(k, v)?,
"configurable-env" => self.configurable_env = parse_empty(k, v)?,
"host-config" => self.host_config = parse_empty(k, v)?,
"target-applies-to-host" => self.target_applies_to_host = parse_empty(k, v)?,
"patch-in-config" => self.patch_in_config = parse_empty(k, v)?,
"features" => {
// For now this is still allowed (there are still some

View File

@ -1486,6 +1486,16 @@ impl Config {
.try_borrow_with(|| self.get::<RustdocExternMap>("doc.extern-map"))
}
/// Returns true if the `[target]` table should be applied to host targets.
pub fn target_applies_to_host(&self) -> CargoResult<bool> {
target::get_target_applies_to_host(self)
}
/// Returns the `[host]` table definition for the given target triple.
pub fn host_cfg_triple(&self, target: &str) -> CargoResult<TargetConfig> {
target::load_host_triple(self, target)
}
/// Returns the `[target]` table definition for the given target triple.
pub fn target_cfg_triple(&self, target: &str) -> CargoResult<TargetConfig> {
target::load_target_triple(self, target)

View File

@ -19,7 +19,7 @@ pub struct TargetCfgConfig {
pub other: BTreeMap<String, toml::Value>,
}
/// Config definition of a `[target]` table.
/// Config definition of a `[target]` table or `[host]`.
#[derive(Debug, Clone)]
pub struct TargetConfig {
/// Process to run as a wrapper for `cargo run`, `test`, and `bench` commands.
@ -64,18 +64,62 @@ pub(super) fn load_target_cfgs(config: &Config) -> CargoResult<Vec<(String, Targ
Ok(result)
}
/// Returns true if the `[target]` table should be applied to host targets.
pub(super) fn get_target_applies_to_host(config: &Config) -> CargoResult<bool> {
if config.cli_unstable().target_applies_to_host {
if let Ok(target_applies_to_host) = config.get::<bool>("target-applies-to-host") {
Ok(target_applies_to_host)
} else {
Ok(!config.cli_unstable().host_config)
}
} else {
if config.cli_unstable().host_config {
anyhow::bail!(
"the -Zhost-config flag requires the -Ztarget-applies-to-host flag to be set"
);
} else {
Ok(true)
}
}
}
/// Loads a single `[host]` table for the given triple.
pub(super) fn load_host_triple(config: &Config, triple: &str) -> CargoResult<TargetConfig> {
if config.cli_unstable().host_config {
let host_triple_prefix = format!("host.{}", triple);
let host_triple_key = ConfigKey::from_str(&host_triple_prefix);
let host_prefix = match config.get_cv(&host_triple_key)? {
Some(_) => host_triple_prefix,
None => "host".to_string(),
};
load_config_table(config, &host_prefix)
} else {
Ok(TargetConfig {
runner: None,
rustflags: None,
linker: None,
links_overrides: BTreeMap::new(),
})
}
}
/// Loads a single `[target]` table for the given triple.
pub(super) fn load_target_triple(config: &Config, triple: &str) -> CargoResult<TargetConfig> {
load_config_table(config, &format!("target.{}", triple))
}
/// Loads a single table for the given prefix.
fn load_config_table(config: &Config, prefix: &str) -> CargoResult<TargetConfig> {
// This needs to get each field individually because it cannot fetch the
// struct all at once due to `links_overrides`. Can't use `serde(flatten)`
// because it causes serde to use `deserialize_map` which means the config
// deserializer does not know which keys to deserialize, which means
// environment variables would not work.
let runner: OptValue<PathAndArgs> = config.get(&format!("target.{}.runner", triple))?;
let rustflags: OptValue<StringList> = config.get(&format!("target.{}.rustflags", triple))?;
let linker: OptValue<ConfigRelativePath> = config.get(&format!("target.{}.linker", triple))?;
let runner: OptValue<PathAndArgs> = config.get(&format!("{}.runner", prefix))?;
let rustflags: OptValue<StringList> = config.get(&format!("{}.rustflags", prefix))?;
let linker: OptValue<ConfigRelativePath> = config.get(&format!("{}.linker", prefix))?;
// Links do not support environment variables.
let target_key = ConfigKey::from_str(&format!("target.{}", triple));
let target_key = ConfigKey::from_str(prefix);
let links_overrides = match config.get_table(&target_key)? {
Some(links) => parse_links_overrides(&target_key, links.val, config)?,
None => BTreeMap::new(),

View File

@ -705,6 +705,61 @@ cargo +nightly -Zunstable-options -Zconfig-include --config somefile.toml build
CLI paths are relative to the current working directory.
### target-applies-to-host
* 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`.
```toml
# config.toml
target-applies-to-host = false
```
```console
cargo +nightly -Ztarget-applies-to-host build --target x86_64-unknown-linux-gnu
```
### host-config
* Original Pull Request: [#9322](https://github.com/rust-lang/cargo/pull/9322)
* Tracking Issue: [#9452](https://github.com/rust-lang/cargo/issues/9452)
The `host` key in a config file can be used pass flags to host build targets
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.
```toml
# config.toml
[host]
linker = "/path/to/host/linker"
[host.x86_64-unknown-linux-gnu]
linker = "/path/to/host/arch/linker"
[target.x86_64-unknown-linux-gnu]
linker = "/path/to/target/linker"
```
The generic `host` table above will be entirely ignored when building on a
`x86_64-unknown-linux-gnu` host as the `host.x86_64-unknown-linux-gnu` table
takes precedence.
Setting `-Zhost-config` changes the default for `target-applies-to-host` to
`false` from `true`.
```console
cargo +nightly -Ztarget-applies-to-host -Zhost-config build --target x86_64-unknown-linux-gnu
```
### unit-graph
* Tracking Issue: [#8002](https://github.com/rust-lang/cargo/issues/8002)

View File

@ -164,6 +164,393 @@ fn custom_build_env_var_rustc_linker() {
p.cargo("build --target").arg(&target).run();
}
#[cargo_test]
fn custom_build_env_var_rustc_linker_bad_host_target() {
let target = rustc_host();
let p = project()
.file(
".cargo/config",
&format!(
r#"
[target.{}]
linker = "/path/to/linker"
"#,
target
),
)
.file(
"build.rs",
r#"
use std::env;
fn main() {
assert!(env::var("RUSTC_LINKER").unwrap().ends_with("/path/to/linker"));
}
"#,
)
.file("src/lib.rs", "")
.build();
// build.rs should fail since host == target when no target is set
p.cargo("build --verbose")
.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/linker [..]`
[ERROR] linker `[..]/path/to/linker` not found
"
)
.run();
}
#[cargo_test]
fn custom_build_env_var_rustc_linker_host_target() {
let target = rustc_host();
let p = project()
.file(
".cargo/config",
&format!(
r#"
target-applies-to-host = false
[target.{}]
linker = "/path/to/linker"
"#,
target
),
)
.file(
"build.rs",
r#"
use std::env;
fn main() {
assert!(env::var("RUSTC_LINKER").unwrap().ends_with("/path/to/linker"));
}
"#,
)
.file("src/lib.rs", "")
.build();
// 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();
}
}
#[cargo_test]
fn custom_build_env_var_rustc_linker_host_target_env() {
let target = rustc_host();
let p = project()
.file(
".cargo/config",
&format!(
r#"
[target.{}]
linker = "/path/to/linker"
"#,
target
),
)
.file(
"build.rs",
r#"
use std::env;
fn main() {
assert!(env::var("RUSTC_LINKER").unwrap().ends_with("/path/to/linker"));
}
"#,
)
.file("src/lib.rs", "")
.build();
// 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();
}
}
#[cargo_test]
fn custom_build_invalid_host_config_feature_flag() {
let target = rustc_host();
let p = project()
.file(
".cargo/config",
&format!(
r#"
[target.{}]
linker = "/path/to/linker"
"#,
target
),
)
.file(
"build.rs",
r#"
use std::env;
fn main() {
assert!(env::var("RUSTC_LINKER").unwrap().ends_with("/path/to/linker"));
}
"#,
)
.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(
"\
error: the -Zhost-config flag requires the -Ztarget-applies-to-host flag to be set
",
)
.run();
}
}
#[cargo_test]
fn custom_build_env_var_rustc_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.{}]
linker = "/path/to/target/linker"
"#,
target
),
)
.file(
"build.rs",
r#"
use std::env;
fn main() {
assert!(env::var("RUSTC_LINKER").unwrap().ends_with("/path/to/target/linker"));
}
"#,
)
.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")
.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
"
)
.run();
}
}
#[cargo_test]
fn custom_build_env_var_rustc_linker_bad_host() {
let target = rustc_host();
let p = project()
.file(
".cargo/config",
&format!(
r#"
[host]
linker = "/path/to/host/linker"
[target.{}]
linker = "/path/to/target/linker"
"#,
target
),
)
.file(
"build.rs",
r#"
use std::env;
fn main() {
assert!(env::var("RUSTC_LINKER").unwrap().ends_with("/path/to/target/linker"));
}
"#,
)
.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()
.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/host/linker [..]`
[ERROR] linker `[..]/path/to/host/linker` not found
"
)
.run();
}
}
#[cargo_test]
fn custom_build_env_var_rustc_linker_bad_host_with_arch() {
let target = rustc_host();
let p = project()
.file(
".cargo/config",
&format!(
r#"
[host]
linker = "/path/to/host/linker"
[host.{}]
linker = "/path/to/host/arch/linker"
[target.{}]
linker = "/path/to/target/linker"
"#,
target, target
),
)
.file(
"build.rs",
r#"
use std::env;
fn main() {
assert!(env::var("RUSTC_LINKER").unwrap().ends_with("/path/to/target/linker"));
}
"#,
)
.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()
.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/host/arch/linker [..]`
[ERROR] linker `[..]/path/to/host/arch/linker` not found
"
)
.run();
}
}
#[cargo_test]
fn custom_build_env_var_rustc_linker_cross_arch_host() {
let target = rustc_host();
let cross_target = cross_compile::alternate();
let p = project()
.file(
".cargo/config",
&format!(
r#"
[host.{}]
linker = "/path/to/host/arch/linker"
[target.{}]
linker = "/path/to/target/linker"
"#,
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("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();
}
}
#[cargo_test]
fn custom_build_env_var_rustc_linker_bad_cross_arch_host() {
let target = rustc_host();
let cross_target = cross_compile::alternate();
let p = project()
.file(
".cargo/config",
&format!(
r#"
[host]
linker = "/path/to/host/linker"
[host.{}]
linker = "/path/to/host/arch/linker"
[target.{}]
linker = "/path/to/target/linker"
"#,
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("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()
.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/host/linker [..]`
[ERROR] linker `[..]/path/to/host/linker` not found
"
)
.run();
}
}
#[cargo_test]
fn custom_build_script_wrong_rustc_flags() {
let p = project()