Skip to content

Commit b305504

Browse files
committed
WIP
1 parent a170f2b commit b305504

13 files changed

+132
-108
lines changed

compiler/rustc_metadata/src/native_libs.rs

+110-71
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use rustc_ast::{NestedMetaItem, CRATE_NODE_ID};
22
use rustc_attr as attr;
3-
use rustc_data_structures::fx::FxHashSet;
3+
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
44
use rustc_errors::struct_span_err;
55
use rustc_hir as hir;
66
use rustc_hir::def::DefKind;
@@ -12,12 +12,17 @@ use rustc_session::Session;
1212
use rustc_span::symbol::{sym, Symbol};
1313
use rustc_target::spec::abi::Abi;
1414

15+
use std::{iter, mem};
16+
1517
crate fn collect(tcx: TyCtxt<'_>) -> Vec<NativeLib> {
16-
let mut collector = Collector { tcx, libs: Vec::new() };
18+
let mut collector = Collector { tcx, libs: Vec::new(), attr_libs: 0 };
1719
for id in tcx.hir().items() {
1820
collector.process_item(id);
1921
}
22+
collector.attr_libs = collector.libs.len();
2023
collector.process_command_line();
24+
collector.unify_kinds_and_modifiers();
25+
collector.compat_reorder();
2126
collector.libs
2227
}
2328

@@ -31,6 +36,7 @@ crate fn relevant_lib(sess: &Session, lib: &NativeLib) -> bool {
3136
struct Collector<'tcx> {
3237
tcx: TyCtxt<'tcx>,
3338
libs: Vec<NativeLib>,
39+
attr_libs: usize,
3440
}
3541

3642
impl<'tcx> Collector<'tcx> {
@@ -363,99 +369,132 @@ impl<'tcx> Collector<'tcx> {
363369

364370
// Process libs passed on the command line
365371
fn process_command_line(&mut self) {
366-
// First, check for errors
367-
let mut renames = FxHashSet::default();
368-
for lib in &self.tcx.sess.opts.libs {
369-
if let NativeLibKind::Framework { .. } = lib.kind && !self.tcx.sess.target.is_like_osx {
372+
// Collect overrides and check them for errors
373+
let mut overrides = FxHashMap::default();
374+
for cmd_lib in &self.tcx.sess.opts.libs {
375+
if let NativeLibKind::Framework { .. } = cmd_lib.kind && !self.tcx.sess.target.is_like_osx {
370376
// Cannot check this when parsing options because the target is not yet available.
371377
self.tcx.sess.err("library kind `framework` is only supported on Apple targets");
372378
}
373-
if let Some(ref new_name) = lib.new_name {
374-
let any_duplicate = self
379+
if let Some(override_name) = &cmd_lib.new_name {
380+
if override_name.is_empty() {
381+
self.tcx.sess.err(&format!(
382+
"empty override name was specified for library `{}`",
383+
cmd_lib.name
384+
));
385+
} else if self
375386
.libs
376387
.iter()
377-
.filter_map(|lib| lib.name.as_ref())
378-
.any(|n| n.as_str() == lib.name);
379-
if new_name.is_empty() {
380-
self.tcx.sess.err(format!(
381-
"an empty renaming target was specified for library `{}`",
382-
lib.name
388+
.filter_map(|attr_lib| attr_lib.name)
389+
.all(|attr_lib_name| attr_lib_name.as_str() != cmd_lib.name)
390+
{
391+
self.tcx.sess.err(&format!(
392+
"override of the library `{}` was specified, however this crate \
393+
contains no `#[link]` attributes referencing this library",
394+
cmd_lib.name
383395
));
384-
} else if !any_duplicate {
385-
self.tcx.sess.err(format!(
386-
"renaming of the library `{}` was specified, \
387-
however this crate contains no `#[link(...)]` \
388-
attributes referencing this library",
389-
lib.name
390-
));
391-
} else if !renames.insert(&lib.name) {
392-
self.tcx.sess.err(format!(
393-
"multiple renamings were \
394-
specified for library `{}`",
395-
lib.name
396+
} else if overrides.insert(&cmd_lib.name, cmd_lib).is_some() {
397+
self.tcx.sess.err(&format!(
398+
"multiple overrides were specified for library `{}`",
399+
cmd_lib.name
396400
));
397401
}
398402
}
399403
}
400404

401-
// Update kind and, optionally, the name of all native libraries
402-
// (there may be more than one) with the specified name. If any
403-
// library is mentioned more than once, keep the latest mention
404-
// of it, so that any possible dependent libraries appear before
405-
// it. (This ensures that the linker is able to see symbols from
406-
// all possible dependent libraries before linking in the library
407-
// in question.)
408-
for passed_lib in &self.tcx.sess.opts.libs {
409-
// If we've already added any native libraries with the same
410-
// name, they will be pulled out into `existing`, so that we
411-
// can move them to the end of the list below.
412-
let mut existing = self
413-
.libs
414-
.drain_filter(|lib| {
415-
if let Some(lib_name) = lib.name {
416-
if lib_name.as_str() == passed_lib.name {
417-
// FIXME: This whole logic is questionable, whether modifiers are
418-
// involved or not, library reordering and kind overriding without
419-
// explicit `:rename` in particular.
420-
if lib.has_modifiers() || passed_lib.has_modifiers() {
421-
self.tcx.sess.span_err(
422-
self.tcx.def_span(lib.foreign_module.unwrap()),
423-
"overriding linking modifiers from command line is not supported"
424-
);
405+
// Apply overrides
406+
if !overrides.is_empty() {
407+
let orig_attr_lib_names = Vec::from_iter(self.libs.iter().map(|lib| lib.name));
408+
for (name, override_lib) in overrides {
409+
for (orig_attr_lib_name, attr_lib) in
410+
iter::zip(&orig_attr_lib_names, &mut self.libs)
411+
{
412+
if let Some(orig_attr_lib_name) = orig_attr_lib_name
413+
&& orig_attr_lib_name.as_str() == name {
414+
// The name is overridden unconditionally
415+
attr_lib.name =
416+
Some(Symbol::intern(&override_lib.new_name.as_ref().unwrap()));
417+
// The kind and modifiers are overridden only if the override specifies
418+
// them explicitly
419+
if override_lib.kind != NativeLibKind::Unspecified {
420+
if attr_lib.has_modifiers() && !override_lib.has_modifiers() {
421+
// Not clear what behavior is desirable here
422+
self.tcx.sess.err(&format!(
423+
"override for library `{name}` must specify modifiers because \
424+
the overridden `#[link]` attribute specified modifiers",
425+
));
425426
}
426-
if passed_lib.kind != NativeLibKind::Unspecified {
427-
lib.kind = passed_lib.kind;
428-
}
429-
if let Some(new_name) = &passed_lib.new_name {
430-
lib.name = Some(Symbol::intern(new_name));
431-
}
432-
lib.verbatim = passed_lib.verbatim;
433-
return true;
427+
attr_lib.kind = override_lib.kind;
428+
attr_lib.verbatim = override_lib.verbatim;
434429
}
435430
}
436-
false
437-
})
438-
.collect::<Vec<_>>();
439-
if existing.is_empty() {
440-
// Add if not found
441-
let new_name: Option<&str> = passed_lib.new_name.as_deref();
431+
}
432+
}
433+
}
434+
435+
// Add regular (non-override) libraries from the command line
436+
for cmd_lib in &self.tcx.sess.opts.libs {
437+
if cmd_lib.new_name.is_none() {
442438
self.libs.push(NativeLib {
443-
name: Some(Symbol::intern(new_name.unwrap_or(&passed_lib.name))),
444-
kind: passed_lib.kind,
439+
name: Some(Symbol::intern(&cmd_lib.name)),
440+
kind: cmd_lib.kind,
445441
cfg: None,
446442
foreign_module: None,
447443
wasm_import_module: None,
448-
verbatim: passed_lib.verbatim,
444+
verbatim: cmd_lib.verbatim,
449445
dll_imports: Vec::new(),
450446
});
451-
} else {
452-
// Move all existing libraries with the same name to the
453-
// end of the command line.
454-
self.libs.append(&mut existing);
455447
}
456448
}
457449
}
458450

451+
fn unify_kinds_and_modifiers(&mut self) {
452+
let mut kinds_and_modifiers =
453+
FxHashMap::<Symbol, FxHashSet<(NativeLibKind, Option<bool>)>>::default();
454+
for NativeLib { name, kind, verbatim, cfg, .. } in &self.libs {
455+
if let Some(name) = *name && *kind != NativeLibKind::Unspecified && cfg.is_none() {
456+
kinds_and_modifiers.entry(name).or_default().insert((*kind, *verbatim));
457+
}
458+
}
459+
460+
for NativeLib { name, kind, verbatim, .. } in &mut self.libs {
461+
if let Some(name) = name
462+
&& *kind == NativeLibKind::Unspecified
463+
&& let Some(kinds_and_modifiers) = kinds_and_modifiers.get(name) {
464+
if kinds_and_modifiers.len() == 1 {
465+
(*kind, *verbatim) = *kinds_and_modifiers.iter().next().unwrap();
466+
} else {
467+
self.tcx.sess.err(&format!(
468+
"cannot infer kind for library `{name}`, it is linked more than once \
469+
with different kinds or modifiers",
470+
));
471+
}
472+
}
473+
}
474+
}
475+
476+
fn compat_reorder(&mut self) {
477+
let mut tmp = Vec::with_capacity(self.libs.len());
478+
479+
let mut cmd_libs = Vec::from_iter(self.libs.drain(self.attr_libs..));
480+
cmd_libs.reverse();
481+
let mut attr_libs = mem::take(&mut self.libs);
482+
attr_libs.reverse();
483+
484+
while !cmd_libs.is_empty() {
485+
let cmd_lib = cmd_libs.remove(0);
486+
let name = cmd_lib.name;
487+
tmp.push(cmd_lib);
488+
tmp.extend(cmd_libs.drain_filter(|cmd_lib| cmd_lib.name == name));
489+
tmp.extend(attr_libs.drain_filter(|attr_lib| attr_lib.name == name));
490+
}
491+
492+
tmp.append(&mut attr_libs);
493+
tmp.reverse();
494+
495+
self.libs = tmp;
496+
}
497+
459498
fn i686_arg_list_size(&self, item: &hir::ForeignItemRef) -> usize {
460499
let argument_types: &List<Ty<'_>> = self.tcx.erase_late_bound_regions(
461500
self.tcx

compiler/rustc_session/src/config.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1339,7 +1339,7 @@ pub fn rustc_short_optgroups() -> Vec<RustcOptGroup> {
13391339
Optional comma separated MODIFIERS (bundle|verbatim|whole-archive|as-needed)
13401340
may be specified each with a prefix of either '+' to
13411341
enable or '-' to disable.",
1342-
"[KIND[:MODIFIERS]=]NAME[:RENAME]",
1342+
"[KIND[:MODIFIERS]=]NAME[:OVERRIDE_NAME]",
13431343
),
13441344
make_crate_type_option(),
13451345
opt::opt_s("", "crate-name", "Specify the name of the crate being built", "NAME"),

src/doc/rustc/src/command-line-arguments.md

+12-9
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ KIND=PATH` where `KIND` may be one of:
3737
<a id="option-l-link-lib"></a>
3838
## `-l`: link the generated crate to a native library
3939

40-
Syntax: `-l [KIND[:MODIFIERS]=]NAME[:RENAME]`.
40+
Syntax: `-l [KIND[:MODIFIERS]=]NAME[:OVERRIDE_NAME]`.
4141

4242
This flag allows you to specify linking to a specific native library when building
4343
a crate.
@@ -56,15 +56,18 @@ Specifying multiple `modifiers` arguments in a single `link` attribute,
5656
or multiple identical modifiers in the same `modifiers` argument is not currently supported. \
5757
Example: `-l static:+whole-archive=mylib`.
5858

59-
The kind of library and the modifiers can also be specified in a [`#[link]`
60-
attribute][link-attribute]. If the kind is not specified in the `link`
61-
attribute or on the command-line, it will link a dynamic library if available,
62-
otherwise it will use a static library. If the kind is specified on the
63-
command-line, it will override the kind specified in a `link` attribute.
59+
Unspecified kind is equivalent to `dylib`.
6460

65-
The name used in a `link` attribute may be overridden using the form `-l
66-
ATTR_NAME:LINK_NAME` where `ATTR_NAME` is the name in the `link` attribute,
67-
and `LINK_NAME` is the name of the actual library that will be linked.
61+
Depending on the used linker, `dylib` is typically a preference (a static library with the same
62+
name can still be linked if the dynamic library is not found), but `static` is typically a
63+
requirement (an error is reported if the static library is not found).
64+
65+
If the `:OVERRIDE_NAME` component is specified, then a new library won't be linked, but the option
66+
will instead modify one of the libraries previously passed with
67+
[`#[link]` attributes][link-attribute]. \
68+
`OVERRIDE_NAME` will be used as a name for the library to link instead of the `name` argument
69+
passed to `#[link]`, and the `KIND` and `MODIFIERS` (if explicitly specified) will also replace
70+
original `kind` and `modifiers` arguments from the `#[link]` attribute.
6871

6972
[link-attribute]: ../reference/items/external-blocks.html#the-link-attribute
7073

src/test/ui/native-library-link-flags/modifiers-override.rs

-2
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,5 @@
1212
//~^ ERROR multiple `modifiers` arguments in a single `#[link]` attribute
1313
)]
1414
extern "C" {}
15-
//~^ ERROR overriding linking modifiers from command line is not supported
16-
//~| ERROR overriding linking modifiers from command line is not supported
1715

1816
fn main() {}

src/test/ui/native-library-link-flags/modifiers-override.stderr

+1-13
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,5 @@ error: multiple `whole-archive` modifiers in a single `modifiers` argument
1010
LL | modifiers = "+whole-archive,-whole-archive",
1111
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1212

13-
error: overriding linking modifiers from command line is not supported
14-
--> $DIR/modifiers-override.rs:14:1
15-
|
16-
LL | extern "C" {}
17-
| ^^^^^^^^^^^^^
18-
19-
error: overriding linking modifiers from command line is not supported
20-
--> $DIR/modifiers-override.rs:14:1
21-
|
22-
LL | extern "C" {}
23-
| ^^^^^^^^^^^^^
24-
25-
error: aborting due to 4 previous errors
13+
error: aborting due to 2 previous errors
2614

Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
// compile-flags: -l foo:bar
2-
// error-pattern: renaming of the library `foo` was specified
2+
// error-pattern: override of the library `foo` was specified
33

44
#![crate_type = "lib"]
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
error: renaming of the library `foo` was specified, however this crate contains no `#[link(...)]` attributes referencing this library
1+
error: override of the library `foo` was specified, however this crate contains no `#[link]` attributes referencing this library
22

33
error: aborting due to previous error
44

src/test/ui/rfc-1717-dllimport/multiple-renames.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// compile-flags: -l foo:bar -l foo:baz
2-
// error-pattern: multiple renamings were specified for library
2+
// error-pattern: multiple overrides were specified for library
33

44
#![crate_type = "lib"]
55

Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
error: multiple renamings were specified for library `foo`
1+
error: multiple overrides were specified for library `foo`
22

33
error: aborting due to previous error
44

src/test/ui/rfc-1717-dllimport/rename-modifiers.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// compile-flags: -l dylib=foo:bar
2-
// error-pattern: overriding linking modifiers from command line is not supported
2+
// error-pattern: override for library `foo` must specify modifiers
33

44
#![feature(native_link_modifiers_as_needed)]
55

Original file line numberDiff line numberDiff line change
@@ -1,8 +1,4 @@
1-
error: overriding linking modifiers from command line is not supported
2-
--> $DIR/rename-modifiers.rs:9:1
3-
|
4-
LL | extern "C" {}
5-
| ^^^^^^^^^^^^^
1+
error: override for library `foo` must specify modifiers because the overridden `#[link]` attribute specified modifiers
62

73
error: aborting due to previous error
84

src/test/ui/rfc-1717-dllimport/rename-to-empty.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// compile-flags: -l foo:
2-
// error-pattern: an empty renaming target was specified for library
2+
// error-pattern: empty override name was specified for library
33

44
#![crate_type = "lib"]
55

Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
error: an empty renaming target was specified for library `foo`
1+
error: empty override name was specified for library `foo`
22

33
error: aborting due to previous error
44

0 commit comments

Comments
 (0)