mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-22 06:44:35 +00:00
Auto merge of #109421 - mhammerly:extern-force-option, r=petrochenkov
Add `force` option for `--extern` flag When `--extern force:foo=libfoo.so` is passed to `rustc` and `foo` is not actually used in the crate, ~inject an `extern crate foo;` statement into the AST~ force it to be resolved anyway in `CrateLoader::postprocess()`. This allows you to, for instance, inject a `#[panic_handler]` implementation into a `#![no_std]` crate without modifying its source so that it can be built as a `dylib`. It may also be useful for `#![panic_runtime]` or `#[global_allocator]`/`#![default_lib_allocator]` implementations. My work previously involved integrating Rust into an existing C/C++ codebase which was built with Buck and shipped on, among other platforms, Android. When targeting Android, Buck builds all "native" code with shared linkage* so it can be loaded from Java/Kotlin. My project was not itself `#![no_std]`, but many of our dependencies were, and they would fail to build with shared linkage due to a lack of a panic handler. With this change, that project can add the new `force` option to the `std` dependency it already explicitly provides to every crate to solve this problem. *This is an oversimplification - Buck has a couple features for aggregating dependencies into larger shared libraries, but none that I think sustainably solve this problem. ~The AST injection happens after macro expansion around where we similarly inject a test harness and proc-macro harness. The resolver's list of actually-used extern flags is populated during macro expansion, and if any of our `--extern` arguments have the `force` option and weren't already used, we inject an `extern crate` statement for them. The injection logic was added in `rustc_builtin_macros` as that's where similar injections for tests, proc-macros, and std/core already live.~ (New contributor - grateful for feedback and guidance!)
This commit is contained in:
commit
333b920fee
@ -69,6 +69,7 @@ where
|
||||
is_private_dep: false,
|
||||
add_prelude: true,
|
||||
nounused_dep: false,
|
||||
force: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -865,6 +865,17 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
fn inject_forced_externs(&mut self) {
|
||||
for (name, entry) in self.sess.opts.externs.iter() {
|
||||
if entry.force {
|
||||
let name_interned = Symbol::intern(name);
|
||||
if !self.used_extern_options.contains(&name_interned) {
|
||||
self.resolve_crate(name_interned, DUMMY_SP, CrateDepKind::Explicit);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn inject_dependency_if(
|
||||
&self,
|
||||
krate: CrateNum,
|
||||
@ -913,7 +924,7 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> {
|
||||
// Don't worry about pathless `--extern foo` sysroot references
|
||||
continue;
|
||||
}
|
||||
if entry.nounused_dep {
|
||||
if entry.nounused_dep || entry.force {
|
||||
// We're not worried about this one
|
||||
continue;
|
||||
}
|
||||
@ -942,6 +953,7 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> {
|
||||
}
|
||||
|
||||
pub fn postprocess(&mut self, krate: &ast::Crate) {
|
||||
self.inject_forced_externs();
|
||||
self.inject_profiler_runtime(krate);
|
||||
self.inject_allocator_crate(krate);
|
||||
self.inject_panic_runtime(krate);
|
||||
|
@ -518,6 +518,12 @@ pub struct ExternEntry {
|
||||
/// `--extern nounused:std=/path/to/lib/libstd.rlib`. This is used to
|
||||
/// suppress `unused-crate-dependencies` warnings.
|
||||
pub nounused_dep: bool,
|
||||
/// If the extern entry is not referenced in the crate, force it to be resolved anyway.
|
||||
///
|
||||
/// Allows a dependency satisfying, for instance, a missing panic handler to be injected
|
||||
/// without modifying source:
|
||||
/// `--extern force:extras=/path/to/lib/libstd.rlib`
|
||||
pub force: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
@ -556,7 +562,13 @@ impl Externs {
|
||||
|
||||
impl ExternEntry {
|
||||
fn new(location: ExternLocation) -> ExternEntry {
|
||||
ExternEntry { location, is_private_dep: false, add_prelude: false, nounused_dep: false }
|
||||
ExternEntry {
|
||||
location,
|
||||
is_private_dep: false,
|
||||
add_prelude: false,
|
||||
nounused_dep: false,
|
||||
force: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn files(&self) -> Option<impl Iterator<Item = &CanonicalizedPath>> {
|
||||
@ -2261,6 +2273,7 @@ pub fn parse_externs(
|
||||
let mut is_private_dep = false;
|
||||
let mut add_prelude = true;
|
||||
let mut nounused_dep = false;
|
||||
let mut force = false;
|
||||
if let Some(opts) = options {
|
||||
if !is_unstable_enabled {
|
||||
early_error(
|
||||
@ -2283,6 +2296,7 @@ pub fn parse_externs(
|
||||
}
|
||||
}
|
||||
"nounused" => nounused_dep = true,
|
||||
"force" => force = true,
|
||||
_ => early_error(error_format, &format!("unknown --extern option `{opt}`")),
|
||||
}
|
||||
}
|
||||
@ -2293,6 +2307,8 @@ pub fn parse_externs(
|
||||
entry.is_private_dep |= is_private_dep;
|
||||
// likewise `nounused`
|
||||
entry.nounused_dep |= nounused_dep;
|
||||
// and `force`
|
||||
entry.force |= force;
|
||||
// If any flag is missing `noprelude`, then add to the prelude.
|
||||
entry.add_prelude |= add_prelude;
|
||||
}
|
||||
|
17
tests/ui/extern-flag/auxiliary/panic_handler.rs
Normal file
17
tests/ui/extern-flag/auxiliary/panic_handler.rs
Normal file
@ -0,0 +1,17 @@
|
||||
#![feature(lang_items)]
|
||||
#![no_std]
|
||||
|
||||
// Since `rustc` generally passes `-nodefaultlibs` to the linker,
|
||||
// Rust programs link necessary system libraries via `#[link()]`
|
||||
// attributes in the `libc` crate. `libc` is a dependency of `std`,
|
||||
// but as we are `#![no_std]`, we need to include it manually.
|
||||
#![feature(rustc_private)]
|
||||
extern crate libc;
|
||||
|
||||
#[panic_handler]
|
||||
pub fn begin_panic_handler(_info: &core::panic::PanicInfo<'_>) -> ! {
|
||||
loop {}
|
||||
}
|
||||
|
||||
#[lang = "eh_personality"]
|
||||
extern "C" fn eh_personality() {}
|
9
tests/ui/extern-flag/force-extern.rs
Normal file
9
tests/ui/extern-flag/force-extern.rs
Normal file
@ -0,0 +1,9 @@
|
||||
// check-pass
|
||||
// ignore-cross-compile (needs dylibs and compiletest doesn't have a more specific header)
|
||||
// aux-crate:force:panic_handler=panic_handler.rs
|
||||
// compile-flags: -Zunstable-options --crate-type dylib
|
||||
// edition:2018
|
||||
|
||||
#![no_std]
|
||||
|
||||
fn foo() {}
|
10
tests/ui/extern-flag/no-force-extern.rs
Normal file
10
tests/ui/extern-flag/no-force-extern.rs
Normal file
@ -0,0 +1,10 @@
|
||||
// aux-crate:panic_handler=panic_handler.rs
|
||||
// ignore-cross-compile (needs dylibs and compiletest doesn't have a more specific header)
|
||||
// compile_flags: -Zunstable-options --crate-type dylib
|
||||
// error-pattern: `#[panic_handler]` function required, but not found
|
||||
// dont-check-compiler-stderr
|
||||
// edition: 2018
|
||||
|
||||
#![no_std]
|
||||
|
||||
fn foo() {}
|
11
tests/ui/extern-flag/redundant-force-extern.rs
Normal file
11
tests/ui/extern-flag/redundant-force-extern.rs
Normal file
@ -0,0 +1,11 @@
|
||||
// check-pass
|
||||
// ignore-cross-compile (needs dylibs and compiletest doesn't have a more specific header)
|
||||
// aux-crate:force:panic_handler=panic_handler.rs
|
||||
// compile-flags: -Zunstable-options --crate-type dylib
|
||||
// edition:2018
|
||||
|
||||
#![no_std]
|
||||
|
||||
extern crate panic_handler;
|
||||
|
||||
fn foo() {}
|
Loading…
Reference in New Issue
Block a user