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

Support -Zmultitarget in cargo config #10473

Merged
merged 8 commits into from
Mar 31, 2022
Merged
38 changes: 27 additions & 11 deletions src/cargo/core/compiler/compile_kind.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::core::Target;
use crate::util::config::BuildTargetConfigValue;
use crate::util::errors::CargoResult;
use crate::util::interning::InternedString;
use crate::util::{Config, StableHasher};
Expand Down Expand Up @@ -66,19 +67,34 @@ impl CompileKind {
.into_iter()
.collect());
}
let kind = match &config.build_config()?.target {
Some(val) => {
let value = if val.raw_value().ends_with(".json") {
let path = val.clone().resolve_path(config);
path.to_str().expect("must be utf-8 in toml").to_string()
} else {
val.raw_value().to_string()
};
CompileKind::Target(CompileTarget::new(&value)?)

let kinds = match &config.build_config()?.target {
None => vec![CompileKind::Host],
Some(build_target_config) => {
let values = build_target_config.values(config);
if values.len() > 1 && !config.cli_unstable().multitarget {
bail!("specifying multiple `--target` flags requires `-Zmultitarget`")
}
weihanglo marked this conversation as resolved.
Show resolved Hide resolved
values
.into_iter()
.map(|k| {
use BuildTargetConfigValue::*;
let value = match &k {
Path(p) => p.to_str().expect("must be utf-8 in toml"),
Simple(s) => s,
};
CompileTarget::new(value).map(CompileKind::Target)
})
// First collect into a set to deduplicate any `--target` passed
// more than once...
.collect::<CargoResult<BTreeSet<_>>>()?
// ... then generate a flat list for everything else to use.
.into_iter()
.collect()
}
None => CompileKind::Host,
};
Ok(vec![kind])

Ok(kinds)
}

/// Hash used for fingerprinting.
Expand Down
53 changes: 52 additions & 1 deletion src/cargo/util/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2169,7 +2169,7 @@ pub struct CargoBuildConfig {
pub dep_info_basedir: Option<ConfigRelativePath>,
pub target_dir: Option<ConfigRelativePath>,
pub incremental: Option<bool>,
pub target: Option<ConfigRelativePath>,
pub target: Option<BuildTargetConfig>,
pub jobs: Option<u32>,
pub rustflags: Option<StringList>,
pub rustdocflags: Option<StringList>,
Expand All @@ -2180,6 +2180,57 @@ pub struct CargoBuildConfig {
pub out_dir: Option<ConfigRelativePath>,
}

/// Configuration for `build.target`.
///
/// Accepts in the following forms:
///
/// ```toml
/// target = "a"
/// target = ["a"]
/// target = ["a", "b"]
/// ```
#[derive(Debug, Deserialize)]
#[serde(transparent)]
pub struct BuildTargetConfig {
inner: Value<BuildTargetConfigInner>,
}

#[derive(Debug, Deserialize)]
#[serde(untagged)]
enum BuildTargetConfigInner {
One(String),
Many(Vec<String>),
}

impl BuildTargetConfig {
/// Gets values of `build.target` as a list of [`BuildTargetConfigValue`].
pub fn values(&self, config: &Config) -> Vec<BuildTargetConfigValue<'_>> {
weihanglo marked this conversation as resolved.
Show resolved Hide resolved
let def_root = self.inner.definition.root(config);
fn map<'a>(s: &'a str, root: &Path) -> BuildTargetConfigValue<'a> {
weihanglo marked this conversation as resolved.
Show resolved Hide resolved
if s.ends_with(".json") {
// To absolute path.
BuildTargetConfigValue::Path(root.join(s))
} else {
BuildTargetConfigValue::Simple(s)
}
}
match &self.inner.val {
BuildTargetConfigInner::One(s) => vec![map(s, def_root)],
BuildTargetConfigInner::Many(v) => v.iter().map(|s| map(s, def_root)).collect(),
}
}
}

/// Represents a value of `build.target`.
#[derive(Debug)]
pub enum BuildTargetConfigValue<'a> {
/// Path to a target specification file (in JSON).
/// <https://doc.rust-lang.org/rustc/targets/custom.html>
Path(PathBuf),
/// A string. Probably a target triple.
Simple(&'a str),
}

#[derive(Deserialize, Default)]
struct TermConfig {
verbose: Option<bool>,
Expand Down
6 changes: 6 additions & 0 deletions src/doc/src/reference/unstable.md
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,12 @@ or running tests for both targets:
cargo test --target x86_64-unknown-linux-gnu --target i686-unknown-linux-gnu
```

This can also be specified in `.cargo/config.toml` files.

```toml
[build]
target = ["x86_64-unknown-linux-gnu", "i686-unknown-linux-gnu"]
```

#### New `dir-name` attribute

Expand Down
141 changes: 139 additions & 2 deletions tests/testsuite/multitarget.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,27 @@ fn double_target_rejected() {
.build();

p.cargo("build --target a --target b")
.with_stderr("error: specifying multiple `--target` flags requires `-Zmultitarget`")
.with_stderr("[ERROR] specifying multiple `--target` flags requires `-Zmultitarget`")
.with_status(101)
.run();
}

#[cargo_test]
fn double_target_rejected_with_config() {
let p = project()
.file("Cargo.toml", &basic_manifest("foo", "1.0.0"))
.file("src/main.rs", "fn main() {}")
.file(
".cargo/config.toml",
r#"
[build]
target = ["a", "b"]
"#,
)
.build();

p.cargo("build")
.with_stderr("[ERROR] specifying multiple `--target` flags requires `-Zmultitarget`")
.with_status(101)
.run();
}
Expand Down Expand Up @@ -39,6 +59,35 @@ fn simple_build() {
assert!(p.target_bin(t2, "foo").is_file());
}

#[cargo_test]
fn simple_build_with_config() {
if cross_compile::disabled() {
return;
}
let t1 = cross_compile::alternate();
let t2 = rustc_host();
let p = project()
.file("Cargo.toml", &basic_manifest("foo", "1.0.0"))
.file("src/main.rs", "fn main() {}")
.file(
".cargo/config.toml",
&format!(
r#"
[unstable]
multitarget = true
[build]
target = ["{t1}", "{t2}"]
"#
),
)
.build();

p.cargo("build").masquerade_as_nightly_cargo().run();

assert!(p.target_bin(t1, "foo").is_file());
assert!(p.target_bin(t2, "foo").is_file());
}

#[cargo_test]
fn simple_test() {
if !cross_compile::can_run_on_host() {
Expand Down Expand Up @@ -70,7 +119,7 @@ fn simple_run() {
.build();

p.cargo("run -Z multitarget --target a --target b")
.with_stderr("error: only one `--target` argument is supported")
.with_stderr("[ERROR] only one `--target` argument is supported")
.with_status(101)
.masquerade_as_nightly_cargo()
.run();
Expand Down Expand Up @@ -142,3 +191,91 @@ fn same_value_twice() {

assert!(p.target_bin(t, "foo").is_file());
}

#[cargo_test]
fn same_value_twice_with_config() {
if cross_compile::disabled() {
return;
}
let t = rustc_host();
let p = project()
.file("Cargo.toml", &basic_manifest("foo", "1.0.0"))
.file("src/main.rs", "fn main() {}")
.file(
".cargo/config.toml",
&format!(
r#"
[unstable]
multitarget = true
[build]
target = ["{t}", "{t}"]
"#
),
)
.build();

p.cargo("build").masquerade_as_nightly_cargo().run();

assert!(p.target_bin(t, "foo").is_file());
}

#[cargo_test]
fn works_with_config_in_both_string_or_list() {
if cross_compile::disabled() {
return;
}
let t = rustc_host();
let p = project()
.file("Cargo.toml", &basic_manifest("foo", "1.0.0"))
.file("src/main.rs", "fn main() {}")
.file(
".cargo/config.toml",
&format!(
r#"
[unstable]
multitarget = true
[build]
target = "{t}"
"#
),
)
.build();

p.cargo("build").masquerade_as_nightly_cargo().run();

assert!(p.target_bin(t, "foo").is_file());

p.cargo("clean").run();

p.change_file(
".cargo/config.toml",
&format!(
r#"
[unstable]
multitarget = true
[build]
target = ["{t}"]
"#
),
);

p.cargo("build").masquerade_as_nightly_cargo().run();

assert!(p.target_bin(t, "foo").is_file());
}

#[cargo_test]
fn works_with_env() {
let t = rustc_host();
let p = project()
.file("Cargo.toml", &basic_manifest("foo", "1.0.0"))
.file("src/main.rs", "fn main() {}")
.build();

p.cargo("build")
.env("CARGO_BUILD_TARGET", t)
.masquerade_as_nightly_cargo()
weihanglo marked this conversation as resolved.
Show resolved Hide resolved
.run();

assert!(p.target_bin(t, "foo").is_file());
}