Skip to content

Commit

Permalink
Auto merge of #9486 - Dirbaio:link-arg-bin, r=ehuss
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
bors committed May 24, 2021
2 parents f898eff + daffd0a commit e931e47
Show file tree
Hide file tree
Showing 5 changed files with 99 additions and 31 deletions.
26 changes: 22 additions & 4 deletions src/cargo/core/compiler/custom_build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ pub struct BuildOutput {
/// Names and link kinds of libraries, suitable for the `-l` flag.
pub library_links: Vec<String>,
/// Linker arguments suitable to be passed to `-C link-arg=<args>`
pub linker_args: Vec<(Option<LinkType>, String)>,
pub linker_args: Vec<(LinkType, String)>,
/// Various `--cfg` flags to pass to the compiler.
pub cfgs: Vec<String>,
/// Additional environment variables to run the compiler with.
Expand Down Expand Up @@ -562,18 +562,36 @@ impl BuildOutput {
"rustc-link-lib" => library_links.push(value.to_string()),
"rustc-link-search" => library_paths.push(PathBuf::from(value)),
"rustc-link-arg-cdylib" | "rustc-cdylib-link-arg" => {
linker_args.push((Some(LinkType::Cdylib), value))
linker_args.push((LinkType::Cdylib, value))
}
"rustc-link-arg-bins" => {
if extra_link_arg {
linker_args.push((Some(LinkType::Bin), value));
linker_args.push((LinkType::Bin, value));
} else {
warnings.push(format!("cargo:{} requires -Zextra-link-arg flag", key));
}
}
"rustc-link-arg-bin" => {
if extra_link_arg {
let parts = value.splitn(2, "=").collect::<Vec<_>>();
if parts.len() == 2 {
linker_args.push((
LinkType::SingleBin(parts[0].to_string()),
parts[1].to_string(),
));
} else {
warnings.push(format!(
"cargo:{} has invalid syntax: expected `cargo:{}=BIN=ARG`",
key, key
));
}
} else {
warnings.push(format!("cargo:{} requires -Zextra-link-arg flag", key));
}
}
"rustc-link-arg" => {
if extra_link_arg {
linker_args.push((None, value));
linker_args.push((LinkType::All, value));
} else {
warnings.push(format!("cargo:{} requires -Zextra-link-arg flag", key));
}
Expand Down
41 changes: 18 additions & 23 deletions src/cargo/core/compiler/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,29 +62,27 @@ use cargo_util::{paths, ProcessBuilder, ProcessError};

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

#[derive(Copy, Clone, Hash, Debug, PartialEq, Eq)]
#[derive(Clone, Hash, Debug, PartialEq, Eq)]
pub enum LinkType {
All,
Cdylib,
Bin,
SingleBin(String),
Test,
Bench,
Example,
}

impl From<&super::Target> for Option<LinkType> {
fn from(value: &super::Target) -> Self {
if value.is_cdylib() {
Some(LinkType::Cdylib)
} else if value.is_bin() {
Some(LinkType::Bin)
} else if value.is_test() {
Some(LinkType::Test)
} else if value.is_bench() {
Some(LinkType::Bench)
} else if value.is_exe_example() {
Some(LinkType::Example)
} else {
None
impl LinkType {
pub fn applies_to(&self, target: &Target) -> bool {
match self {
LinkType::All => true,
LinkType::Cdylib => target.is_cdylib(),
LinkType::Bin => target.is_bin(),
LinkType::SingleBin(name) => target.is_bin() && target.name() == name,
LinkType::Test => target.is_test(),
LinkType::Bench => target.is_bench(),
LinkType::Example => target.is_exe_example(),
}
}
}
Expand Down Expand Up @@ -227,7 +225,6 @@ fn rustc(cx: &mut Context<'_, '_>, unit: &Unit, exec: &Arc<dyn Executor>) -> Car
// If we are a binary and the package also contains a library, then we
// don't pass the `-l` flags.
let pass_l_flag = unit.target.is_lib() || !unit.pkg.targets().iter().any(|t| t.is_lib());
let link_type = (&unit.target).into();

let dep_info_name = if cx.files().use_extra_filename(unit) {
format!(
Expand Down Expand Up @@ -280,7 +277,7 @@ fn rustc(cx: &mut Context<'_, '_>, unit: &Unit, exec: &Arc<dyn Executor>) -> Car
&script_outputs,
&build_scripts,
pass_l_flag,
link_type,
&target,
current_id,
)?;
add_plugin_deps(&mut rustc, &script_outputs, &build_scripts, &root_output)?;
Expand Down Expand Up @@ -371,7 +368,7 @@ fn rustc(cx: &mut Context<'_, '_>, unit: &Unit, exec: &Arc<dyn Executor>) -> Car
build_script_outputs: &BuildScriptOutputs,
build_scripts: &BuildScripts,
pass_l_flag: bool,
link_type: Option<LinkType>,
target: &Target,
current_id: PackageId,
) -> CargoResult<()> {
for key in build_scripts.to_link.iter() {
Expand All @@ -396,11 +393,9 @@ fn rustc(cx: &mut Context<'_, '_>, unit: &Unit, exec: &Arc<dyn Executor>) -> Car
}
}

if link_type.is_some() {
for (lt, arg) in &output.linker_args {
if lt.is_none() || *lt == link_type {
rustc.arg("-C").arg(format!("link-arg={}", arg));
}
for (lt, arg) in &output.linker_args {
if lt.applies_to(&target) {
rustc.arg("-C").arg(format!("link-arg={}", arg));
}
}
}
Expand Down
6 changes: 3 additions & 3 deletions src/cargo/util/config/target.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,13 +134,13 @@ fn parse_links_overrides(
}
"rustc-link-arg-cdylib" | "rustc-cdylib-link-arg" => {
let args = value.list(key)?;
let args = args.iter().map(|v| (Some(LinkType::Cdylib), v.0.clone()));
let args = args.iter().map(|v| (LinkType::Cdylib, v.0.clone()));
output.linker_args.extend(args);
}
"rustc-link-arg-bins" => {
if extra_link_arg {
let args = value.list(key)?;
let args = args.iter().map(|v| (Some(LinkType::Bin), v.0.clone()));
let args = args.iter().map(|v| (LinkType::Bin, v.0.clone()));
output.linker_args.extend(args);
} else {
config.shell().warn(format!(
Expand All @@ -152,7 +152,7 @@ fn parse_links_overrides(
"rustc-link-arg" => {
if extra_link_arg {
let args = value.list(key)?;
let args = args.iter().map(|v| (None, v.0.clone()));
let args = args.iter().map(|v| (LinkType::All, v.0.clone()));
output.linker_args.extend(args);
} else {
config.shell().warn(format!(
Expand Down
14 changes: 13 additions & 1 deletion src/doc/src/reference/unstable.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,11 +137,13 @@ Cargo _or_ Rust features can be used.
* Tracking Issue: [#9426](https://github.com/rust-lang/cargo/issues/9426)
* Original Pull Request: [#7811](https://github.com/rust-lang/cargo/pull/7811)

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

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

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

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

<a id="rustc-link-arg-bin"></a>
#### `cargo:rustc-link-arg-bin=BIN=FLAG`

The `rustc-link-arg-bin` instruction tells Cargo to pass the [`-C
link-arg=FLAG` option][link-arg] to the compiler, but only when building
the binary target with name `BIN`. Its usage is highly platform specific. It is useful
to set a linker script or other linker options.

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

<a id="rustc-link-arg"></a>
#### `cargo:rustc-link-arg=FLAG`

Expand Down
43 changes: 43 additions & 0 deletions tests/testsuite/build_script_extra_link_arg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,49 @@ fn build_script_extra_link_arg_bin() {
.run();
}

#[cargo_test]
fn build_script_extra_link_arg_bin_single() {
let p = project()
.file(
"Cargo.toml",
r#"
[package]
name = "foobar"
version = "0.5.0"
authors = ["wycats@example.com"]
[[bin]]
name = "foo"
[[bin]]
name = "bar"
"#,
)
.file("src/main.rs", "fn main() {}")
.file(
"build.rs",
r#"
fn main() {
println!("cargo:rustc-link-arg-bins=--bogus-flag-all");
println!("cargo:rustc-link-arg-bin=foo=--bogus-flag-foo");
println!("cargo:rustc-link-arg-bin=bar=--bogus-flag-bar");
}
"#,
)
.build();

p.cargo("build -Zextra-link-arg -v")
.masquerade_as_nightly_cargo()
.without_status()
.with_stderr_contains(
"[RUNNING] `rustc --crate-name foo [..]-C link-arg=--bogus-flag-all -C link-arg=--bogus-flag-foo[..]",
)
.with_stderr_contains(
"[RUNNING] `rustc --crate-name bar [..]-C link-arg=--bogus-flag-all -C link-arg=--bogus-flag-bar[..]",
)
.run();
}

#[cargo_test]
fn build_script_extra_link_arg() {
let p = project()
Expand Down

0 comments on commit e931e47

Please sign in to comment.