Skip to content

Commit e931e47

Browse files
committed
Auto merge of #9486 - Dirbaio:link-arg-bin, r=ehuss
Add `cargo:rustc-link-arg-bin` flag. This PR implements a `cargo:rustc-link-arg-bin` command to specify per-binary link args from build scripts. This follows the suggestion from the tracking issue #9426. Syntax is `cargo:rustc-link-arg-bin=BIN_NAME=ARG` This was previously possible to do using the `#[link_args=".."]` attribute, but it was removed in rust-lang/rust#83820 in favor of the Cargo extra-link-args feature, which can currently not specify different link args for different bins in the same crate. This PR adds back the ability to do that.
2 parents f898eff + daffd0a commit e931e47

File tree

5 files changed

+99
-31
lines changed

5 files changed

+99
-31
lines changed

src/cargo/core/compiler/custom_build.rs

+22-4
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ pub struct BuildOutput {
2525
/// Names and link kinds of libraries, suitable for the `-l` flag.
2626
pub library_links: Vec<String>,
2727
/// Linker arguments suitable to be passed to `-C link-arg=<args>`
28-
pub linker_args: Vec<(Option<LinkType>, String)>,
28+
pub linker_args: Vec<(LinkType, String)>,
2929
/// Various `--cfg` flags to pass to the compiler.
3030
pub cfgs: Vec<String>,
3131
/// Additional environment variables to run the compiler with.
@@ -562,18 +562,36 @@ impl BuildOutput {
562562
"rustc-link-lib" => library_links.push(value.to_string()),
563563
"rustc-link-search" => library_paths.push(PathBuf::from(value)),
564564
"rustc-link-arg-cdylib" | "rustc-cdylib-link-arg" => {
565-
linker_args.push((Some(LinkType::Cdylib), value))
565+
linker_args.push((LinkType::Cdylib, value))
566566
}
567567
"rustc-link-arg-bins" => {
568568
if extra_link_arg {
569-
linker_args.push((Some(LinkType::Bin), value));
569+
linker_args.push((LinkType::Bin, value));
570+
} else {
571+
warnings.push(format!("cargo:{} requires -Zextra-link-arg flag", key));
572+
}
573+
}
574+
"rustc-link-arg-bin" => {
575+
if extra_link_arg {
576+
let parts = value.splitn(2, "=").collect::<Vec<_>>();
577+
if parts.len() == 2 {
578+
linker_args.push((
579+
LinkType::SingleBin(parts[0].to_string()),
580+
parts[1].to_string(),
581+
));
582+
} else {
583+
warnings.push(format!(
584+
"cargo:{} has invalid syntax: expected `cargo:{}=BIN=ARG`",
585+
key, key
586+
));
587+
}
570588
} else {
571589
warnings.push(format!("cargo:{} requires -Zextra-link-arg flag", key));
572590
}
573591
}
574592
"rustc-link-arg" => {
575593
if extra_link_arg {
576-
linker_args.push((None, value));
594+
linker_args.push((LinkType::All, value));
577595
} else {
578596
warnings.push(format!("cargo:{} requires -Zextra-link-arg flag", key));
579597
}

src/cargo/core/compiler/mod.rs

+18-23
Original file line numberDiff line numberDiff line change
@@ -62,29 +62,27 @@ use cargo_util::{paths, ProcessBuilder, ProcessError};
6262

6363
const RUSTDOC_CRATE_VERSION_FLAG: &str = "--crate-version";
6464

65-
#[derive(Copy, Clone, Hash, Debug, PartialEq, Eq)]
65+
#[derive(Clone, Hash, Debug, PartialEq, Eq)]
6666
pub enum LinkType {
67+
All,
6768
Cdylib,
6869
Bin,
70+
SingleBin(String),
6971
Test,
7072
Bench,
7173
Example,
7274
}
7375

74-
impl From<&super::Target> for Option<LinkType> {
75-
fn from(value: &super::Target) -> Self {
76-
if value.is_cdylib() {
77-
Some(LinkType::Cdylib)
78-
} else if value.is_bin() {
79-
Some(LinkType::Bin)
80-
} else if value.is_test() {
81-
Some(LinkType::Test)
82-
} else if value.is_bench() {
83-
Some(LinkType::Bench)
84-
} else if value.is_exe_example() {
85-
Some(LinkType::Example)
86-
} else {
87-
None
76+
impl LinkType {
77+
pub fn applies_to(&self, target: &Target) -> bool {
78+
match self {
79+
LinkType::All => true,
80+
LinkType::Cdylib => target.is_cdylib(),
81+
LinkType::Bin => target.is_bin(),
82+
LinkType::SingleBin(name) => target.is_bin() && target.name() == name,
83+
LinkType::Test => target.is_test(),
84+
LinkType::Bench => target.is_bench(),
85+
LinkType::Example => target.is_exe_example(),
8886
}
8987
}
9088
}
@@ -227,7 +225,6 @@ fn rustc(cx: &mut Context<'_, '_>, unit: &Unit, exec: &Arc<dyn Executor>) -> Car
227225
// If we are a binary and the package also contains a library, then we
228226
// don't pass the `-l` flags.
229227
let pass_l_flag = unit.target.is_lib() || !unit.pkg.targets().iter().any(|t| t.is_lib());
230-
let link_type = (&unit.target).into();
231228

232229
let dep_info_name = if cx.files().use_extra_filename(unit) {
233230
format!(
@@ -280,7 +277,7 @@ fn rustc(cx: &mut Context<'_, '_>, unit: &Unit, exec: &Arc<dyn Executor>) -> Car
280277
&script_outputs,
281278
&build_scripts,
282279
pass_l_flag,
283-
link_type,
280+
&target,
284281
current_id,
285282
)?;
286283
add_plugin_deps(&mut rustc, &script_outputs, &build_scripts, &root_output)?;
@@ -371,7 +368,7 @@ fn rustc(cx: &mut Context<'_, '_>, unit: &Unit, exec: &Arc<dyn Executor>) -> Car
371368
build_script_outputs: &BuildScriptOutputs,
372369
build_scripts: &BuildScripts,
373370
pass_l_flag: bool,
374-
link_type: Option<LinkType>,
371+
target: &Target,
375372
current_id: PackageId,
376373
) -> CargoResult<()> {
377374
for key in build_scripts.to_link.iter() {
@@ -396,11 +393,9 @@ fn rustc(cx: &mut Context<'_, '_>, unit: &Unit, exec: &Arc<dyn Executor>) -> Car
396393
}
397394
}
398395

399-
if link_type.is_some() {
400-
for (lt, arg) in &output.linker_args {
401-
if lt.is_none() || *lt == link_type {
402-
rustc.arg("-C").arg(format!("link-arg={}", arg));
403-
}
396+
for (lt, arg) in &output.linker_args {
397+
if lt.applies_to(&target) {
398+
rustc.arg("-C").arg(format!("link-arg={}", arg));
404399
}
405400
}
406401
}

src/cargo/util/config/target.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -134,13 +134,13 @@ fn parse_links_overrides(
134134
}
135135
"rustc-link-arg-cdylib" | "rustc-cdylib-link-arg" => {
136136
let args = value.list(key)?;
137-
let args = args.iter().map(|v| (Some(LinkType::Cdylib), v.0.clone()));
137+
let args = args.iter().map(|v| (LinkType::Cdylib, v.0.clone()));
138138
output.linker_args.extend(args);
139139
}
140140
"rustc-link-arg-bins" => {
141141
if extra_link_arg {
142142
let args = value.list(key)?;
143-
let args = args.iter().map(|v| (Some(LinkType::Bin), v.0.clone()));
143+
let args = args.iter().map(|v| (LinkType::Bin, v.0.clone()));
144144
output.linker_args.extend(args);
145145
} else {
146146
config.shell().warn(format!(
@@ -152,7 +152,7 @@ fn parse_links_overrides(
152152
"rustc-link-arg" => {
153153
if extra_link_arg {
154154
let args = value.list(key)?;
155-
let args = args.iter().map(|v| (None, v.0.clone()));
155+
let args = args.iter().map(|v| (LinkType::All, v.0.clone()));
156156
output.linker_args.extend(args);
157157
} else {
158158
config.shell().warn(format!(

src/doc/src/reference/unstable.md

+13-1
Original file line numberDiff line numberDiff line change
@@ -137,11 +137,13 @@ Cargo _or_ Rust features can be used.
137137
* Tracking Issue: [#9426](https://github.com/rust-lang/cargo/issues/9426)
138138
* Original Pull Request: [#7811](https://github.com/rust-lang/cargo/pull/7811)
139139

140-
The `-Z extra-link-arg` flag makes the following two instructions available
140+
The `-Z extra-link-arg` flag makes the following instructions available
141141
in build scripts:
142142

143143
* [`cargo:rustc-link-arg-bins=FLAG`](#rustc-link-arg-bins) – Passes custom
144144
flags to a linker for binaries.
145+
* [`cargo:rustc-link-arg-bin=BIN=FLAG`](#rustc-link-arg-bin) – Passes custom
146+
flags to a linker for the binary `BIN`.
145147
* [`cargo:rustc-link-arg=FLAG`](#rustc-link-arg) – Passes custom flags to a
146148
linker for benchmarks, binaries, `cdylib` crates, examples, and tests.
147149

@@ -155,6 +157,16 @@ to set a linker script or other linker options.
155157

156158
[link-arg]: ../../rustc/codegen-options/index.md#link-arg
157159

160+
<a id="rustc-link-arg-bin"></a>
161+
#### `cargo:rustc-link-arg-bin=BIN=FLAG`
162+
163+
The `rustc-link-arg-bin` instruction tells Cargo to pass the [`-C
164+
link-arg=FLAG` option][link-arg] to the compiler, but only when building
165+
the binary target with name `BIN`. Its usage is highly platform specific. It is useful
166+
to set a linker script or other linker options.
167+
168+
[link-arg]: ../../rustc/codegen-options/index.md#link-arg
169+
158170
<a id="rustc-link-arg"></a>
159171
#### `cargo:rustc-link-arg=FLAG`
160172

tests/testsuite/build_script_extra_link_arg.rs

+43
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,49 @@ fn build_script_extra_link_arg_bin() {
2626
.run();
2727
}
2828

29+
#[cargo_test]
30+
fn build_script_extra_link_arg_bin_single() {
31+
let p = project()
32+
.file(
33+
"Cargo.toml",
34+
r#"
35+
[package]
36+
37+
name = "foobar"
38+
version = "0.5.0"
39+
authors = ["[email protected]"]
40+
41+
[[bin]]
42+
name = "foo"
43+
[[bin]]
44+
name = "bar"
45+
"#,
46+
)
47+
.file("src/main.rs", "fn main() {}")
48+
.file(
49+
"build.rs",
50+
r#"
51+
fn main() {
52+
println!("cargo:rustc-link-arg-bins=--bogus-flag-all");
53+
println!("cargo:rustc-link-arg-bin=foo=--bogus-flag-foo");
54+
println!("cargo:rustc-link-arg-bin=bar=--bogus-flag-bar");
55+
}
56+
"#,
57+
)
58+
.build();
59+
60+
p.cargo("build -Zextra-link-arg -v")
61+
.masquerade_as_nightly_cargo()
62+
.without_status()
63+
.with_stderr_contains(
64+
"[RUNNING] `rustc --crate-name foo [..]-C link-arg=--bogus-flag-all -C link-arg=--bogus-flag-foo[..]",
65+
)
66+
.with_stderr_contains(
67+
"[RUNNING] `rustc --crate-name bar [..]-C link-arg=--bogus-flag-all -C link-arg=--bogus-flag-bar[..]",
68+
)
69+
.run();
70+
}
71+
2972
#[cargo_test]
3073
fn build_script_extra_link_arg() {
3174
let p = project()

0 commit comments

Comments
 (0)