Skip to content

Commit 9051345

Browse files
committed
Auto merge of #8441 - reitermarkus:linkarg, r=alexcrichton
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`.
2 parents 668a6c6 + 51f7a44 commit 9051345

File tree

7 files changed

+211
-16
lines changed

7 files changed

+211
-16
lines changed

src/cargo/core/compiler/custom_build.rs

+35-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use super::job::{Freshness, Job, Work};
2-
use super::{fingerprint, Context, Unit};
2+
use super::{fingerprint, Context, LinkType, Unit};
33
use crate::core::compiler::context::Metadata;
44
use crate::core::compiler::job_queue::JobState;
55
use crate::core::{profiles::ProfileRoot, PackageId};
@@ -23,7 +23,7 @@ pub struct BuildOutput {
2323
/// Names and link kinds of libraries, suitable for the `-l` flag.
2424
pub library_links: Vec<String>,
2525
/// Linker arguments suitable to be passed to `-C link-arg=<args>`
26-
pub linker_args: Vec<String>,
26+
pub linker_args: Vec<(Option<LinkType>, String)>,
2727
/// Various `--cfg` flags to pass to the compiler.
2828
pub cfgs: Vec<String>,
2929
/// Additional environment variables to run the compiler with.
@@ -290,6 +290,8 @@ fn build_work(cx: &mut Context<'_, '_>, unit: &Unit) -> CargoResult<Job> {
290290
paths::create_dir_all(&script_dir)?;
291291
paths::create_dir_all(&script_out_dir)?;
292292

293+
let extra_link_arg = cx.bcx.config.cli_unstable().extra_link_arg;
294+
293295
// Prepare the unit of "dirty work" which will actually run the custom build
294296
// command.
295297
//
@@ -393,8 +395,13 @@ fn build_work(cx: &mut Context<'_, '_>, unit: &Unit) -> CargoResult<Job> {
393395
paths::set_file_time_no_err(output_file, timestamp);
394396
paths::write(&err_file, &output.stderr)?;
395397
paths::write(&root_output_file, util::path2bytes(&script_out_dir)?)?;
396-
let parsed_output =
397-
BuildOutput::parse(&output.stdout, &pkg_name, &script_out_dir, &script_out_dir)?;
398+
let parsed_output = BuildOutput::parse(
399+
&output.stdout,
400+
&pkg_name,
401+
&script_out_dir,
402+
&script_out_dir,
403+
extra_link_arg,
404+
)?;
398405

399406
if json_messages {
400407
emit_build_output(state, &parsed_output, script_out_dir.as_path(), id)?;
@@ -418,6 +425,7 @@ fn build_work(cx: &mut Context<'_, '_>, unit: &Unit) -> CargoResult<Job> {
418425
&pkg_name,
419426
&prev_script_out_dir,
420427
&script_out_dir,
428+
extra_link_arg,
421429
)?,
422430
};
423431

@@ -467,13 +475,15 @@ impl BuildOutput {
467475
pkg_name: &str,
468476
script_out_dir_when_generated: &Path,
469477
script_out_dir: &Path,
478+
extra_link_arg: bool,
470479
) -> CargoResult<BuildOutput> {
471480
let contents = paths::read_bytes(path)?;
472481
BuildOutput::parse(
473482
&contents,
474483
pkg_name,
475484
script_out_dir_when_generated,
476485
script_out_dir,
486+
extra_link_arg,
477487
)
478488
}
479489

@@ -484,6 +494,7 @@ impl BuildOutput {
484494
pkg_name: &str,
485495
script_out_dir_when_generated: &Path,
486496
script_out_dir: &Path,
497+
extra_link_arg: bool,
487498
) -> CargoResult<BuildOutput> {
488499
let mut library_paths = Vec::new();
489500
let mut library_links = Vec::new();
@@ -536,7 +547,23 @@ impl BuildOutput {
536547
}
537548
"rustc-link-lib" => library_links.push(value.to_string()),
538549
"rustc-link-search" => library_paths.push(PathBuf::from(value)),
539-
"rustc-cdylib-link-arg" => linker_args.push(value.to_string()),
550+
"rustc-link-arg-cdylib" | "rustc-cdylib-link-arg" => {
551+
linker_args.push((Some(LinkType::Cdylib), value))
552+
}
553+
"rustc-link-arg-bins" => {
554+
if extra_link_arg {
555+
linker_args.push((Some(LinkType::Bin), value));
556+
} else {
557+
warnings.push(format!("cargo:{} requires -Zextra-link-arg flag", key));
558+
}
559+
}
560+
"rustc-link-arg" => {
561+
if extra_link_arg {
562+
linker_args.push((None, value));
563+
} else {
564+
warnings.push(format!("cargo:{} requires -Zextra-link-arg flag", key));
565+
}
566+
}
540567
"rustc-cfg" => cfgs.push(value.to_string()),
541568
"rustc-env" => env.push(BuildOutput::parse_rustc_env(&value, &whence)?),
542569
"warning" => warnings.push(value.to_string()),
@@ -785,12 +812,15 @@ fn prev_build_output(cx: &mut Context<'_, '_>, unit: &Unit) -> (Option<BuildOutp
785812
.and_then(|bytes| util::bytes2path(&bytes))
786813
.unwrap_or_else(|_| script_out_dir.clone());
787814

815+
let extra_link_arg = cx.bcx.config.cli_unstable().extra_link_arg;
816+
788817
(
789818
BuildOutput::parse_file(
790819
&output_file,
791820
&unit.pkg.to_string(),
792821
&prev_script_out_dir,
793822
&script_out_dir,
823+
extra_link_arg,
794824
)
795825
.ok(),
796826
prev_script_out_dir,

src/cargo/core/compiler/mod.rs

+37-7
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,33 @@ use crate::util::{internal, join_paths, paths, profile};
5959

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

62+
#[derive(Copy, Clone, Hash, Debug, PartialEq, Eq)]
63+
pub enum LinkType {
64+
Cdylib,
65+
Bin,
66+
Test,
67+
Bench,
68+
Example,
69+
}
70+
71+
impl From<&super::Target> for Option<LinkType> {
72+
fn from(value: &super::Target) -> Self {
73+
if value.is_cdylib() {
74+
Some(LinkType::Cdylib)
75+
} else if value.is_bin() {
76+
Some(LinkType::Bin)
77+
} else if value.is_test() {
78+
Some(LinkType::Test)
79+
} else if value.is_bench() {
80+
Some(LinkType::Bench)
81+
} else if value.is_exe_example() {
82+
Some(LinkType::Example)
83+
} else {
84+
None
85+
}
86+
}
87+
}
88+
6289
/// A glorified callback for executing calls to rustc. Rather than calling rustc
6390
/// directly, we'll use an `Executor`, giving clients an opportunity to intercept
6491
/// the build calls.
@@ -196,7 +223,7 @@ fn rustc(cx: &mut Context<'_, '_>, unit: &Unit, exec: &Arc<dyn Executor>) -> Car
196223
// If we are a binary and the package also contains a library, then we
197224
// don't pass the `-l` flags.
198225
let pass_l_flag = unit.target.is_lib() || !unit.pkg.targets().iter().any(|t| t.is_lib());
199-
let pass_cdylib_link_args = unit.target.is_cdylib();
226+
let link_type = (&unit.target).into();
200227

201228
let dep_info_name = match cx.files().metadata(unit) {
202229
Some(metadata) => format!("{}-{}.d", unit.target.crate_name(), metadata),
@@ -244,7 +271,7 @@ fn rustc(cx: &mut Context<'_, '_>, unit: &Unit, exec: &Arc<dyn Executor>) -> Car
244271
&script_outputs,
245272
&build_scripts,
246273
pass_l_flag,
247-
pass_cdylib_link_args,
274+
link_type,
248275
current_id,
249276
)?;
250277
add_plugin_deps(&mut rustc, &script_outputs, &build_scripts, &root_output)?;
@@ -326,7 +353,7 @@ fn rustc(cx: &mut Context<'_, '_>, unit: &Unit, exec: &Arc<dyn Executor>) -> Car
326353
build_script_outputs: &BuildScriptOutputs,
327354
build_scripts: &BuildScripts,
328355
pass_l_flag: bool,
329-
pass_cdylib_link_args: bool,
356+
link_type: Option<LinkType>,
330357
current_id: PackageId,
331358
) -> CargoResult<()> {
332359
for key in build_scripts.to_link.iter() {
@@ -339,6 +366,7 @@ fn rustc(cx: &mut Context<'_, '_>, unit: &Unit, exec: &Arc<dyn Executor>) -> Car
339366
for path in output.library_paths.iter() {
340367
rustc.arg("-L").arg(path);
341368
}
369+
342370
if key.0 == current_id {
343371
for cfg in &output.cfgs {
344372
rustc.arg("--cfg").arg(cfg);
@@ -348,10 +376,12 @@ fn rustc(cx: &mut Context<'_, '_>, unit: &Unit, exec: &Arc<dyn Executor>) -> Car
348376
rustc.arg("-l").arg(name);
349377
}
350378
}
351-
if pass_cdylib_link_args {
352-
for arg in output.linker_args.iter() {
353-
let link_arg = format!("link-arg={}", arg);
354-
rustc.arg("-C").arg(link_arg);
379+
}
380+
381+
if link_type.is_some() {
382+
for (lt, arg) in &output.linker_args {
383+
if lt.is_none() || *lt == link_type {
384+
rustc.arg("-C").arg(format!("link-arg={}", arg));
355385
}
356386
}
357387
}

src/cargo/core/features.rs

+2
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,7 @@ pub struct CliUnstable {
359359
pub terminal_width: Option<Option<usize>>,
360360
pub namespaced_features: bool,
361361
pub weak_dep_features: bool,
362+
pub extra_link_arg: bool,
362363
}
363364

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

src/cargo/util/config/target.rs

+32-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use super::{Config, ConfigKey, ConfigRelativePath, OptValue, PathAndArgs, StringList, CV};
2-
use crate::core::compiler::BuildOutput;
2+
use crate::core::compiler::{BuildOutput, LinkType};
33
use crate::util::CargoResult;
44
use serde::Deserialize;
55
use std::collections::{BTreeMap, HashMap};
@@ -77,7 +77,7 @@ pub(super) fn load_target_triple(config: &Config, triple: &str) -> CargoResult<T
7777
// Links do not support environment variables.
7878
let target_key = ConfigKey::from_str(&format!("target.{}", triple));
7979
let links_overrides = match config.get_table(&target_key)? {
80-
Some(links) => parse_links_overrides(&target_key, links.val)?,
80+
Some(links) => parse_links_overrides(&target_key, links.val, &config)?,
8181
None => BTreeMap::new(),
8282
};
8383
Ok(TargetConfig {
@@ -91,7 +91,10 @@ pub(super) fn load_target_triple(config: &Config, triple: &str) -> CargoResult<T
9191
fn parse_links_overrides(
9292
target_key: &ConfigKey,
9393
links: HashMap<String, CV>,
94+
config: &Config,
9495
) -> CargoResult<BTreeMap<String, BuildOutput>> {
96+
let extra_link_arg = config.cli_unstable().extra_link_arg;
97+
9598
let mut links_overrides = BTreeMap::new();
9699
for (lib_name, value) in links {
97100
// Skip these keys, it shares the namespace with `TargetConfig`.
@@ -129,9 +132,34 @@ fn parse_links_overrides(
129132
.library_paths
130133
.extend(list.iter().map(|v| PathBuf::from(&v.0)));
131134
}
132-
"rustc-cdylib-link-arg" => {
135+
"rustc-link-arg-cdylib" | "rustc-cdylib-link-arg" => {
133136
let args = value.list(key)?;
134-
output.linker_args.extend(args.iter().map(|v| v.0.clone()));
137+
let args = args.iter().map(|v| (Some(LinkType::Cdylib), v.0.clone()));
138+
output.linker_args.extend(args);
139+
}
140+
"rustc-link-arg-bins" => {
141+
if extra_link_arg {
142+
let args = value.list(key)?;
143+
let args = args.iter().map(|v| (Some(LinkType::Bin), v.0.clone()));
144+
output.linker_args.extend(args);
145+
} else {
146+
config.shell().warn(format!(
147+
"target config `{}.{}` requires -Zextra-link-arg flag",
148+
target_key, key
149+
))?;
150+
}
151+
}
152+
"rustc-link-arg" => {
153+
if extra_link_arg {
154+
let args = value.list(key)?;
155+
let args = args.iter().map(|v| (None, v.0.clone()));
156+
output.linker_args.extend(args);
157+
} else {
158+
config.shell().warn(format!(
159+
"target config `{}.{}` requires -Zextra-link-arg flag",
160+
target_key, key
161+
))?;
162+
}
135163
}
136164
"rustc-cfg" => {
137165
let list = value.list(key)?;

src/doc/src/reference/unstable.md

+32
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,38 @@ timings = 'yes'
2020
Some unstable features will require you to specify the `cargo-features` key in
2121
`Cargo.toml`.
2222

23+
### extra-link-arg
24+
* Original Pull Request: [#7811](https://github.com/rust-lang/cargo/pull/7811)
25+
26+
The `-Z extra-link-arg` flag makes the following two instructions available
27+
in build scripts:
28+
29+
* [`cargo:rustc-link-arg-bins=FLAG`](#rustc-link-arg-bins) – Passes custom
30+
flags to a linker for binaries.
31+
* [`cargo:rustc-link-arg=FLAG`](#rustc-link-arg) – Passes custom flags to a
32+
linker for benchmarks, binaries, `cdylib` crates, examples, and tests.
33+
34+
<a id="rustc-link-arg-bins"></a>
35+
#### `cargo:rustc-link-arg-bins=FLAG`
36+
37+
The `rustc-link-arg-bins` instruction tells Cargo to pass the [`-C
38+
link-arg=FLAG` option][link-arg] to the compiler, but only when building a
39+
binary target. Its usage is highly platform specific. It is useful
40+
to set a linker script or other linker options.
41+
42+
[link-arg]: ../../rustc/codegen-options/index.md#link-arg
43+
44+
<a id="rustc-link-arg"></a>
45+
#### `cargo:rustc-link-arg=FLAG`
46+
47+
The `rustc-link-arg` instruction tells Cargo to pass the [`-C link-arg=FLAG`
48+
option][link-arg] to the compiler, but only when building supported targets
49+
(benchmarks, binaries, `cdylib` crates, examples, and tests). Its usage is
50+
highly platform specific. It is useful to set the shared library version or
51+
linker script.
52+
53+
[link-arg]: ../../rustc/codegen-options/index.md#link-arg
54+
2355
### no-index-update
2456
* Original Issue: [#3479](https://github.com/rust-lang/cargo/issues/3479)
2557
* Tracking Issue: [#7404](https://github.com/rust-lang/cargo/issues/7404)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
//! Tests for -Zextra-link-arg.
2+
3+
use cargo_test_support::{basic_bin_manifest, project};
4+
5+
#[cargo_test]
6+
fn build_script_extra_link_arg_bin() {
7+
let p = project()
8+
.file("Cargo.toml", &basic_bin_manifest("foo"))
9+
.file("src/main.rs", "fn main() {}")
10+
.file(
11+
"build.rs",
12+
r#"
13+
fn main() {
14+
println!("cargo:rustc-link-arg-bins=--this-is-a-bogus-flag");
15+
}
16+
"#,
17+
)
18+
.build();
19+
20+
p.cargo("build -Zextra-link-arg -v")
21+
.masquerade_as_nightly_cargo()
22+
.without_status()
23+
.with_stderr_contains(
24+
"[RUNNING] `rustc --crate-name foo [..]-C link-arg=--this-is-a-bogus-flag[..]",
25+
)
26+
.run();
27+
}
28+
29+
#[cargo_test]
30+
fn build_script_extra_link_arg() {
31+
let p = project()
32+
.file("Cargo.toml", &basic_bin_manifest("foo"))
33+
.file("src/main.rs", "fn main() {}")
34+
.file(
35+
"build.rs",
36+
r#"
37+
fn main() {
38+
println!("cargo:rustc-link-arg=--this-is-a-bogus-flag");
39+
}
40+
"#,
41+
)
42+
.build();
43+
44+
p.cargo("build -Zextra-link-arg -v")
45+
.masquerade_as_nightly_cargo()
46+
.without_status()
47+
.with_stderr_contains(
48+
"[RUNNING] `rustc --crate-name foo [..]-C link-arg=--this-is-a-bogus-flag[..]",
49+
)
50+
.run();
51+
}
52+
53+
#[cargo_test]
54+
fn build_script_extra_link_arg_warn_without_flag() {
55+
let p = project()
56+
.file("Cargo.toml", &basic_bin_manifest("foo"))
57+
.file("src/main.rs", "fn main() {}")
58+
.file(
59+
"build.rs",
60+
r#"
61+
fn main() {
62+
println!("cargo:rustc-link-arg=--this-is-a-bogus-flag");
63+
}
64+
"#,
65+
)
66+
.build();
67+
68+
p.cargo("build -v")
69+
.with_status(0)
70+
.with_stderr_contains("warning: cargo:rustc-link-arg requires -Zextra-link-arg flag")
71+
.run();
72+
}

tests/testsuite/main.rs

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ mod build;
2020
mod build_plan;
2121
mod build_script;
2222
mod build_script_env;
23+
mod build_script_extra_link_arg;
2324
mod cache_messages;
2425
mod cargo_alias_config;
2526
mod cargo_command;

0 commit comments

Comments
 (0)