Introduce an ArchiveBuilderBuilder
This avoids monomorphizing all linker code for each codegen backend and will allow passing in extra information to the archive builder from the codegen backend.
This commit is contained in:
parent
90da3c6f2b
commit
7c6c7e8785
8 changed files with 214 additions and 148 deletions
|
@ -39,16 +39,8 @@ pub(super) fn find_library(
|
|||
));
|
||||
}
|
||||
|
||||
pub trait ArchiveBuilder<'a> {
|
||||
fn new(sess: &'a Session) -> Self;
|
||||
|
||||
fn add_file(&mut self, path: &Path);
|
||||
|
||||
fn add_archive<F>(&mut self, archive: &Path, skip: F) -> io::Result<()>
|
||||
where
|
||||
F: FnMut(&str) -> bool + 'static;
|
||||
|
||||
fn build(self, output: &Path) -> bool;
|
||||
pub trait ArchiveBuilderBuilder {
|
||||
fn new_archive_builder<'a>(&self, sess: &'a Session) -> Box<dyn ArchiveBuilder<'a> + 'a>;
|
||||
|
||||
/// Creates a DLL Import Library <https://docs.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-creation#creating-an-import-library>.
|
||||
/// and returns the path on disk to that import library.
|
||||
|
@ -56,9 +48,22 @@ pub trait ArchiveBuilder<'a> {
|
|||
/// `linker_with_args`, which is specialized on `ArchiveBuilder` but
|
||||
/// doesn't take or create an instance of that type.
|
||||
fn create_dll_import_lib(
|
||||
&self,
|
||||
sess: &Session,
|
||||
lib_name: &str,
|
||||
dll_imports: &[DllImport],
|
||||
tmpdir: &Path,
|
||||
) -> PathBuf;
|
||||
}
|
||||
|
||||
pub trait ArchiveBuilder<'a> {
|
||||
fn add_file(&mut self, path: &Path);
|
||||
|
||||
fn add_archive(
|
||||
&mut self,
|
||||
archive: &Path,
|
||||
skip: Box<dyn FnMut(&str) -> bool + 'static>,
|
||||
) -> io::Result<()>;
|
||||
|
||||
fn build(self: Box<Self>, output: &Path) -> bool;
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ use rustc_target::spec::crt_objects::{CrtObjects, CrtObjectsFallback};
|
|||
use rustc_target::spec::{LinkOutputKind, LinkerFlavor, LldFlavor, SplitDebuginfo};
|
||||
use rustc_target::spec::{PanicStrategy, RelocModel, RelroLevel, SanitizerSet, Target};
|
||||
|
||||
use super::archive::{find_library, ArchiveBuilder};
|
||||
use super::archive::{find_library, ArchiveBuilder, ArchiveBuilderBuilder};
|
||||
use super::command::Command;
|
||||
use super::linker::{self, Linker};
|
||||
use super::metadata::{create_rmeta_file, MetadataPosition};
|
||||
|
@ -56,8 +56,9 @@ pub fn ensure_removed(diag_handler: &Handler, path: &Path) {
|
|||
|
||||
/// Performs the linkage portion of the compilation phase. This will generate all
|
||||
/// of the requested outputs for this compilation session.
|
||||
pub fn link_binary<'a, B: ArchiveBuilder<'a>>(
|
||||
pub fn link_binary<'a>(
|
||||
sess: &'a Session,
|
||||
archive_builder_builder: &dyn ArchiveBuilderBuilder,
|
||||
codegen_results: &CodegenResults,
|
||||
outputs: &OutputFilenames,
|
||||
) -> Result<(), ErrorGuaranteed> {
|
||||
|
@ -102,15 +103,28 @@ pub fn link_binary<'a, B: ArchiveBuilder<'a>>(
|
|||
CrateType::Rlib => {
|
||||
let _timer = sess.timer("link_rlib");
|
||||
info!("preparing rlib to {:?}", out_filename);
|
||||
link_rlib::<B>(sess, codegen_results, RlibFlavor::Normal, &path)?
|
||||
.build(&out_filename);
|
||||
link_rlib(
|
||||
sess,
|
||||
archive_builder_builder,
|
||||
codegen_results,
|
||||
RlibFlavor::Normal,
|
||||
&path,
|
||||
)?
|
||||
.build(&out_filename);
|
||||
}
|
||||
CrateType::Staticlib => {
|
||||
link_staticlib::<B>(sess, codegen_results, &out_filename, &path)?;
|
||||
link_staticlib(
|
||||
sess,
|
||||
archive_builder_builder,
|
||||
codegen_results,
|
||||
&out_filename,
|
||||
&path,
|
||||
)?;
|
||||
}
|
||||
_ => {
|
||||
link_natively::<B>(
|
||||
link_natively(
|
||||
sess,
|
||||
archive_builder_builder,
|
||||
crate_type,
|
||||
&out_filename,
|
||||
codegen_results,
|
||||
|
@ -240,15 +254,16 @@ pub fn each_linked_rlib(
|
|||
/// the object file of the crate, but it also contains all of the object files from native
|
||||
/// libraries. This is done by unzipping native libraries and inserting all of the contents into
|
||||
/// this archive.
|
||||
fn link_rlib<'a, B: ArchiveBuilder<'a>>(
|
||||
fn link_rlib<'a>(
|
||||
sess: &'a Session,
|
||||
archive_builder_builder: &dyn ArchiveBuilderBuilder,
|
||||
codegen_results: &CodegenResults,
|
||||
flavor: RlibFlavor,
|
||||
tmpdir: &MaybeTempDir,
|
||||
) -> Result<B, ErrorGuaranteed> {
|
||||
) -> Result<Box<dyn ArchiveBuilder<'a> + 'a>, ErrorGuaranteed> {
|
||||
let lib_search_paths = archive_search_paths(sess);
|
||||
|
||||
let mut ab = <B as ArchiveBuilder>::new(sess);
|
||||
let mut ab = archive_builder_builder.new_archive_builder(sess);
|
||||
|
||||
let trailing_metadata = match flavor {
|
||||
RlibFlavor::Normal => {
|
||||
|
@ -333,7 +348,7 @@ fn link_rlib<'a, B: ArchiveBuilder<'a>>(
|
|||
if let Some(name) = lib.name {
|
||||
let location =
|
||||
find_library(name.as_str(), lib.verbatim.unwrap_or(false), &lib_search_paths, sess);
|
||||
ab.add_archive(&location, |_| false).unwrap_or_else(|e| {
|
||||
ab.add_archive(&location, Box::new(|_| false)).unwrap_or_else(|e| {
|
||||
sess.fatal(&format!(
|
||||
"failed to add native library {}: {}",
|
||||
location.to_string_lossy(),
|
||||
|
@ -346,10 +361,14 @@ fn link_rlib<'a, B: ArchiveBuilder<'a>>(
|
|||
for (raw_dylib_name, raw_dylib_imports) in
|
||||
collate_raw_dylibs(sess, &codegen_results.crate_info.used_libraries)?
|
||||
{
|
||||
let output_path =
|
||||
B::create_dll_import_lib(sess, &raw_dylib_name, &raw_dylib_imports, tmpdir.as_ref());
|
||||
let output_path = archive_builder_builder.create_dll_import_lib(
|
||||
sess,
|
||||
&raw_dylib_name,
|
||||
&raw_dylib_imports,
|
||||
tmpdir.as_ref(),
|
||||
);
|
||||
|
||||
ab.add_archive(&output_path, |_| false).unwrap_or_else(|e| {
|
||||
ab.add_archive(&output_path, Box::new(|_| false)).unwrap_or_else(|e| {
|
||||
sess.fatal(&format!("failed to add native library {}: {}", output_path.display(), e));
|
||||
});
|
||||
}
|
||||
|
@ -442,14 +461,21 @@ fn collate_raw_dylibs(
|
|||
///
|
||||
/// There's no need to include metadata in a static archive, so ensure to not link in the metadata
|
||||
/// object file (and also don't prepare the archive with a metadata file).
|
||||
fn link_staticlib<'a, B: ArchiveBuilder<'a>>(
|
||||
fn link_staticlib<'a>(
|
||||
sess: &'a Session,
|
||||
archive_builder_builder: &dyn ArchiveBuilderBuilder,
|
||||
codegen_results: &CodegenResults,
|
||||
out_filename: &Path,
|
||||
tempdir: &MaybeTempDir,
|
||||
) -> Result<(), ErrorGuaranteed> {
|
||||
info!("preparing staticlib to {:?}", out_filename);
|
||||
let mut ab = link_rlib::<B>(sess, codegen_results, RlibFlavor::StaticlibBase, tempdir)?;
|
||||
let mut ab = link_rlib(
|
||||
sess,
|
||||
archive_builder_builder,
|
||||
codegen_results,
|
||||
RlibFlavor::StaticlibBase,
|
||||
tempdir,
|
||||
)?;
|
||||
let mut all_native_libs = vec![];
|
||||
|
||||
let res = each_linked_rlib(&codegen_results.crate_info, &mut |cnum, path| {
|
||||
|
@ -483,26 +509,29 @@ fn link_staticlib<'a, B: ArchiveBuilder<'a>>(
|
|||
// might be also an extra name suffix
|
||||
let obj_start = name.as_str().to_owned();
|
||||
|
||||
ab.add_archive(path, move |fname: &str| {
|
||||
// Ignore metadata files, no matter the name.
|
||||
if fname == METADATA_FILENAME {
|
||||
return true;
|
||||
}
|
||||
ab.add_archive(
|
||||
path,
|
||||
Box::new(move |fname: &str| {
|
||||
// Ignore metadata files, no matter the name.
|
||||
if fname == METADATA_FILENAME {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Don't include Rust objects if LTO is enabled
|
||||
if lto && looks_like_rust_object_file(fname) {
|
||||
return true;
|
||||
}
|
||||
// Don't include Rust objects if LTO is enabled
|
||||
if lto && looks_like_rust_object_file(fname) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Otherwise if this is *not* a rust object and we're skipping
|
||||
// objects then skip this file
|
||||
if skip_object_files && (!fname.starts_with(&obj_start) || !fname.ends_with(".o")) {
|
||||
return true;
|
||||
}
|
||||
// Otherwise if this is *not* a rust object and we're skipping
|
||||
// objects then skip this file
|
||||
if skip_object_files && (!fname.starts_with(&obj_start) || !fname.ends_with(".o")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// ok, don't skip this
|
||||
false
|
||||
})
|
||||
// ok, don't skip this
|
||||
false
|
||||
}),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
all_native_libs.extend(codegen_results.crate_info.native_libraries[&cnum].iter().cloned());
|
||||
|
@ -641,8 +670,9 @@ fn link_dwarf_object<'a>(
|
|||
///
|
||||
/// This will invoke the system linker/cc to create the resulting file. This links to all upstream
|
||||
/// files as well.
|
||||
fn link_natively<'a, B: ArchiveBuilder<'a>>(
|
||||
fn link_natively<'a>(
|
||||
sess: &'a Session,
|
||||
archive_builder_builder: &dyn ArchiveBuilderBuilder,
|
||||
crate_type: CrateType,
|
||||
out_filename: &Path,
|
||||
codegen_results: &CodegenResults,
|
||||
|
@ -650,10 +680,11 @@ fn link_natively<'a, B: ArchiveBuilder<'a>>(
|
|||
) -> Result<(), ErrorGuaranteed> {
|
||||
info!("preparing {:?} to {:?}", crate_type, out_filename);
|
||||
let (linker_path, flavor) = linker_and_flavor(sess);
|
||||
let mut cmd = linker_with_args::<B>(
|
||||
let mut cmd = linker_with_args(
|
||||
&linker_path,
|
||||
flavor,
|
||||
sess,
|
||||
archive_builder_builder,
|
||||
crate_type,
|
||||
tmpdir,
|
||||
out_filename,
|
||||
|
@ -1839,10 +1870,11 @@ fn add_rpath_args(
|
|||
/// to the linking process as a whole.
|
||||
/// Order-independent options may still override each other in order-dependent fashion,
|
||||
/// e.g `--foo=yes --foo=no` may be equivalent to `--foo=no`.
|
||||
fn linker_with_args<'a, B: ArchiveBuilder<'a>>(
|
||||
fn linker_with_args<'a>(
|
||||
path: &Path,
|
||||
flavor: LinkerFlavor,
|
||||
sess: &'a Session,
|
||||
archive_builder_builder: &dyn ArchiveBuilderBuilder,
|
||||
crate_type: CrateType,
|
||||
tmpdir: &Path,
|
||||
out_filename: &Path,
|
||||
|
@ -1943,7 +1975,14 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>(
|
|||
}
|
||||
|
||||
// Upstream rust libraries and their non-bundled static libraries
|
||||
add_upstream_rust_crates::<B>(cmd, sess, codegen_results, crate_type, tmpdir);
|
||||
add_upstream_rust_crates(
|
||||
cmd,
|
||||
sess,
|
||||
archive_builder_builder,
|
||||
codegen_results,
|
||||
crate_type,
|
||||
tmpdir,
|
||||
);
|
||||
|
||||
// Upstream dynamic native libraries linked with `#[link]` attributes at and `-l`
|
||||
// command line options.
|
||||
|
@ -1958,7 +1997,7 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>(
|
|||
for (raw_dylib_name, raw_dylib_imports) in
|
||||
collate_raw_dylibs(sess, &codegen_results.crate_info.used_libraries)?
|
||||
{
|
||||
cmd.add_object(&B::create_dll_import_lib(
|
||||
cmd.add_object(&archive_builder_builder.create_dll_import_lib(
|
||||
sess,
|
||||
&raw_dylib_name,
|
||||
&raw_dylib_imports,
|
||||
|
@ -2248,9 +2287,10 @@ fn add_local_native_libraries(
|
|||
///
|
||||
/// Rust crates are not considered at all when creating an rlib output. All dependencies will be
|
||||
/// linked when producing the final output (instead of the intermediate rlib version).
|
||||
fn add_upstream_rust_crates<'a, B: ArchiveBuilder<'a>>(
|
||||
fn add_upstream_rust_crates<'a>(
|
||||
cmd: &mut dyn Linker,
|
||||
sess: &'a Session,
|
||||
archive_builder_builder: &dyn ArchiveBuilderBuilder,
|
||||
codegen_results: &CodegenResults,
|
||||
crate_type: CrateType,
|
||||
tmpdir: &Path,
|
||||
|
@ -2339,7 +2379,7 @@ fn add_upstream_rust_crates<'a, B: ArchiveBuilder<'a>>(
|
|||
let src = &codegen_results.crate_info.used_crate_source[&cnum];
|
||||
match data[cnum.as_usize() - 1] {
|
||||
_ if codegen_results.crate_info.profiler_runtime == Some(cnum) => {
|
||||
add_static_crate::<B>(cmd, sess, codegen_results, tmpdir, cnum);
|
||||
add_static_crate(cmd, sess, archive_builder_builder, codegen_results, tmpdir, cnum);
|
||||
}
|
||||
// compiler-builtins are always placed last to ensure that they're
|
||||
// linked correctly.
|
||||
|
@ -2349,7 +2389,7 @@ fn add_upstream_rust_crates<'a, B: ArchiveBuilder<'a>>(
|
|||
}
|
||||
Linkage::NotLinked | Linkage::IncludedFromDylib => {}
|
||||
Linkage::Static => {
|
||||
add_static_crate::<B>(cmd, sess, codegen_results, tmpdir, cnum);
|
||||
add_static_crate(cmd, sess, archive_builder_builder, codegen_results, tmpdir, cnum);
|
||||
|
||||
// Link static native libs with "-bundle" modifier only if the crate they originate from
|
||||
// is being linked statically to the current crate. If it's linked dynamically
|
||||
|
@ -2408,7 +2448,7 @@ fn add_upstream_rust_crates<'a, B: ArchiveBuilder<'a>>(
|
|||
// was already "included" in a dylib (e.g., `libstd` when `-C prefer-dynamic`
|
||||
// is used)
|
||||
if let Some(cnum) = compiler_builtins {
|
||||
add_static_crate::<B>(cmd, sess, codegen_results, tmpdir, cnum);
|
||||
add_static_crate(cmd, sess, archive_builder_builder, codegen_results, tmpdir, cnum);
|
||||
}
|
||||
|
||||
// Converts a library file-stem into a cc -l argument
|
||||
|
@ -2434,9 +2474,10 @@ fn add_upstream_rust_crates<'a, B: ArchiveBuilder<'a>>(
|
|||
// Note, however, that if we're not doing LTO we can just pass the rlib
|
||||
// blindly to the linker (fast) because it's fine if it's not actually
|
||||
// included as we're at the end of the dependency chain.
|
||||
fn add_static_crate<'a, B: ArchiveBuilder<'a>>(
|
||||
fn add_static_crate<'a>(
|
||||
cmd: &mut dyn Linker,
|
||||
sess: &'a Session,
|
||||
archive_builder_builder: &dyn ArchiveBuilderBuilder,
|
||||
codegen_results: &CodegenResults,
|
||||
tmpdir: &Path,
|
||||
cnum: CrateNum,
|
||||
|
@ -2476,35 +2517,38 @@ fn add_upstream_rust_crates<'a, B: ArchiveBuilder<'a>>(
|
|||
let is_builtins = sess.target.no_builtins
|
||||
|| !codegen_results.crate_info.is_no_builtins.contains(&cnum);
|
||||
|
||||
let mut archive = <B as ArchiveBuilder>::new(sess);
|
||||
if let Err(e) = archive.add_archive(cratepath, move |f| {
|
||||
if f == METADATA_FILENAME {
|
||||
return true;
|
||||
}
|
||||
let mut archive = archive_builder_builder.new_archive_builder(sess);
|
||||
if let Err(e) = archive.add_archive(
|
||||
cratepath,
|
||||
Box::new(move |f| {
|
||||
if f == METADATA_FILENAME {
|
||||
return true;
|
||||
}
|
||||
|
||||
let canonical = f.replace('-', "_");
|
||||
let canonical = f.replace('-', "_");
|
||||
|
||||
let is_rust_object =
|
||||
canonical.starts_with(&canonical_name) && looks_like_rust_object_file(&f);
|
||||
let is_rust_object =
|
||||
canonical.starts_with(&canonical_name) && looks_like_rust_object_file(&f);
|
||||
|
||||
// If we've been requested to skip all native object files
|
||||
// (those not generated by the rust compiler) then we can skip
|
||||
// this file. See above for why we may want to do this.
|
||||
let skip_because_cfg_say_so = skip_native && !is_rust_object;
|
||||
// If we've been requested to skip all native object files
|
||||
// (those not generated by the rust compiler) then we can skip
|
||||
// this file. See above for why we may want to do this.
|
||||
let skip_because_cfg_say_so = skip_native && !is_rust_object;
|
||||
|
||||
// If we're performing LTO and this is a rust-generated object
|
||||
// file, then we don't need the object file as it's part of the
|
||||
// LTO module. Note that `#![no_builtins]` is excluded from LTO,
|
||||
// though, so we let that object file slide.
|
||||
let skip_because_lto =
|
||||
upstream_rust_objects_already_included && is_rust_object && is_builtins;
|
||||
// If we're performing LTO and this is a rust-generated object
|
||||
// file, then we don't need the object file as it's part of the
|
||||
// LTO module. Note that `#![no_builtins]` is excluded from LTO,
|
||||
// though, so we let that object file slide.
|
||||
let skip_because_lto =
|
||||
upstream_rust_objects_already_included && is_rust_object && is_builtins;
|
||||
|
||||
if skip_because_cfg_say_so || skip_because_lto {
|
||||
return true;
|
||||
}
|
||||
if skip_because_cfg_say_so || skip_because_lto {
|
||||
return true;
|
||||
}
|
||||
|
||||
false
|
||||
}) {
|
||||
false
|
||||
}),
|
||||
) {
|
||||
sess.fatal(&format!("failed to build archive from rlib: {}", e));
|
||||
}
|
||||
if archive.build(&dst) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue