about summary refs log tree commit diff
path: root/compiler/rustc_session
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2023-05-06 11:24:37 +0000
committerbors <bors@rust-lang.org>2023-05-06 11:24:37 +0000
commit333b920feeb136d35fac804d51310df1a35c399e (patch)
treef759f1a51dbf09a333ebe83e0864796e7f96287a /compiler/rustc_session
parent151a070afe09c0c844e8d9af98a20fee56a5a7f2 (diff)
parent812f2d75e11a1acd52a4bfb201247c34a76c8e49 (diff)
downloadrust-333b920feeb136d35fac804d51310df1a35c399e.tar.gz
rust-333b920feeb136d35fac804d51310df1a35c399e.zip
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!)
Diffstat (limited to 'compiler/rustc_session')
-rw-r--r--compiler/rustc_session/src/config.rs18
1 files changed, 17 insertions, 1 deletions
diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs
index 18917120256..d80cc0aa043 100644
--- a/compiler/rustc_session/src/config.rs
+++ b/compiler/rustc_session/src/config.rs
@@ -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;
     }