Skip to content

Commit

Permalink
Auto merge of #8441 - reitermarkus:linkarg, r=alexcrichton
Browse files Browse the repository at this point in the history
Finish implementation of `-Zextra-link-arg`.

This is a continuation of #7811, which adds tests and a warning if the `-Zextra-link-arg` flag is not specified. Also moved the documentation into `unstable.md`.
  • Loading branch information
bors committed Nov 18, 2020
2 parents 668a6c6 + 51f7a44 commit 9051345
Show file tree
Hide file tree
Showing 7 changed files with 211 additions and 16 deletions.
40 changes: 35 additions & 5 deletions src/cargo/core/compiler/custom_build.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use super::job::{Freshness, Job, Work};
use super::{fingerprint, Context, Unit};
use super::{fingerprint, Context, LinkType, Unit};
use crate::core::compiler::context::Metadata;
use crate::core::compiler::job_queue::JobState;
use crate::core::{profiles::ProfileRoot, PackageId};
Expand All @@ -23,7 +23,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<String>,
pub linker_args: Vec<(Option<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 @@ -290,6 +290,8 @@ fn build_work(cx: &mut Context<'_, '_>, unit: &Unit) -> CargoResult<Job> {
paths::create_dir_all(&script_dir)?;
paths::create_dir_all(&script_out_dir)?;

let extra_link_arg = cx.bcx.config.cli_unstable().extra_link_arg;

// Prepare the unit of "dirty work" which will actually run the custom build
// command.
//
Expand Down Expand Up @@ -393,8 +395,13 @@ fn build_work(cx: &mut Context<'_, '_>, unit: &Unit) -> CargoResult<Job> {
paths::set_file_time_no_err(output_file, timestamp);
paths::write(&err_file, &output.stderr)?;
paths::write(&root_output_file, util::path2bytes(&script_out_dir)?)?;
let parsed_output =
BuildOutput::parse(&output.stdout, &pkg_name, &script_out_dir, &script_out_dir)?;
let parsed_output = BuildOutput::parse(
&output.stdout,
&pkg_name,
&script_out_dir,
&script_out_dir,
extra_link_arg,
)?;

if json_messages {
emit_build_output(state, &parsed_output, script_out_dir.as_path(), id)?;
Expand All @@ -418,6 +425,7 @@ fn build_work(cx: &mut Context<'_, '_>, unit: &Unit) -> CargoResult<Job> {
&pkg_name,
&prev_script_out_dir,
&script_out_dir,
extra_link_arg,
)?,
};

Expand Down Expand Up @@ -467,13 +475,15 @@ impl BuildOutput {
pkg_name: &str,
script_out_dir_when_generated: &Path,
script_out_dir: &Path,
extra_link_arg: bool,
) -> CargoResult<BuildOutput> {
let contents = paths::read_bytes(path)?;
BuildOutput::parse(
&contents,
pkg_name,
script_out_dir_when_generated,
script_out_dir,
extra_link_arg,
)
}

Expand All @@ -484,6 +494,7 @@ impl BuildOutput {
pkg_name: &str,
script_out_dir_when_generated: &Path,
script_out_dir: &Path,
extra_link_arg: bool,
) -> CargoResult<BuildOutput> {
let mut library_paths = Vec::new();
let mut library_links = Vec::new();
Expand Down Expand Up @@ -536,7 +547,23 @@ impl BuildOutput {
}
"rustc-link-lib" => library_links.push(value.to_string()),
"rustc-link-search" => library_paths.push(PathBuf::from(value)),
"rustc-cdylib-link-arg" => linker_args.push(value.to_string()),
"rustc-link-arg-cdylib" | "rustc-cdylib-link-arg" => {
linker_args.push((Some(LinkType::Cdylib), value))
}
"rustc-link-arg-bins" => {
if extra_link_arg {
linker_args.push((Some(LinkType::Bin), value));
} else {
warnings.push(format!("cargo:{} requires -Zextra-link-arg flag", key));
}
}
"rustc-link-arg" => {
if extra_link_arg {
linker_args.push((None, value));
} else {
warnings.push(format!("cargo:{} requires -Zextra-link-arg flag", key));
}
}
"rustc-cfg" => cfgs.push(value.to_string()),
"rustc-env" => env.push(BuildOutput::parse_rustc_env(&value, &whence)?),
"warning" => warnings.push(value.to_string()),
Expand Down Expand Up @@ -785,12 +812,15 @@ fn prev_build_output(cx: &mut Context<'_, '_>, unit: &Unit) -> (Option<BuildOutp
.and_then(|bytes| util::bytes2path(&bytes))
.unwrap_or_else(|_| script_out_dir.clone());

let extra_link_arg = cx.bcx.config.cli_unstable().extra_link_arg;

(
BuildOutput::parse_file(
&output_file,
&unit.pkg.to_string(),
&prev_script_out_dir,
&script_out_dir,
extra_link_arg,
)
.ok(),
prev_script_out_dir,
Expand Down
44 changes: 37 additions & 7 deletions src/cargo/core/compiler/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,33 @@ use crate::util::{internal, join_paths, paths, profile};

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

#[derive(Copy, Clone, Hash, Debug, PartialEq, Eq)]
pub enum LinkType {
Cdylib,
Bin,
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
}
}
}

/// A glorified callback for executing calls to rustc. Rather than calling rustc
/// directly, we'll use an `Executor`, giving clients an opportunity to intercept
/// the build calls.
Expand Down Expand Up @@ -196,7 +223,7 @@ 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 pass_cdylib_link_args = unit.target.is_cdylib();
let link_type = (&unit.target).into();

let dep_info_name = match cx.files().metadata(unit) {
Some(metadata) => format!("{}-{}.d", unit.target.crate_name(), metadata),
Expand Down Expand Up @@ -244,7 +271,7 @@ fn rustc(cx: &mut Context<'_, '_>, unit: &Unit, exec: &Arc<dyn Executor>) -> Car
&script_outputs,
&build_scripts,
pass_l_flag,
pass_cdylib_link_args,
link_type,
current_id,
)?;
add_plugin_deps(&mut rustc, &script_outputs, &build_scripts, &root_output)?;
Expand Down Expand Up @@ -326,7 +353,7 @@ fn rustc(cx: &mut Context<'_, '_>, unit: &Unit, exec: &Arc<dyn Executor>) -> Car
build_script_outputs: &BuildScriptOutputs,
build_scripts: &BuildScripts,
pass_l_flag: bool,
pass_cdylib_link_args: bool,
link_type: Option<LinkType>,
current_id: PackageId,
) -> CargoResult<()> {
for key in build_scripts.to_link.iter() {
Expand All @@ -339,6 +366,7 @@ fn rustc(cx: &mut Context<'_, '_>, unit: &Unit, exec: &Arc<dyn Executor>) -> Car
for path in output.library_paths.iter() {
rustc.arg("-L").arg(path);
}

if key.0 == current_id {
for cfg in &output.cfgs {
rustc.arg("--cfg").arg(cfg);
Expand All @@ -348,10 +376,12 @@ fn rustc(cx: &mut Context<'_, '_>, unit: &Unit, exec: &Arc<dyn Executor>) -> Car
rustc.arg("-l").arg(name);
}
}
if pass_cdylib_link_args {
for arg in output.linker_args.iter() {
let link_arg = format!("link-arg={}", arg);
rustc.arg("-C").arg(link_arg);
}

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));
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions src/cargo/core/features.rs
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,7 @@ pub struct CliUnstable {
pub terminal_width: Option<Option<usize>>,
pub namespaced_features: bool,
pub weak_dep_features: bool,
pub extra_link_arg: bool,
}

fn deserialize_build_std<'de, D>(deserializer: D) -> Result<Option<Vec<String>>, D::Error>
Expand Down Expand Up @@ -466,6 +467,7 @@ impl CliUnstable {
"terminal-width" => self.terminal_width = Some(parse_usize_opt(v)?),
"namespaced-features" => self.namespaced_features = parse_empty(k, v)?,
"weak-dep-features" => self.weak_dep_features = parse_empty(k, v)?,
"extra-link-arg" => self.extra_link_arg = parse_empty(k, v)?,
_ => bail!("unknown `-Z` flag specified: {}", k),
}

Expand Down
36 changes: 32 additions & 4 deletions src/cargo/util/config/target.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use super::{Config, ConfigKey, ConfigRelativePath, OptValue, PathAndArgs, StringList, CV};
use crate::core::compiler::BuildOutput;
use crate::core::compiler::{BuildOutput, LinkType};
use crate::util::CargoResult;
use serde::Deserialize;
use std::collections::{BTreeMap, HashMap};
Expand Down Expand Up @@ -77,7 +77,7 @@ pub(super) fn load_target_triple(config: &Config, triple: &str) -> CargoResult<T
// Links do not support environment variables.
let target_key = ConfigKey::from_str(&format!("target.{}", triple));
let links_overrides = match config.get_table(&target_key)? {
Some(links) => parse_links_overrides(&target_key, links.val)?,
Some(links) => parse_links_overrides(&target_key, links.val, &config)?,
None => BTreeMap::new(),
};
Ok(TargetConfig {
Expand All @@ -91,7 +91,10 @@ pub(super) fn load_target_triple(config: &Config, triple: &str) -> CargoResult<T
fn parse_links_overrides(
target_key: &ConfigKey,
links: HashMap<String, CV>,
config: &Config,
) -> CargoResult<BTreeMap<String, BuildOutput>> {
let extra_link_arg = config.cli_unstable().extra_link_arg;

let mut links_overrides = BTreeMap::new();
for (lib_name, value) in links {
// Skip these keys, it shares the namespace with `TargetConfig`.
Expand Down Expand Up @@ -129,9 +132,34 @@ fn parse_links_overrides(
.library_paths
.extend(list.iter().map(|v| PathBuf::from(&v.0)));
}
"rustc-cdylib-link-arg" => {
"rustc-link-arg-cdylib" | "rustc-cdylib-link-arg" => {
let args = value.list(key)?;
output.linker_args.extend(args.iter().map(|v| v.0.clone()));
let args = args.iter().map(|v| (Some(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()));
output.linker_args.extend(args);
} else {
config.shell().warn(format!(
"target config `{}.{}` requires -Zextra-link-arg flag",
target_key, key
))?;
}
}
"rustc-link-arg" => {
if extra_link_arg {
let args = value.list(key)?;
let args = args.iter().map(|v| (None, v.0.clone()));
output.linker_args.extend(args);
} else {
config.shell().warn(format!(
"target config `{}.{}` requires -Zextra-link-arg flag",
target_key, key
))?;
}
}
"rustc-cfg" => {
let list = value.list(key)?;
Expand Down
32 changes: 32 additions & 0 deletions src/doc/src/reference/unstable.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,38 @@ timings = 'yes'
Some unstable features will require you to specify the `cargo-features` key in
`Cargo.toml`.

### extra-link-arg
* Original Pull Request: [#7811](https://github.com/rust-lang/cargo/pull/7811)

The `-Z extra-link-arg` flag makes the following two 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=FLAG`](#rustc-link-arg) – Passes custom flags to a
linker for benchmarks, binaries, `cdylib` crates, examples, and tests.

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

The `rustc-link-arg-bins` instruction tells Cargo to pass the [`-C
link-arg=FLAG` option][link-arg] to the compiler, but only when building a
binary target. 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`

The `rustc-link-arg` instruction tells Cargo to pass the [`-C link-arg=FLAG`
option][link-arg] to the compiler, but only when building supported targets
(benchmarks, binaries, `cdylib` crates, examples, and tests). Its usage is
highly platform specific. It is useful to set the shared library version or
linker script.

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

### no-index-update
* Original Issue: [#3479](https://github.com/rust-lang/cargo/issues/3479)
* Tracking Issue: [#7404](https://github.com/rust-lang/cargo/issues/7404)
Expand Down
72 changes: 72 additions & 0 deletions tests/testsuite/build_script_extra_link_arg.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
//! Tests for -Zextra-link-arg.

use cargo_test_support::{basic_bin_manifest, project};

#[cargo_test]
fn build_script_extra_link_arg_bin() {
let p = project()
.file("Cargo.toml", &basic_bin_manifest("foo"))
.file("src/main.rs", "fn main() {}")
.file(
"build.rs",
r#"
fn main() {
println!("cargo:rustc-link-arg-bins=--this-is-a-bogus-flag");
}
"#,
)
.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=--this-is-a-bogus-flag[..]",
)
.run();
}

#[cargo_test]
fn build_script_extra_link_arg() {
let p = project()
.file("Cargo.toml", &basic_bin_manifest("foo"))
.file("src/main.rs", "fn main() {}")
.file(
"build.rs",
r#"
fn main() {
println!("cargo:rustc-link-arg=--this-is-a-bogus-flag");
}
"#,
)
.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=--this-is-a-bogus-flag[..]",
)
.run();
}

#[cargo_test]
fn build_script_extra_link_arg_warn_without_flag() {
let p = project()
.file("Cargo.toml", &basic_bin_manifest("foo"))
.file("src/main.rs", "fn main() {}")
.file(
"build.rs",
r#"
fn main() {
println!("cargo:rustc-link-arg=--this-is-a-bogus-flag");
}
"#,
)
.build();

p.cargo("build -v")
.with_status(0)
.with_stderr_contains("warning: cargo:rustc-link-arg requires -Zextra-link-arg flag")
.run();
}
1 change: 1 addition & 0 deletions tests/testsuite/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ mod build;
mod build_plan;
mod build_script;
mod build_script_env;
mod build_script_extra_link_arg;
mod cache_messages;
mod cargo_alias_config;
mod cargo_command;
Expand Down

0 comments on commit 9051345

Please sign in to comment.