Skip to content

Commit 1ef1e0a

Browse files
committed
Auto merge of #10473 - weihanglo:multitarget-config, r=ehuss
Support `-Zmultitarget` in cargo config
2 parents 28afa1e + c18275b commit 1ef1e0a

File tree

4 files changed

+231
-21
lines changed

4 files changed

+231
-21
lines changed

src/cargo/core/compiler/compile_kind.rs

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -52,33 +52,31 @@ impl CompileKind {
5252
config: &Config,
5353
targets: &[String],
5454
) -> CargoResult<Vec<CompileKind>> {
55-
if targets.len() > 1 && !config.cli_unstable().multitarget {
56-
bail!("specifying multiple `--target` flags requires `-Zmultitarget`")
57-
}
58-
if !targets.is_empty() {
59-
return Ok(targets
55+
let dedup = |targets: &[String]| {
56+
Ok(targets
6057
.iter()
6158
.map(|value| Ok(CompileKind::Target(CompileTarget::new(value)?)))
6259
// First collect into a set to deduplicate any `--target` passed
6360
// more than once...
6461
.collect::<CargoResult<BTreeSet<_>>>()?
6562
// ... then generate a flat list for everything else to use.
6663
.into_iter()
67-
.collect());
68-
}
69-
let kind = match &config.build_config()?.target {
70-
Some(val) => {
71-
let value = if val.raw_value().ends_with(".json") {
72-
let path = val.clone().resolve_path(config);
73-
path.to_str().expect("must be utf-8 in toml").to_string()
74-
} else {
75-
val.raw_value().to_string()
76-
};
77-
CompileKind::Target(CompileTarget::new(&value)?)
64+
.collect())
65+
};
66+
67+
if !targets.is_empty() {
68+
if targets.len() > 1 && !config.cli_unstable().multitarget {
69+
bail!("specifying multiple `--target` flags requires `-Zmultitarget`")
7870
}
79-
None => CompileKind::Host,
71+
return dedup(targets);
72+
}
73+
74+
let kinds = match &config.build_config()?.target {
75+
None => Ok(vec![CompileKind::Host]),
76+
Some(build_target_config) => dedup(&build_target_config.values(config)?),
8077
};
81-
Ok(vec![kind])
78+
79+
kinds
8280
}
8381

8482
/// Hash used for fingerprinting.

src/cargo/util/config/mod.rs

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2169,7 +2169,7 @@ pub struct CargoBuildConfig {
21692169
pub dep_info_basedir: Option<ConfigRelativePath>,
21702170
pub target_dir: Option<ConfigRelativePath>,
21712171
pub incremental: Option<bool>,
2172-
pub target: Option<ConfigRelativePath>,
2172+
pub target: Option<BuildTargetConfig>,
21732173
pub jobs: Option<u32>,
21742174
pub rustflags: Option<StringList>,
21752175
pub rustdocflags: Option<StringList>,
@@ -2180,6 +2180,61 @@ pub struct CargoBuildConfig {
21802180
pub out_dir: Option<ConfigRelativePath>,
21812181
}
21822182

2183+
/// Configuration for `build.target`.
2184+
///
2185+
/// Accepts in the following forms:
2186+
///
2187+
/// ```toml
2188+
/// target = "a"
2189+
/// target = ["a"]
2190+
/// target = ["a", "b"]
2191+
/// ```
2192+
#[derive(Debug, Deserialize)]
2193+
#[serde(transparent)]
2194+
pub struct BuildTargetConfig {
2195+
inner: Value<BuildTargetConfigInner>,
2196+
}
2197+
2198+
#[derive(Debug, Deserialize)]
2199+
#[serde(untagged)]
2200+
enum BuildTargetConfigInner {
2201+
One(String),
2202+
Many(Vec<String>),
2203+
}
2204+
2205+
impl BuildTargetConfig {
2206+
/// Gets values of `build.target` as a list of strings.
2207+
pub fn values(&self, config: &Config) -> CargoResult<Vec<String>> {
2208+
let map = |s: &String| {
2209+
if s.ends_with(".json") {
2210+
// Path to a target specification file (in JSON).
2211+
// <https://doc.rust-lang.org/rustc/targets/custom.html>
2212+
self.inner
2213+
.definition
2214+
.root(config)
2215+
.join(s)
2216+
.to_str()
2217+
.expect("must be utf-8 in toml")
2218+
.to_string()
2219+
} else {
2220+
// A string. Probably a target triple.
2221+
s.to_string()
2222+
}
2223+
};
2224+
let values = match &self.inner.val {
2225+
BuildTargetConfigInner::One(s) => vec![map(s)],
2226+
BuildTargetConfigInner::Many(v) => {
2227+
if !config.cli_unstable().multitarget {
2228+
bail!("specifying an array in `build.target` config value requires `-Zmultitarget`")
2229+
} else {
2230+
v.iter().map(map).collect()
2231+
}
2232+
}
2233+
};
2234+
Ok(values)
2235+
}
2236+
}
2237+
21832238
#[derive(Deserialize, Default)]
21842239
struct TermConfig {
21852240
verbose: Option<bool>,

src/doc/src/reference/unstable.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,12 @@ or running tests for both targets:
238238
cargo test --target x86_64-unknown-linux-gnu --target i686-unknown-linux-gnu
239239
```
240240

241+
This can also be specified in `.cargo/config.toml` files.
242+
243+
```toml
244+
[build]
245+
target = ["x86_64-unknown-linux-gnu", "i686-unknown-linux-gnu"]
246+
```
241247

242248
#### New `dir-name` attribute
243249

tests/testsuite/multitarget.rs

Lines changed: 153 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,44 @@ fn double_target_rejected() {
1010
.build();
1111

1212
p.cargo("build --target a --target b")
13-
.with_stderr("error: specifying multiple `--target` flags requires `-Zmultitarget`")
13+
.with_stderr("[ERROR] specifying multiple `--target` flags requires `-Zmultitarget`")
14+
.with_status(101)
15+
.run();
16+
}
17+
18+
#[cargo_test]
19+
fn array_of_target_rejected_with_config() {
20+
let p = project()
21+
.file("Cargo.toml", &basic_manifest("foo", "1.0.0"))
22+
.file("src/main.rs", "fn main() {}")
23+
.file(
24+
".cargo/config.toml",
25+
r#"
26+
[build]
27+
target = ["a", "b"]
28+
"#,
29+
)
30+
.build();
31+
32+
p.cargo("build")
33+
.with_stderr(
34+
"[ERROR] specifying an array in `build.target` config value requires `-Zmultitarget`",
35+
)
36+
.with_status(101)
37+
.run();
38+
39+
p.change_file(
40+
".cargo/config.toml",
41+
r#"
42+
[build]
43+
target = ["a"]
44+
"#,
45+
);
46+
47+
p.cargo("build")
48+
.with_stderr(
49+
"[ERROR] specifying an array in `build.target` config value requires `-Zmultitarget`",
50+
)
1451
.with_status(101)
1552
.run();
1653
}
@@ -39,6 +76,35 @@ fn simple_build() {
3976
assert!(p.target_bin(t2, "foo").is_file());
4077
}
4178

79+
#[cargo_test]
80+
fn simple_build_with_config() {
81+
if cross_compile::disabled() {
82+
return;
83+
}
84+
let t1 = cross_compile::alternate();
85+
let t2 = rustc_host();
86+
let p = project()
87+
.file("Cargo.toml", &basic_manifest("foo", "1.0.0"))
88+
.file("src/main.rs", "fn main() {}")
89+
.file(
90+
".cargo/config.toml",
91+
&format!(
92+
r#"
93+
[unstable]
94+
multitarget = true
95+
[build]
96+
target = ["{t1}", "{t2}"]
97+
"#
98+
),
99+
)
100+
.build();
101+
102+
p.cargo("build").masquerade_as_nightly_cargo().run();
103+
104+
assert!(p.target_bin(t1, "foo").is_file());
105+
assert!(p.target_bin(t2, "foo").is_file());
106+
}
107+
42108
#[cargo_test]
43109
fn simple_test() {
44110
if !cross_compile::can_run_on_host() {
@@ -70,7 +136,7 @@ fn simple_run() {
70136
.build();
71137

72138
p.cargo("run -Z multitarget --target a --target b")
73-
.with_stderr("error: only one `--target` argument is supported")
139+
.with_stderr("[ERROR] only one `--target` argument is supported")
74140
.with_status(101)
75141
.masquerade_as_nightly_cargo()
76142
.run();
@@ -142,3 +208,88 @@ fn same_value_twice() {
142208

143209
assert!(p.target_bin(t, "foo").is_file());
144210
}
211+
212+
#[cargo_test]
213+
fn same_value_twice_with_config() {
214+
if cross_compile::disabled() {
215+
return;
216+
}
217+
let t = rustc_host();
218+
let p = project()
219+
.file("Cargo.toml", &basic_manifest("foo", "1.0.0"))
220+
.file("src/main.rs", "fn main() {}")
221+
.file(
222+
".cargo/config.toml",
223+
&format!(
224+
r#"
225+
[unstable]
226+
multitarget = true
227+
[build]
228+
target = ["{t}", "{t}"]
229+
"#
230+
),
231+
)
232+
.build();
233+
234+
p.cargo("build").masquerade_as_nightly_cargo().run();
235+
236+
assert!(p.target_bin(t, "foo").is_file());
237+
}
238+
239+
#[cargo_test]
240+
fn works_with_config_in_both_string_or_list() {
241+
if cross_compile::disabled() {
242+
return;
243+
}
244+
let t = rustc_host();
245+
let p = project()
246+
.file("Cargo.toml", &basic_manifest("foo", "1.0.0"))
247+
.file("src/main.rs", "fn main() {}")
248+
.file(
249+
".cargo/config.toml",
250+
&format!(
251+
r#"
252+
[unstable]
253+
multitarget = true
254+
[build]
255+
target = "{t}"
256+
"#
257+
),
258+
)
259+
.build();
260+
261+
p.cargo("build").masquerade_as_nightly_cargo().run();
262+
263+
assert!(p.target_bin(t, "foo").is_file());
264+
265+
p.cargo("clean").run();
266+
267+
p.change_file(
268+
".cargo/config.toml",
269+
&format!(
270+
r#"
271+
[unstable]
272+
multitarget = true
273+
[build]
274+
target = ["{t}"]
275+
"#
276+
),
277+
);
278+
279+
p.cargo("build").masquerade_as_nightly_cargo().run();
280+
281+
assert!(p.target_bin(t, "foo").is_file());
282+
}
283+
284+
#[cargo_test]
285+
fn works_with_env() {
286+
let t = rustc_host();
287+
let p = project()
288+
.file("Cargo.toml", &basic_manifest("foo", "1.0.0"))
289+
.file("src/main.rs", "fn main() {}")
290+
.build();
291+
292+
p.cargo("build").env("CARGO_BUILD_TARGET", t).run();
293+
294+
assert!(p.target_bin(t, "foo").is_file());
295+
}

0 commit comments

Comments
 (0)