Skip to content

Commit 083cc9e

Browse files
committed
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
1 parent d1c0a9d commit 083cc9e

File tree

4 files changed

+292
-4
lines changed

4 files changed

+292
-4
lines changed

src/cargo/core/compiler/build_context/target_info.rs

+17-3
Original file line numberDiff line numberDiff line change
@@ -682,7 +682,6 @@ impl<'cfg> RustcTargetData<'cfg> {
682682
) -> CargoResult<RustcTargetData<'cfg>> {
683683
let config = ws.config();
684684
let rustc = config.load_global_rustc(Some(ws))?;
685-
let host_config = config.target_cfg_triple(&rustc.host)?;
686685
let host_info = TargetInfo::new(config, requested_kinds, &rustc, CompileKind::Host)?;
687686
let mut target_config = HashMap::new();
688687
let mut target_info = HashMap::new();
@@ -692,10 +691,25 @@ impl<'cfg> RustcTargetData<'cfg> {
692691
// `--target` flag is not specified. Since the unit_dependency code
693692
// needs access to the target config data, create a copy so that it
694693
// can be found. See `rebuild_unit_graph_shared` for why this is done.
695-
if requested_kinds.iter().any(CompileKind::is_host) {
694+
let host_config = if requested_kinds.iter().any(CompileKind::is_host) {
696695
let ct = CompileTarget::new(&rustc.host)?;
697696
target_info.insert(ct, host_info.clone());
698-
target_config.insert(ct, host_config.clone());
697+
let target_host_config = config.target_cfg_triple(&rustc.host)?;
698+
target_config.insert(ct, target_host_config.clone());
699+
target_host_config
700+
} else {
701+
config.host_cfg_triple(&rustc.host)?
702+
};
703+
704+
for kind in requested_kinds {
705+
if let CompileKind::Target(target) = *kind {
706+
let tcfg = config.target_cfg_triple(target.short_name())?;
707+
target_config.insert(target, tcfg);
708+
target_info.insert(
709+
target,
710+
TargetInfo::new(config, requested_kinds, &rustc, *kind)?,
711+
);
712+
}
699713
}
700714

701715
let mut res = RustcTargetData {

src/cargo/util/config/mod.rs

+5
Original file line numberDiff line numberDiff line change
@@ -1486,6 +1486,11 @@ impl Config {
14861486
.try_borrow_with(|| self.get::<RustdocExternMap>("doc.extern-map"))
14871487
}
14881488

1489+
/// Returns the `[host]` table definition for the given target triple.
1490+
pub fn host_cfg_triple(&self, target: &str) -> CargoResult<TargetConfig> {
1491+
target::load_host_triple(self, target)
1492+
}
1493+
14891494
/// Returns the `[target]` table definition for the given target triple.
14901495
pub fn target_cfg_triple(&self, target: &str) -> CargoResult<TargetConfig> {
14911496
target::load_target_triple(self, target)

src/cargo/util/config/target.rs

+30-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ pub struct TargetCfgConfig {
1919
pub other: BTreeMap<String, toml::Value>,
2020
}
2121

22-
/// Config definition of a `[target]` table.
22+
/// Config definition of a `[target]` table or `[host]`.
2323
#[derive(Debug, Clone)]
2424
pub struct TargetConfig {
2525
/// Process to run as a wrapper for `cargo run`, `test`, and `bench` commands.
@@ -64,6 +64,35 @@ pub(super) fn load_target_cfgs(config: &Config) -> CargoResult<Vec<(String, Targ
6464
Ok(result)
6565
}
6666

67+
/// Loads a single `[host]` table for the given triple.
68+
pub(super) fn load_host_triple(config: &Config, triple: &str) -> CargoResult<TargetConfig> {
69+
// This needs to get each field individually because it cannot fetch the
70+
// struct all at once due to `links_overrides`. Can't use `serde(flatten)`
71+
// because it causes serde to use `deserialize_map` which means the config
72+
// deserializer does not know which keys to deserialize, which means
73+
// environment variables would not work.
74+
let host_triple_key = ConfigKey::from_str(&format!("host.{}", triple));
75+
let host_prefix = match config.get_cv(&host_triple_key)? {
76+
Some(_) => format!("host.{}", triple),
77+
None => "host".to_string(),
78+
};
79+
let runner: OptValue<PathAndArgs> = config.get(&format!("{}.runner", host_prefix))?;
80+
let rustflags: OptValue<StringList> = config.get(&format!("{}.rustflags", host_prefix))?;
81+
let linker: OptValue<ConfigRelativePath> = config.get(&format!("{}.linker", host_prefix))?;
82+
// Links do not support environment variables.
83+
let target_key = ConfigKey::from_str(&host_prefix);
84+
let links_overrides = match config.get_table(&target_key)? {
85+
Some(links) => parse_links_overrides(&target_key, links.val, config)?,
86+
None => BTreeMap::new(),
87+
};
88+
Ok(TargetConfig {
89+
runner,
90+
rustflags,
91+
linker,
92+
links_overrides,
93+
})
94+
}
95+
6796
/// Loads a single `[target]` table for the given triple.
6897
pub(super) fn load_target_triple(config: &Config, triple: &str) -> CargoResult<TargetConfig> {
6998
// This needs to get each field individually because it cannot fetch the

tests/testsuite/build_script.rs

+240
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,246 @@ fn custom_build_env_var_rustc_linker() {
164164
p.cargo("build --target").arg(&target).run();
165165
}
166166

167+
#[cargo_test]
168+
fn custom_build_env_var_rustc_linker_bad_host_target() {
169+
let target = rustc_host();
170+
let p = project()
171+
.file(
172+
".cargo/config",
173+
&format!(
174+
r#"
175+
[target.{}]
176+
linker = "/path/to/linker"
177+
"#,
178+
target
179+
),
180+
)
181+
.file(
182+
"build.rs",
183+
r#"
184+
use std::env;
185+
186+
fn main() {
187+
assert!(env::var("RUSTC_LINKER").unwrap().ends_with("/path/to/linker"));
188+
}
189+
"#,
190+
)
191+
.file("src/lib.rs", "")
192+
.build();
193+
194+
// build.rs should fail since host == target when no target is set
195+
p.cargo("build --verbose")
196+
.with_status(101)
197+
.with_stderr_contains(
198+
"\
199+
[COMPILING] foo v0.0.1 ([CWD])
200+
[RUNNING] `rustc --crate-name build_script_build build.rs [..]--crate-type bin [..]-C linker=[..]/path/to/linker [..]`
201+
[ERROR] linker `[..]/path/to/linker` not found
202+
"
203+
)
204+
.run();
205+
}
206+
207+
#[cargo_test]
208+
fn custom_build_env_var_rustc_linker_host_target() {
209+
let target = rustc_host();
210+
let p = project()
211+
.file(
212+
".cargo/config",
213+
&format!(
214+
r#"
215+
[target.{}]
216+
linker = "/path/to/linker"
217+
"#,
218+
target
219+
),
220+
)
221+
.file(
222+
"build.rs",
223+
r#"
224+
use std::env;
225+
226+
fn main() {
227+
assert!(env::var("RUSTC_LINKER").unwrap().ends_with("/path/to/linker"));
228+
}
229+
"#,
230+
)
231+
.file("src/lib.rs", "")
232+
.build();
233+
234+
// no crate type set => linker never called => build succeeds if and
235+
// only if build.rs succeeds, despite linker binary not existing.
236+
p.cargo("build --target").arg(&target).run();
237+
}
238+
239+
#[cargo_test]
240+
fn custom_build_env_var_rustc_linker_bad_host() {
241+
let target = rustc_host();
242+
let p = project()
243+
.file(
244+
".cargo/config",
245+
&format!(
246+
r#"
247+
[host]
248+
linker = "/path/to/host/linker"
249+
[target.{}]
250+
linker = "/path/to/target/linker"
251+
"#,
252+
target
253+
),
254+
)
255+
.file(
256+
"build.rs",
257+
r#"
258+
use std::env;
259+
260+
fn main() {
261+
assert!(env::var("RUSTC_LINKER").unwrap().ends_with("/path/to/target/linker"));
262+
}
263+
"#,
264+
)
265+
.file("src/lib.rs", "")
266+
.build();
267+
268+
// build.rs should fail due to bad host linker being set
269+
p.cargo("build --verbose --target")
270+
.arg(&target)
271+
.with_status(101)
272+
.with_stderr_contains(
273+
"\
274+
[COMPILING] foo v0.0.1 ([CWD])
275+
[RUNNING] `rustc --crate-name build_script_build build.rs [..]--crate-type bin [..]-C linker=[..]/path/to/host/linker [..]`
276+
[ERROR] linker `[..]/path/to/host/linker` not found
277+
"
278+
)
279+
.run();
280+
}
281+
282+
#[cargo_test]
283+
fn custom_build_env_var_rustc_linker_bad_host_with_arch() {
284+
let target = rustc_host();
285+
let p = project()
286+
.file(
287+
".cargo/config",
288+
&format!(
289+
r#"
290+
[host]
291+
linker = "/path/to/host/linker"
292+
[host.{}]
293+
linker = "/path/to/host/arch/linker"
294+
[target.{}]
295+
linker = "/path/to/target/linker"
296+
"#,
297+
target, target
298+
),
299+
)
300+
.file(
301+
"build.rs",
302+
r#"
303+
use std::env;
304+
305+
fn main() {
306+
assert!(env::var("RUSTC_LINKER").unwrap().ends_with("/path/to/target/linker"));
307+
}
308+
"#,
309+
)
310+
.file("src/lib.rs", "")
311+
.build();
312+
313+
// build.rs should fail due to bad host linker being set
314+
p.cargo("build --verbose --target")
315+
.arg(&target)
316+
.with_status(101)
317+
.with_stderr_contains(
318+
"\
319+
[COMPILING] foo v0.0.1 ([CWD])
320+
[RUNNING] `rustc --crate-name build_script_build build.rs [..]--crate-type bin [..]-C linker=[..]/path/to/host/arch/linker [..]`
321+
[ERROR] linker `[..]/path/to/host/arch/linker` not found
322+
"
323+
)
324+
.run();
325+
}
326+
327+
#[cargo_test]
328+
fn custom_build_env_var_rustc_linker_cross_arch_host() {
329+
let target = rustc_host();
330+
let cross_target = cross_compile::alternate();
331+
let p = project()
332+
.file(
333+
".cargo/config",
334+
&format!(
335+
r#"
336+
[host.{}]
337+
linker = "/path/to/host/arch/linker"
338+
[target.{}]
339+
linker = "/path/to/target/linker"
340+
"#,
341+
cross_target, target
342+
),
343+
)
344+
.file(
345+
"build.rs",
346+
r#"
347+
use std::env;
348+
349+
fn main() {
350+
assert!(env::var("RUSTC_LINKER").unwrap().ends_with("/path/to/target/linker"));
351+
}
352+
"#,
353+
)
354+
.file("src/lib.rs", "")
355+
.build();
356+
357+
// build.rs should fail due to bad host linker being set
358+
p.cargo("build --verbose --target").arg(&target).run();
359+
}
360+
361+
#[cargo_test]
362+
fn custom_build_env_var_rustc_linker_bad_cross_arch_host() {
363+
let target = rustc_host();
364+
let cross_target = cross_compile::alternate();
365+
let p = project()
366+
.file(
367+
".cargo/config",
368+
&format!(
369+
r#"
370+
[host]
371+
linker = "/path/to/host/linker"
372+
[host.{}]
373+
linker = "/path/to/host/arch/linker"
374+
[target.{}]
375+
linker = "/path/to/target/linker"
376+
"#,
377+
cross_target, target
378+
),
379+
)
380+
.file(
381+
"build.rs",
382+
r#"
383+
use std::env;
384+
385+
fn main() {
386+
assert!(env::var("RUSTC_LINKER").unwrap().ends_with("/path/to/target/linker"));
387+
}
388+
"#,
389+
)
390+
.file("src/lib.rs", "")
391+
.build();
392+
393+
// build.rs should fail due to bad host linker being set
394+
p.cargo("build --verbose --target")
395+
.arg(&target)
396+
.with_status(101)
397+
.with_stderr_contains(
398+
"\
399+
[COMPILING] foo v0.0.1 ([CWD])
400+
[RUNNING] `rustc --crate-name build_script_build build.rs [..]--crate-type bin [..]-C linker=[..]/path/to/host/linker [..]`
401+
[ERROR] linker `[..]/path/to/host/linker` not found
402+
"
403+
)
404+
.run();
405+
}
406+
167407
#[cargo_test]
168408
fn custom_build_script_wrong_rustc_flags() {
169409
let p = project()

0 commit comments

Comments
 (0)