2014-01-17 17:44:44 -06:00
|
|
|
// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
|
Add generation of static libraries to rustc
This commit implements the support necessary for generating both intermediate
and result static rust libraries. This is an implementation of my thoughts in
https://mail.mozilla.org/pipermail/rust-dev/2013-November/006686.html.
When compiling a library, we still retain the "lib" option, although now there
are "rlib", "staticlib", and "dylib" as options for crate_type (and these are
stackable). The idea of "lib" is to generate the "compiler default" instead of
having too choose (although all are interchangeable). For now I have left the
"complier default" to be a dynamic library for size reasons.
Of the rust libraries, lib{std,extra,rustuv} will bootstrap with an
rlib/dylib pair, but lib{rustc,syntax,rustdoc,rustpkg} will only be built as a
dynamic object. I chose this for size reasons, but also because you're probably
not going to be embedding the rustc compiler anywhere any time soon.
Other than the options outlined above, there are a few defaults/preferences that
are now opinionated in the compiler:
* If both a .dylib and .rlib are found for a rust library, the compiler will
prefer the .rlib variant. This is overridable via the -Z prefer-dynamic option
* If generating a "lib", the compiler will generate a dynamic library. This is
overridable by explicitly saying what flavor you'd like (rlib, staticlib,
dylib).
* If no options are passed to the command line, and no crate_type is found in
the destination crate, then an executable is generated
With this change, you can successfully build a rust program with 0 dynamic
dependencies on rust libraries. There is still a dynamic dependency on
librustrt, but I plan on removing that in a subsequent commit.
This change includes no tests just yet. Our current testing
infrastructure/harnesses aren't very amenable to doing flavorful things with
linking, so I'm planning on adding a new mode of testing which I believe belongs
as a separate commit.
Closes #552
2013-11-15 16:03:29 -06:00
|
|
|
// file at the top-level directory of this distribution and at
|
|
|
|
// http://rust-lang.org/COPYRIGHT.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
|
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
|
|
// option. This file may not be copied, modified, or distributed
|
|
|
|
// except according to those terms.
|
|
|
|
|
|
|
|
//! A helper class for dealing with static archives
|
|
|
|
|
2014-09-11 00:26:41 -05:00
|
|
|
use std::io::fs::PathExtensions;
|
2014-05-05 16:33:55 -05:00
|
|
|
use std::io::process::{Command, ProcessOutput};
|
core: Remove the cast module
This commit revisits the `cast` module in libcore and libstd, and scrutinizes
all functions inside of it. The result was to remove the `cast` module entirely,
folding all functionality into the `mem` module. Specifically, this is the fate
of each function in the `cast` module.
* transmute - This function was moved to `mem`, but it is now marked as
#[unstable]. This is due to planned changes to the `transmute`
function and how it can be invoked (see the #[unstable] comment).
For more information, see RFC 5 and #12898
* transmute_copy - This function was moved to `mem`, with clarification that is
is not an error to invoke it with T/U that are different
sizes, but rather that it is strongly discouraged. This
function is now #[stable]
* forget - This function was moved to `mem` and marked #[stable]
* bump_box_refcount - This function was removed due to the deprecation of
managed boxes as well as its questionable utility.
* transmute_mut - This function was previously deprecated, and removed as part
of this commit.
* transmute_mut_unsafe - This function doesn't serve much of a purpose when it
can be achieved with an `as` in safe code, so it was
removed.
* transmute_lifetime - This function was removed because it is likely a strong
indication that code is incorrect in the first place.
* transmute_mut_lifetime - This function was removed for the same reasons as
`transmute_lifetime`
* copy_lifetime - This function was moved to `mem`, but it is marked
`#[unstable]` now due to the likelihood of being removed in
the future if it is found to not be very useful.
* copy_mut_lifetime - This function was also moved to `mem`, but had the same
treatment as `copy_lifetime`.
* copy_lifetime_vec - This function was removed because it is not used today,
and its existence is not necessary with DST
(copy_lifetime will suffice).
In summary, the cast module was stripped down to these functions, and then the
functions were moved to the `mem` module.
transmute - #[unstable]
transmute_copy - #[stable]
forget - #[stable]
copy_lifetime - #[unstable]
copy_mut_lifetime - #[unstable]
[breaking-change]
2014-05-09 12:34:51 -05:00
|
|
|
use std::io::{fs, TempDir};
|
|
|
|
use std::io;
|
|
|
|
use std::os;
|
|
|
|
use std::str;
|
2014-08-18 10:29:44 -05:00
|
|
|
use syntax::diagnostic::Handler as ErrorHandler;
|
Add generation of static libraries to rustc
This commit implements the support necessary for generating both intermediate
and result static rust libraries. This is an implementation of my thoughts in
https://mail.mozilla.org/pipermail/rust-dev/2013-November/006686.html.
When compiling a library, we still retain the "lib" option, although now there
are "rlib", "staticlib", and "dylib" as options for crate_type (and these are
stackable). The idea of "lib" is to generate the "compiler default" instead of
having too choose (although all are interchangeable). For now I have left the
"complier default" to be a dynamic library for size reasons.
Of the rust libraries, lib{std,extra,rustuv} will bootstrap with an
rlib/dylib pair, but lib{rustc,syntax,rustdoc,rustpkg} will only be built as a
dynamic object. I chose this for size reasons, but also because you're probably
not going to be embedding the rustc compiler anywhere any time soon.
Other than the options outlined above, there are a few defaults/preferences that
are now opinionated in the compiler:
* If both a .dylib and .rlib are found for a rust library, the compiler will
prefer the .rlib variant. This is overridable via the -Z prefer-dynamic option
* If generating a "lib", the compiler will generate a dynamic library. This is
overridable by explicitly saying what flavor you'd like (rlib, staticlib,
dylib).
* If no options are passed to the command line, and no crate_type is found in
the destination crate, then an executable is generated
With this change, you can successfully build a rust program with 0 dynamic
dependencies on rust libraries. There is still a dynamic dependency on
librustrt, but I plan on removing that in a subsequent commit.
This change includes no tests just yet. Our current testing
infrastructure/harnesses aren't very amenable to doing flavorful things with
linking, so I'm planning on adding a new mode of testing which I believe belongs
as a separate commit.
Closes #552
2013-11-15 16:03:29 -06:00
|
|
|
|
2013-12-16 22:58:21 -06:00
|
|
|
pub static METADATA_FILENAME: &'static str = "rust.metadata.bin";
|
Store metadata separately in rlib files
Right now whenever an rlib file is linked against, all of the metadata from the
rlib is pulled in to the final staticlib or binary. The reason for this is that
the metadata is currently stored in a section of the object file. Note that this
is intentional for dynamic libraries in order to distribute metadata bundled
with static libraries.
This commit alters the situation for rlib libraries to instead store the
metadata in a separate file in the archive. In doing so, when the archive is
passed to the linker, none of the metadata will get pulled into the result
executable. Furthermore, the metadata file is skipped when assembling rlibs into
an archive.
The snag in this implementation comes with multiple output formats. When
generating a dylib, the metadata needs to be in the object file, but when
generating an rlib this needs to be separate. In order to accomplish this, the
metadata variable is inserted into an entirely separate LLVM Module which is
then codegen'd into a different location (foo.metadata.o). This is then linked
into dynamic libraries and silently ignored for rlib files.
While changing how metadata is inserted into archives, I have also stopped
compressing metadata when inserted into rlib files. We have wanted to stop
compressing metadata, but the sections it creates in object file sections are
apparently too large. Thankfully if it's just an arbitrary file it doesn't
matter how large it is.
I have seen massive reductions in executable sizes, as well as staticlib output
sizes (to confirm that this is all working).
2013-12-03 19:41:01 -06:00
|
|
|
|
2014-07-06 22:46:00 -05:00
|
|
|
pub struct ArchiveConfig<'a> {
|
|
|
|
pub handler: &'a ErrorHandler,
|
|
|
|
pub dst: Path,
|
|
|
|
pub lib_search_paths: Vec<Path>,
|
2014-07-23 13:56:36 -05:00
|
|
|
pub slib_prefix: String,
|
|
|
|
pub slib_suffix: String,
|
2014-07-06 22:46:00 -05:00
|
|
|
pub maybe_ar_prog: Option<String>
|
|
|
|
}
|
|
|
|
|
2014-03-05 08:36:01 -06:00
|
|
|
pub struct Archive<'a> {
|
2014-07-06 22:46:00 -05:00
|
|
|
handler: &'a ErrorHandler,
|
2014-03-28 12:05:27 -05:00
|
|
|
dst: Path,
|
2014-07-06 22:46:00 -05:00
|
|
|
lib_search_paths: Vec<Path>,
|
2014-07-23 13:56:36 -05:00
|
|
|
slib_prefix: String,
|
|
|
|
slib_suffix: String,
|
2014-07-06 22:46:00 -05:00
|
|
|
maybe_ar_prog: Option<String>
|
Add generation of static libraries to rustc
This commit implements the support necessary for generating both intermediate
and result static rust libraries. This is an implementation of my thoughts in
https://mail.mozilla.org/pipermail/rust-dev/2013-November/006686.html.
When compiling a library, we still retain the "lib" option, although now there
are "rlib", "staticlib", and "dylib" as options for crate_type (and these are
stackable). The idea of "lib" is to generate the "compiler default" instead of
having too choose (although all are interchangeable). For now I have left the
"complier default" to be a dynamic library for size reasons.
Of the rust libraries, lib{std,extra,rustuv} will bootstrap with an
rlib/dylib pair, but lib{rustc,syntax,rustdoc,rustpkg} will only be built as a
dynamic object. I chose this for size reasons, but also because you're probably
not going to be embedding the rustc compiler anywhere any time soon.
Other than the options outlined above, there are a few defaults/preferences that
are now opinionated in the compiler:
* If both a .dylib and .rlib are found for a rust library, the compiler will
prefer the .rlib variant. This is overridable via the -Z prefer-dynamic option
* If generating a "lib", the compiler will generate a dynamic library. This is
overridable by explicitly saying what flavor you'd like (rlib, staticlib,
dylib).
* If no options are passed to the command line, and no crate_type is found in
the destination crate, then an executable is generated
With this change, you can successfully build a rust program with 0 dynamic
dependencies on rust libraries. There is still a dynamic dependency on
librustrt, but I plan on removing that in a subsequent commit.
This change includes no tests just yet. Our current testing
infrastructure/harnesses aren't very amenable to doing flavorful things with
linking, so I'm planning on adding a new mode of testing which I believe belongs
as a separate commit.
Closes #552
2013-11-15 16:03:29 -06:00
|
|
|
}
|
|
|
|
|
2014-07-03 18:03:26 -05:00
|
|
|
/// Helper for adding many files to an archive with a single invocation of
|
|
|
|
/// `ar`.
|
|
|
|
#[must_use = "must call build() to finish building the archive"]
|
|
|
|
pub struct ArchiveBuilder<'a> {
|
|
|
|
archive: Archive<'a>,
|
|
|
|
work_dir: TempDir,
|
|
|
|
/// Filename of each member that should be added to the archive.
|
|
|
|
members: Vec<Path>,
|
|
|
|
should_update_symbols: bool,
|
|
|
|
}
|
|
|
|
|
2014-07-06 22:46:00 -05:00
|
|
|
fn run_ar(handler: &ErrorHandler, maybe_ar_prog: &Option<String>,
|
|
|
|
args: &str, cwd: Option<&Path>,
|
2014-03-05 08:36:01 -06:00
|
|
|
paths: &[&Path]) -> ProcessOutput {
|
2014-07-06 22:46:00 -05:00
|
|
|
let ar = match *maybe_ar_prog {
|
|
|
|
Some(ref ar) => ar.as_slice(),
|
|
|
|
None => "ar"
|
|
|
|
};
|
|
|
|
let mut cmd = Command::new(ar);
|
2014-05-05 16:33:55 -05:00
|
|
|
|
|
|
|
cmd.arg(args).args(paths);
|
|
|
|
debug!("{}", cmd);
|
2013-12-06 00:44:27 -06:00
|
|
|
|
Add generation of static libraries to rustc
This commit implements the support necessary for generating both intermediate
and result static rust libraries. This is an implementation of my thoughts in
https://mail.mozilla.org/pipermail/rust-dev/2013-November/006686.html.
When compiling a library, we still retain the "lib" option, although now there
are "rlib", "staticlib", and "dylib" as options for crate_type (and these are
stackable). The idea of "lib" is to generate the "compiler default" instead of
having too choose (although all are interchangeable). For now I have left the
"complier default" to be a dynamic library for size reasons.
Of the rust libraries, lib{std,extra,rustuv} will bootstrap with an
rlib/dylib pair, but lib{rustc,syntax,rustdoc,rustpkg} will only be built as a
dynamic object. I chose this for size reasons, but also because you're probably
not going to be embedding the rustc compiler anywhere any time soon.
Other than the options outlined above, there are a few defaults/preferences that
are now opinionated in the compiler:
* If both a .dylib and .rlib are found for a rust library, the compiler will
prefer the .rlib variant. This is overridable via the -Z prefer-dynamic option
* If generating a "lib", the compiler will generate a dynamic library. This is
overridable by explicitly saying what flavor you'd like (rlib, staticlib,
dylib).
* If no options are passed to the command line, and no crate_type is found in
the destination crate, then an executable is generated
With this change, you can successfully build a rust program with 0 dynamic
dependencies on rust libraries. There is still a dynamic dependency on
librustrt, but I plan on removing that in a subsequent commit.
This change includes no tests just yet. Our current testing
infrastructure/harnesses aren't very amenable to doing flavorful things with
linking, so I'm planning on adding a new mode of testing which I believe belongs
as a separate commit.
Closes #552
2013-11-15 16:03:29 -06:00
|
|
|
match cwd {
|
2014-05-05 16:33:55 -05:00
|
|
|
Some(p) => {
|
|
|
|
cmd.cwd(p);
|
|
|
|
debug!("inside {}", p.display());
|
|
|
|
}
|
Add generation of static libraries to rustc
This commit implements the support necessary for generating both intermediate
and result static rust libraries. This is an implementation of my thoughts in
https://mail.mozilla.org/pipermail/rust-dev/2013-November/006686.html.
When compiling a library, we still retain the "lib" option, although now there
are "rlib", "staticlib", and "dylib" as options for crate_type (and these are
stackable). The idea of "lib" is to generate the "compiler default" instead of
having too choose (although all are interchangeable). For now I have left the
"complier default" to be a dynamic library for size reasons.
Of the rust libraries, lib{std,extra,rustuv} will bootstrap with an
rlib/dylib pair, but lib{rustc,syntax,rustdoc,rustpkg} will only be built as a
dynamic object. I chose this for size reasons, but also because you're probably
not going to be embedding the rustc compiler anywhere any time soon.
Other than the options outlined above, there are a few defaults/preferences that
are now opinionated in the compiler:
* If both a .dylib and .rlib are found for a rust library, the compiler will
prefer the .rlib variant. This is overridable via the -Z prefer-dynamic option
* If generating a "lib", the compiler will generate a dynamic library. This is
overridable by explicitly saying what flavor you'd like (rlib, staticlib,
dylib).
* If no options are passed to the command line, and no crate_type is found in
the destination crate, then an executable is generated
With this change, you can successfully build a rust program with 0 dynamic
dependencies on rust libraries. There is still a dynamic dependency on
librustrt, but I plan on removing that in a subsequent commit.
This change includes no tests just yet. Our current testing
infrastructure/harnesses aren't very amenable to doing flavorful things with
linking, so I'm planning on adding a new mode of testing which I believe belongs
as a separate commit.
Closes #552
2013-11-15 16:03:29 -06:00
|
|
|
None => {}
|
|
|
|
}
|
2014-05-05 16:33:55 -05:00
|
|
|
|
|
|
|
match cmd.spawn() {
|
2014-05-05 18:58:42 -05:00
|
|
|
Ok(prog) => {
|
|
|
|
let o = prog.wait_with_output().unwrap();
|
2013-12-12 08:07:43 -06:00
|
|
|
if !o.status.success() {
|
2014-07-06 22:46:00 -05:00
|
|
|
handler.err(format!("{} failed with: {}",
|
2014-05-16 12:45:16 -05:00
|
|
|
cmd,
|
|
|
|
o.status).as_slice());
|
2014-07-06 22:46:00 -05:00
|
|
|
handler.note(format!("stdout ---\n{}",
|
2014-05-16 12:45:16 -05:00
|
|
|
str::from_utf8(o.output
|
|
|
|
.as_slice()).unwrap())
|
|
|
|
.as_slice());
|
2014-07-06 22:46:00 -05:00
|
|
|
handler.note(format!("stderr ---\n{}",
|
2014-05-16 12:45:16 -05:00
|
|
|
str::from_utf8(o.error
|
|
|
|
.as_slice()).unwrap())
|
|
|
|
.as_slice());
|
2014-07-06 22:46:00 -05:00
|
|
|
handler.abort_if_errors();
|
2013-12-12 08:07:43 -06:00
|
|
|
}
|
|
|
|
o
|
|
|
|
},
|
2014-01-29 20:42:19 -06:00
|
|
|
Err(e) => {
|
2014-07-06 22:46:00 -05:00
|
|
|
handler.err(format!("could not exec `{}`: {}", ar.as_slice(),
|
2014-05-16 12:45:16 -05:00
|
|
|
e).as_slice());
|
2014-07-06 22:46:00 -05:00
|
|
|
handler.abort_if_errors();
|
2014-10-09 14:17:22 -05:00
|
|
|
panic!("rustc::back::archive::run_ar() should not reach this point");
|
2013-12-12 08:07:43 -06:00
|
|
|
}
|
Add generation of static libraries to rustc
This commit implements the support necessary for generating both intermediate
and result static rust libraries. This is an implementation of my thoughts in
https://mail.mozilla.org/pipermail/rust-dev/2013-November/006686.html.
When compiling a library, we still retain the "lib" option, although now there
are "rlib", "staticlib", and "dylib" as options for crate_type (and these are
stackable). The idea of "lib" is to generate the "compiler default" instead of
having too choose (although all are interchangeable). For now I have left the
"complier default" to be a dynamic library for size reasons.
Of the rust libraries, lib{std,extra,rustuv} will bootstrap with an
rlib/dylib pair, but lib{rustc,syntax,rustdoc,rustpkg} will only be built as a
dynamic object. I chose this for size reasons, but also because you're probably
not going to be embedding the rustc compiler anywhere any time soon.
Other than the options outlined above, there are a few defaults/preferences that
are now opinionated in the compiler:
* If both a .dylib and .rlib are found for a rust library, the compiler will
prefer the .rlib variant. This is overridable via the -Z prefer-dynamic option
* If generating a "lib", the compiler will generate a dynamic library. This is
overridable by explicitly saying what flavor you'd like (rlib, staticlib,
dylib).
* If no options are passed to the command line, and no crate_type is found in
the destination crate, then an executable is generated
With this change, you can successfully build a rust program with 0 dynamic
dependencies on rust libraries. There is still a dynamic dependency on
librustrt, but I plan on removing that in a subsequent commit.
This change includes no tests just yet. Our current testing
infrastructure/harnesses aren't very amenable to doing flavorful things with
linking, so I'm planning on adding a new mode of testing which I believe belongs
as a separate commit.
Closes #552
2013-11-15 16:03:29 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-07-23 13:56:36 -05:00
|
|
|
pub fn find_library(name: &str, osprefix: &str, ossuffix: &str,
|
|
|
|
search_paths: &[Path], handler: &ErrorHandler) -> Path {
|
2014-07-30 09:44:20 -05:00
|
|
|
// On Windows, static libraries sometimes show up as libfoo.a and other
|
|
|
|
// times show up as foo.lib
|
2014-07-23 13:56:36 -05:00
|
|
|
let oslibname = format!("{}{}{}", osprefix, name, ossuffix);
|
2014-07-30 09:44:20 -05:00
|
|
|
let unixlibname = format!("lib{}.a", name);
|
|
|
|
|
|
|
|
for path in search_paths.iter() {
|
|
|
|
debug!("looking for {} inside {}", name, path.display());
|
|
|
|
let test = path.join(oslibname.as_slice());
|
|
|
|
if test.exists() { return test }
|
|
|
|
if oslibname != unixlibname {
|
|
|
|
let test = path.join(unixlibname.as_slice());
|
|
|
|
if test.exists() { return test }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
handler.fatal(format!("could not find native static library `{}`, \
|
|
|
|
perhaps an -L flag is missing?",
|
|
|
|
name).as_slice());
|
|
|
|
}
|
|
|
|
|
2014-03-05 08:36:01 -06:00
|
|
|
impl<'a> Archive<'a> {
|
2014-07-03 18:03:26 -05:00
|
|
|
fn new(config: ArchiveConfig<'a>) -> Archive<'a> {
|
2014-07-23 13:56:36 -05:00
|
|
|
let ArchiveConfig { handler, dst, lib_search_paths, slib_prefix, slib_suffix,
|
|
|
|
maybe_ar_prog } = config;
|
2014-07-06 22:46:00 -05:00
|
|
|
Archive {
|
|
|
|
handler: handler,
|
|
|
|
dst: dst,
|
|
|
|
lib_search_paths: lib_search_paths,
|
2014-07-23 13:56:36 -05:00
|
|
|
slib_prefix: slib_prefix,
|
|
|
|
slib_suffix: slib_suffix,
|
2014-07-06 22:46:00 -05:00
|
|
|
maybe_ar_prog: maybe_ar_prog
|
|
|
|
}
|
Add generation of static libraries to rustc
This commit implements the support necessary for generating both intermediate
and result static rust libraries. This is an implementation of my thoughts in
https://mail.mozilla.org/pipermail/rust-dev/2013-November/006686.html.
When compiling a library, we still retain the "lib" option, although now there
are "rlib", "staticlib", and "dylib" as options for crate_type (and these are
stackable). The idea of "lib" is to generate the "compiler default" instead of
having too choose (although all are interchangeable). For now I have left the
"complier default" to be a dynamic library for size reasons.
Of the rust libraries, lib{std,extra,rustuv} will bootstrap with an
rlib/dylib pair, but lib{rustc,syntax,rustdoc,rustpkg} will only be built as a
dynamic object. I chose this for size reasons, but also because you're probably
not going to be embedding the rustc compiler anywhere any time soon.
Other than the options outlined above, there are a few defaults/preferences that
are now opinionated in the compiler:
* If both a .dylib and .rlib are found for a rust library, the compiler will
prefer the .rlib variant. This is overridable via the -Z prefer-dynamic option
* If generating a "lib", the compiler will generate a dynamic library. This is
overridable by explicitly saying what flavor you'd like (rlib, staticlib,
dylib).
* If no options are passed to the command line, and no crate_type is found in
the destination crate, then an executable is generated
With this change, you can successfully build a rust program with 0 dynamic
dependencies on rust libraries. There is still a dynamic dependency on
librustrt, but I plan on removing that in a subsequent commit.
This change includes no tests just yet. Our current testing
infrastructure/harnesses aren't very amenable to doing flavorful things with
linking, so I'm planning on adding a new mode of testing which I believe belongs
as a separate commit.
Closes #552
2013-11-15 16:03:29 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Opens an existing static archive
|
2014-07-06 22:46:00 -05:00
|
|
|
pub fn open(config: ArchiveConfig<'a>) -> Archive<'a> {
|
2014-07-03 18:03:26 -05:00
|
|
|
let archive = Archive::new(config);
|
|
|
|
assert!(archive.dst.exists());
|
|
|
|
archive
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Removes a file from this archive
|
|
|
|
pub fn remove_file(&mut self, file: &str) {
|
|
|
|
run_ar(self.handler, &self.maybe_ar_prog, "d", None, [&self.dst, &Path::new(file)]);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Lists all files in an archive
|
|
|
|
pub fn files(&self) -> Vec<String> {
|
|
|
|
let output = run_ar(self.handler, &self.maybe_ar_prog, "t", None, [&self.dst]);
|
|
|
|
let output = str::from_utf8(output.output.as_slice()).unwrap();
|
|
|
|
// use lines_any because windows delimits output with `\r\n` instead of
|
|
|
|
// just `\n`
|
|
|
|
output.lines_any().map(|s| s.to_string()).collect()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Creates an `ArchiveBuilder` for adding files to this archive.
|
|
|
|
pub fn extend(self) -> ArchiveBuilder<'a> {
|
|
|
|
ArchiveBuilder::new(self)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> ArchiveBuilder<'a> {
|
|
|
|
fn new(archive: Archive<'a>) -> ArchiveBuilder<'a> {
|
|
|
|
ArchiveBuilder {
|
|
|
|
archive: archive,
|
|
|
|
work_dir: TempDir::new("rsar").unwrap(),
|
|
|
|
members: vec![],
|
|
|
|
should_update_symbols: false,
|
2014-07-06 22:46:00 -05:00
|
|
|
}
|
Add generation of static libraries to rustc
This commit implements the support necessary for generating both intermediate
and result static rust libraries. This is an implementation of my thoughts in
https://mail.mozilla.org/pipermail/rust-dev/2013-November/006686.html.
When compiling a library, we still retain the "lib" option, although now there
are "rlib", "staticlib", and "dylib" as options for crate_type (and these are
stackable). The idea of "lib" is to generate the "compiler default" instead of
having too choose (although all are interchangeable). For now I have left the
"complier default" to be a dynamic library for size reasons.
Of the rust libraries, lib{std,extra,rustuv} will bootstrap with an
rlib/dylib pair, but lib{rustc,syntax,rustdoc,rustpkg} will only be built as a
dynamic object. I chose this for size reasons, but also because you're probably
not going to be embedding the rustc compiler anywhere any time soon.
Other than the options outlined above, there are a few defaults/preferences that
are now opinionated in the compiler:
* If both a .dylib and .rlib are found for a rust library, the compiler will
prefer the .rlib variant. This is overridable via the -Z prefer-dynamic option
* If generating a "lib", the compiler will generate a dynamic library. This is
overridable by explicitly saying what flavor you'd like (rlib, staticlib,
dylib).
* If no options are passed to the command line, and no crate_type is found in
the destination crate, then an executable is generated
With this change, you can successfully build a rust program with 0 dynamic
dependencies on rust libraries. There is still a dynamic dependency on
librustrt, but I plan on removing that in a subsequent commit.
This change includes no tests just yet. Our current testing
infrastructure/harnesses aren't very amenable to doing flavorful things with
linking, so I'm planning on adding a new mode of testing which I believe belongs
as a separate commit.
Closes #552
2013-11-15 16:03:29 -06:00
|
|
|
}
|
|
|
|
|
2014-07-03 18:03:26 -05:00
|
|
|
/// Create a new static archive, ready for adding files.
|
|
|
|
pub fn create(config: ArchiveConfig<'a>) -> ArchiveBuilder<'a> {
|
|
|
|
let archive = Archive::new(config);
|
|
|
|
ArchiveBuilder::new(archive)
|
|
|
|
}
|
|
|
|
|
Add generation of static libraries to rustc
This commit implements the support necessary for generating both intermediate
and result static rust libraries. This is an implementation of my thoughts in
https://mail.mozilla.org/pipermail/rust-dev/2013-November/006686.html.
When compiling a library, we still retain the "lib" option, although now there
are "rlib", "staticlib", and "dylib" as options for crate_type (and these are
stackable). The idea of "lib" is to generate the "compiler default" instead of
having too choose (although all are interchangeable). For now I have left the
"complier default" to be a dynamic library for size reasons.
Of the rust libraries, lib{std,extra,rustuv} will bootstrap with an
rlib/dylib pair, but lib{rustc,syntax,rustdoc,rustpkg} will only be built as a
dynamic object. I chose this for size reasons, but also because you're probably
not going to be embedding the rustc compiler anywhere any time soon.
Other than the options outlined above, there are a few defaults/preferences that
are now opinionated in the compiler:
* If both a .dylib and .rlib are found for a rust library, the compiler will
prefer the .rlib variant. This is overridable via the -Z prefer-dynamic option
* If generating a "lib", the compiler will generate a dynamic library. This is
overridable by explicitly saying what flavor you'd like (rlib, staticlib,
dylib).
* If no options are passed to the command line, and no crate_type is found in
the destination crate, then an executable is generated
With this change, you can successfully build a rust program with 0 dynamic
dependencies on rust libraries. There is still a dynamic dependency on
librustrt, but I plan on removing that in a subsequent commit.
This change includes no tests just yet. Our current testing
infrastructure/harnesses aren't very amenable to doing flavorful things with
linking, so I'm planning on adding a new mode of testing which I believe belongs
as a separate commit.
Closes #552
2013-11-15 16:03:29 -06:00
|
|
|
/// Adds all of the contents of a native library to this archive. This will
|
|
|
|
/// search in the relevant locations for a library named `name`.
|
2014-01-29 20:42:19 -06:00
|
|
|
pub fn add_native_library(&mut self, name: &str) -> io::IoResult<()> {
|
2014-07-23 13:56:36 -05:00
|
|
|
let location = find_library(name,
|
|
|
|
self.archive.slib_prefix.as_slice(),
|
|
|
|
self.archive.slib_suffix.as_slice(),
|
2014-07-30 09:44:20 -05:00
|
|
|
self.archive.lib_search_paths.as_slice(),
|
|
|
|
self.archive.handler);
|
2014-01-29 20:42:19 -06:00
|
|
|
self.add_archive(&location, name, [])
|
Add generation of static libraries to rustc
This commit implements the support necessary for generating both intermediate
and result static rust libraries. This is an implementation of my thoughts in
https://mail.mozilla.org/pipermail/rust-dev/2013-November/006686.html.
When compiling a library, we still retain the "lib" option, although now there
are "rlib", "staticlib", and "dylib" as options for crate_type (and these are
stackable). The idea of "lib" is to generate the "compiler default" instead of
having too choose (although all are interchangeable). For now I have left the
"complier default" to be a dynamic library for size reasons.
Of the rust libraries, lib{std,extra,rustuv} will bootstrap with an
rlib/dylib pair, but lib{rustc,syntax,rustdoc,rustpkg} will only be built as a
dynamic object. I chose this for size reasons, but also because you're probably
not going to be embedding the rustc compiler anywhere any time soon.
Other than the options outlined above, there are a few defaults/preferences that
are now opinionated in the compiler:
* If both a .dylib and .rlib are found for a rust library, the compiler will
prefer the .rlib variant. This is overridable via the -Z prefer-dynamic option
* If generating a "lib", the compiler will generate a dynamic library. This is
overridable by explicitly saying what flavor you'd like (rlib, staticlib,
dylib).
* If no options are passed to the command line, and no crate_type is found in
the destination crate, then an executable is generated
With this change, you can successfully build a rust program with 0 dynamic
dependencies on rust libraries. There is still a dynamic dependency on
librustrt, but I plan on removing that in a subsequent commit.
This change includes no tests just yet. Our current testing
infrastructure/harnesses aren't very amenable to doing flavorful things with
linking, so I'm planning on adding a new mode of testing which I believe belongs
as a separate commit.
Closes #552
2013-11-15 16:03:29 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Adds all of the contents of the rlib at the specified path to this
|
|
|
|
/// archive.
|
Implement LTO
This commit implements LTO for rust leveraging LLVM's passes. What this means
is:
* When compiling an rlib, in addition to insdering foo.o into the archive, also
insert foo.bc (the LLVM bytecode) of the optimized module.
* When the compiler detects the -Z lto option, it will attempt to perform LTO on
a staticlib or binary output. The compiler will emit an error if a dylib or
rlib output is being generated.
* The actual act of performing LTO is as follows:
1. Force all upstream libraries to have an rlib version available.
2. Load the bytecode of each upstream library from the rlib.
3. Link all this bytecode into the current LLVM module (just using llvm
apis)
4. Run an internalization pass which internalizes all symbols except those
found reachable for the local crate of compilation.
5. Run the LLVM LTO pass manager over this entire module
6a. If assembling an archive, then add all upstream rlibs into the output
archive. This ignores all of the object/bitcode/metadata files rust
generated and placed inside the rlibs.
6b. If linking a binary, create copies of all upstream rlibs, remove the
rust-generated object-file, and then link everything as usual.
As I have explained in #10741, this process is excruciatingly slow, so this is
*not* turned on by default, and it is also why I have decided to hide it behind
a -Z flag for now. The good news is that the binary sizes are about as small as
they can be as a result of LTO, so it's definitely working.
Closes #10741
Closes #10740
2013-12-03 01:19:29 -06:00
|
|
|
///
|
|
|
|
/// This ignores adding the bytecode from the rlib, and if LTO is enabled
|
|
|
|
/// then the object file also isn't added.
|
2014-01-29 20:42:19 -06:00
|
|
|
pub fn add_rlib(&mut self, rlib: &Path, name: &str,
|
|
|
|
lto: bool) -> io::IoResult<()> {
|
Implement LTO
This commit implements LTO for rust leveraging LLVM's passes. What this means
is:
* When compiling an rlib, in addition to insdering foo.o into the archive, also
insert foo.bc (the LLVM bytecode) of the optimized module.
* When the compiler detects the -Z lto option, it will attempt to perform LTO on
a staticlib or binary output. The compiler will emit an error if a dylib or
rlib output is being generated.
* The actual act of performing LTO is as follows:
1. Force all upstream libraries to have an rlib version available.
2. Load the bytecode of each upstream library from the rlib.
3. Link all this bytecode into the current LLVM module (just using llvm
apis)
4. Run an internalization pass which internalizes all symbols except those
found reachable for the local crate of compilation.
5. Run the LLVM LTO pass manager over this entire module
6a. If assembling an archive, then add all upstream rlibs into the output
archive. This ignores all of the object/bitcode/metadata files rust
generated and placed inside the rlibs.
6b. If linking a binary, create copies of all upstream rlibs, remove the
rust-generated object-file, and then link everything as usual.
As I have explained in #10741, this process is excruciatingly slow, so this is
*not* turned on by default, and it is also why I have decided to hide it behind
a -Z flag for now. The good news is that the binary sizes are about as small as
they can be as a result of LTO, so it's definitely working.
Closes #10741
Closes #10740
2013-12-03 01:19:29 -06:00
|
|
|
let object = format!("{}.o", name);
|
2014-06-05 03:24:34 -05:00
|
|
|
let bytecode = format!("{}.bytecode.deflate", name);
|
2014-06-03 14:38:00 -05:00
|
|
|
let mut ignore = vec!(bytecode.as_slice(), METADATA_FILENAME);
|
Implement LTO
This commit implements LTO for rust leveraging LLVM's passes. What this means
is:
* When compiling an rlib, in addition to insdering foo.o into the archive, also
insert foo.bc (the LLVM bytecode) of the optimized module.
* When the compiler detects the -Z lto option, it will attempt to perform LTO on
a staticlib or binary output. The compiler will emit an error if a dylib or
rlib output is being generated.
* The actual act of performing LTO is as follows:
1. Force all upstream libraries to have an rlib version available.
2. Load the bytecode of each upstream library from the rlib.
3. Link all this bytecode into the current LLVM module (just using llvm
apis)
4. Run an internalization pass which internalizes all symbols except those
found reachable for the local crate of compilation.
5. Run the LLVM LTO pass manager over this entire module
6a. If assembling an archive, then add all upstream rlibs into the output
archive. This ignores all of the object/bitcode/metadata files rust
generated and placed inside the rlibs.
6b. If linking a binary, create copies of all upstream rlibs, remove the
rust-generated object-file, and then link everything as usual.
As I have explained in #10741, this process is excruciatingly slow, so this is
*not* turned on by default, and it is also why I have decided to hide it behind
a -Z flag for now. The good news is that the binary sizes are about as small as
they can be as a result of LTO, so it's definitely working.
Closes #10741
Closes #10740
2013-12-03 01:19:29 -06:00
|
|
|
if lto {
|
|
|
|
ignore.push(object.as_slice());
|
|
|
|
}
|
2014-03-08 14:36:22 -06:00
|
|
|
self.add_archive(rlib, name, ignore.as_slice())
|
Store metadata separately in rlib files
Right now whenever an rlib file is linked against, all of the metadata from the
rlib is pulled in to the final staticlib or binary. The reason for this is that
the metadata is currently stored in a section of the object file. Note that this
is intentional for dynamic libraries in order to distribute metadata bundled
with static libraries.
This commit alters the situation for rlib libraries to instead store the
metadata in a separate file in the archive. In doing so, when the archive is
passed to the linker, none of the metadata will get pulled into the result
executable. Furthermore, the metadata file is skipped when assembling rlibs into
an archive.
The snag in this implementation comes with multiple output formats. When
generating a dylib, the metadata needs to be in the object file, but when
generating an rlib this needs to be separate. In order to accomplish this, the
metadata variable is inserted into an entirely separate LLVM Module which is
then codegen'd into a different location (foo.metadata.o). This is then linked
into dynamic libraries and silently ignored for rlib files.
While changing how metadata is inserted into archives, I have also stopped
compressing metadata when inserted into rlib files. We have wanted to stop
compressing metadata, but the sections it creates in object file sections are
apparently too large. Thankfully if it's just an arbitrary file it doesn't
matter how large it is.
I have seen massive reductions in executable sizes, as well as staticlib output
sizes (to confirm that this is all working).
2013-12-03 19:41:01 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Adds an arbitrary file to this archive
|
2014-07-03 18:03:26 -05:00
|
|
|
pub fn add_file(&mut self, file: &Path) -> io::IoResult<()> {
|
|
|
|
let filename = Path::new(file.filename().unwrap());
|
|
|
|
let new_file = self.work_dir.path().join(&filename);
|
|
|
|
try!(fs::copy(file, &new_file));
|
|
|
|
self.members.push(filename);
|
|
|
|
Ok(())
|
Implement LTO
This commit implements LTO for rust leveraging LLVM's passes. What this means
is:
* When compiling an rlib, in addition to insdering foo.o into the archive, also
insert foo.bc (the LLVM bytecode) of the optimized module.
* When the compiler detects the -Z lto option, it will attempt to perform LTO on
a staticlib or binary output. The compiler will emit an error if a dylib or
rlib output is being generated.
* The actual act of performing LTO is as follows:
1. Force all upstream libraries to have an rlib version available.
2. Load the bytecode of each upstream library from the rlib.
3. Link all this bytecode into the current LLVM module (just using llvm
apis)
4. Run an internalization pass which internalizes all symbols except those
found reachable for the local crate of compilation.
5. Run the LLVM LTO pass manager over this entire module
6a. If assembling an archive, then add all upstream rlibs into the output
archive. This ignores all of the object/bitcode/metadata files rust
generated and placed inside the rlibs.
6b. If linking a binary, create copies of all upstream rlibs, remove the
rust-generated object-file, and then link everything as usual.
As I have explained in #10741, this process is excruciatingly slow, so this is
*not* turned on by default, and it is also why I have decided to hide it behind
a -Z flag for now. The good news is that the binary sizes are about as small as
they can be as a result of LTO, so it's definitely working.
Closes #10741
Closes #10740
2013-12-03 01:19:29 -06:00
|
|
|
}
|
|
|
|
|
2014-07-03 18:03:26 -05:00
|
|
|
/// Indicate that the next call to `build` should updates all symbols in
|
|
|
|
/// the archive (run 'ar s' over it).
|
2013-12-10 11:28:20 -06:00
|
|
|
pub fn update_symbols(&mut self) {
|
2014-07-03 18:03:26 -05:00
|
|
|
self.should_update_symbols = true;
|
2013-12-10 11:28:20 -06:00
|
|
|
}
|
|
|
|
|
2014-07-03 18:03:26 -05:00
|
|
|
/// Combine the provided files, rlibs, and native libraries into a single
|
|
|
|
/// `Archive`.
|
|
|
|
pub fn build(self) -> Archive<'a> {
|
|
|
|
// Get an absolute path to the destination, so `ar` will work even
|
|
|
|
// though we run it from `self.work_dir`.
|
|
|
|
let abs_dst = os::getcwd().join(&self.archive.dst);
|
|
|
|
assert!(!abs_dst.is_relative());
|
|
|
|
let mut args = vec![&abs_dst];
|
|
|
|
let mut total_len = abs_dst.as_vec().len();
|
|
|
|
|
|
|
|
if self.members.is_empty() {
|
|
|
|
// OSX `ar` does not allow using `r` with no members, but it does
|
|
|
|
// allow running `ar s file.a` to update symbols only.
|
|
|
|
if self.should_update_symbols {
|
|
|
|
run_ar(self.archive.handler, &self.archive.maybe_ar_prog,
|
|
|
|
"s", Some(self.work_dir.path()), args.as_slice());
|
|
|
|
}
|
|
|
|
return self.archive;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Don't allow the total size of `args` to grow beyond 32,000 bytes.
|
|
|
|
// Windows will raise an error if the argument string is longer than
|
|
|
|
// 32,768, and we leave a bit of extra space for the program name.
|
|
|
|
static ARG_LENGTH_LIMIT: uint = 32000;
|
|
|
|
|
|
|
|
for member_name in self.members.iter() {
|
|
|
|
let len = member_name.as_vec().len();
|
|
|
|
|
|
|
|
// `len + 1` to account for the space that's inserted before each
|
|
|
|
// argument. (Windows passes command-line arguments as a single
|
|
|
|
// string, not an array of strings.)
|
|
|
|
if total_len + len + 1 > ARG_LENGTH_LIMIT {
|
|
|
|
// Add the archive members seen so far, without updating the
|
|
|
|
// symbol table (`S`).
|
|
|
|
run_ar(self.archive.handler, &self.archive.maybe_ar_prog,
|
|
|
|
"cruS", Some(self.work_dir.path()), args.as_slice());
|
|
|
|
|
|
|
|
args.clear();
|
|
|
|
args.push(&abs_dst);
|
|
|
|
total_len = abs_dst.as_vec().len();
|
|
|
|
}
|
|
|
|
|
|
|
|
args.push(member_name);
|
|
|
|
total_len += len + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add the remaining archive members, and update the symbol table if
|
|
|
|
// necessary.
|
|
|
|
let flags = if self.should_update_symbols { "crus" } else { "cruS" };
|
|
|
|
run_ar(self.archive.handler, &self.archive.maybe_ar_prog,
|
|
|
|
flags, Some(self.work_dir.path()), args.as_slice());
|
|
|
|
|
|
|
|
self.archive
|
Implement LTO
This commit implements LTO for rust leveraging LLVM's passes. What this means
is:
* When compiling an rlib, in addition to insdering foo.o into the archive, also
insert foo.bc (the LLVM bytecode) of the optimized module.
* When the compiler detects the -Z lto option, it will attempt to perform LTO on
a staticlib or binary output. The compiler will emit an error if a dylib or
rlib output is being generated.
* The actual act of performing LTO is as follows:
1. Force all upstream libraries to have an rlib version available.
2. Load the bytecode of each upstream library from the rlib.
3. Link all this bytecode into the current LLVM module (just using llvm
apis)
4. Run an internalization pass which internalizes all symbols except those
found reachable for the local crate of compilation.
5. Run the LLVM LTO pass manager over this entire module
6a. If assembling an archive, then add all upstream rlibs into the output
archive. This ignores all of the object/bitcode/metadata files rust
generated and placed inside the rlibs.
6b. If linking a binary, create copies of all upstream rlibs, remove the
rust-generated object-file, and then link everything as usual.
As I have explained in #10741, this process is excruciatingly slow, so this is
*not* turned on by default, and it is also why I have decided to hide it behind
a -Z flag for now. The good news is that the binary sizes are about as small as
they can be as a result of LTO, so it's definitely working.
Closes #10741
Closes #10740
2013-12-03 01:19:29 -06:00
|
|
|
}
|
|
|
|
|
2014-01-29 20:42:19 -06:00
|
|
|
fn add_archive(&mut self, archive: &Path, name: &str,
|
|
|
|
skip: &[&str]) -> io::IoResult<()> {
|
Add generation of static libraries to rustc
This commit implements the support necessary for generating both intermediate
and result static rust libraries. This is an implementation of my thoughts in
https://mail.mozilla.org/pipermail/rust-dev/2013-November/006686.html.
When compiling a library, we still retain the "lib" option, although now there
are "rlib", "staticlib", and "dylib" as options for crate_type (and these are
stackable). The idea of "lib" is to generate the "compiler default" instead of
having too choose (although all are interchangeable). For now I have left the
"complier default" to be a dynamic library for size reasons.
Of the rust libraries, lib{std,extra,rustuv} will bootstrap with an
rlib/dylib pair, but lib{rustc,syntax,rustdoc,rustpkg} will only be built as a
dynamic object. I chose this for size reasons, but also because you're probably
not going to be embedding the rustc compiler anywhere any time soon.
Other than the options outlined above, there are a few defaults/preferences that
are now opinionated in the compiler:
* If both a .dylib and .rlib are found for a rust library, the compiler will
prefer the .rlib variant. This is overridable via the -Z prefer-dynamic option
* If generating a "lib", the compiler will generate a dynamic library. This is
overridable by explicitly saying what flavor you'd like (rlib, staticlib,
dylib).
* If no options are passed to the command line, and no crate_type is found in
the destination crate, then an executable is generated
With this change, you can successfully build a rust program with 0 dynamic
dependencies on rust libraries. There is still a dynamic dependency on
librustrt, but I plan on removing that in a subsequent commit.
This change includes no tests just yet. Our current testing
infrastructure/harnesses aren't very amenable to doing flavorful things with
linking, so I'm planning on adding a new mode of testing which I believe belongs
as a separate commit.
Closes #552
2013-11-15 16:03:29 -06:00
|
|
|
let loc = TempDir::new("rsar").unwrap();
|
|
|
|
|
2014-07-03 18:03:26 -05:00
|
|
|
// First, extract the contents of the archive to a temporary directory.
|
|
|
|
// We don't unpack directly into `self.work_dir` due to the possibility
|
|
|
|
// of filename collisions.
|
Add generation of static libraries to rustc
This commit implements the support necessary for generating both intermediate
and result static rust libraries. This is an implementation of my thoughts in
https://mail.mozilla.org/pipermail/rust-dev/2013-November/006686.html.
When compiling a library, we still retain the "lib" option, although now there
are "rlib", "staticlib", and "dylib" as options for crate_type (and these are
stackable). The idea of "lib" is to generate the "compiler default" instead of
having too choose (although all are interchangeable). For now I have left the
"complier default" to be a dynamic library for size reasons.
Of the rust libraries, lib{std,extra,rustuv} will bootstrap with an
rlib/dylib pair, but lib{rustc,syntax,rustdoc,rustpkg} will only be built as a
dynamic object. I chose this for size reasons, but also because you're probably
not going to be embedding the rustc compiler anywhere any time soon.
Other than the options outlined above, there are a few defaults/preferences that
are now opinionated in the compiler:
* If both a .dylib and .rlib are found for a rust library, the compiler will
prefer the .rlib variant. This is overridable via the -Z prefer-dynamic option
* If generating a "lib", the compiler will generate a dynamic library. This is
overridable by explicitly saying what flavor you'd like (rlib, staticlib,
dylib).
* If no options are passed to the command line, and no crate_type is found in
the destination crate, then an executable is generated
With this change, you can successfully build a rust program with 0 dynamic
dependencies on rust libraries. There is still a dynamic dependency on
librustrt, but I plan on removing that in a subsequent commit.
This change includes no tests just yet. Our current testing
infrastructure/harnesses aren't very amenable to doing flavorful things with
linking, so I'm planning on adding a new mode of testing which I believe belongs
as a separate commit.
Closes #552
2013-11-15 16:03:29 -06:00
|
|
|
let archive = os::make_absolute(archive);
|
2014-07-03 18:03:26 -05:00
|
|
|
run_ar(self.archive.handler, &self.archive.maybe_ar_prog,
|
|
|
|
"x", Some(loc.path()), [&archive]);
|
Add generation of static libraries to rustc
This commit implements the support necessary for generating both intermediate
and result static rust libraries. This is an implementation of my thoughts in
https://mail.mozilla.org/pipermail/rust-dev/2013-November/006686.html.
When compiling a library, we still retain the "lib" option, although now there
are "rlib", "staticlib", and "dylib" as options for crate_type (and these are
stackable). The idea of "lib" is to generate the "compiler default" instead of
having too choose (although all are interchangeable). For now I have left the
"complier default" to be a dynamic library for size reasons.
Of the rust libraries, lib{std,extra,rustuv} will bootstrap with an
rlib/dylib pair, but lib{rustc,syntax,rustdoc,rustpkg} will only be built as a
dynamic object. I chose this for size reasons, but also because you're probably
not going to be embedding the rustc compiler anywhere any time soon.
Other than the options outlined above, there are a few defaults/preferences that
are now opinionated in the compiler:
* If both a .dylib and .rlib are found for a rust library, the compiler will
prefer the .rlib variant. This is overridable via the -Z prefer-dynamic option
* If generating a "lib", the compiler will generate a dynamic library. This is
overridable by explicitly saying what flavor you'd like (rlib, staticlib,
dylib).
* If no options are passed to the command line, and no crate_type is found in
the destination crate, then an executable is generated
With this change, you can successfully build a rust program with 0 dynamic
dependencies on rust libraries. There is still a dynamic dependency on
librustrt, but I plan on removing that in a subsequent commit.
This change includes no tests just yet. Our current testing
infrastructure/harnesses aren't very amenable to doing flavorful things with
linking, so I'm planning on adding a new mode of testing which I believe belongs
as a separate commit.
Closes #552
2013-11-15 16:03:29 -06:00
|
|
|
|
|
|
|
// Next, we must rename all of the inputs to "guaranteed unique names".
|
2014-07-03 18:03:26 -05:00
|
|
|
// We move each file into `self.work_dir` under its new unique name.
|
|
|
|
// The reason for this renaming is that archives are keyed off the name
|
|
|
|
// of the files, so if two files have the same name they will override
|
|
|
|
// one another in the archive (bad).
|
Implement LTO
This commit implements LTO for rust leveraging LLVM's passes. What this means
is:
* When compiling an rlib, in addition to insdering foo.o into the archive, also
insert foo.bc (the LLVM bytecode) of the optimized module.
* When the compiler detects the -Z lto option, it will attempt to perform LTO on
a staticlib or binary output. The compiler will emit an error if a dylib or
rlib output is being generated.
* The actual act of performing LTO is as follows:
1. Force all upstream libraries to have an rlib version available.
2. Load the bytecode of each upstream library from the rlib.
3. Link all this bytecode into the current LLVM module (just using llvm
apis)
4. Run an internalization pass which internalizes all symbols except those
found reachable for the local crate of compilation.
5. Run the LLVM LTO pass manager over this entire module
6a. If assembling an archive, then add all upstream rlibs into the output
archive. This ignores all of the object/bitcode/metadata files rust
generated and placed inside the rlibs.
6b. If linking a binary, create copies of all upstream rlibs, remove the
rust-generated object-file, and then link everything as usual.
As I have explained in #10741, this process is excruciatingly slow, so this is
*not* turned on by default, and it is also why I have decided to hide it behind
a -Z flag for now. The good news is that the binary sizes are about as small as
they can be as a result of LTO, so it's definitely working.
Closes #10741
Closes #10740
2013-12-03 01:19:29 -06:00
|
|
|
//
|
|
|
|
// We skip any files explicitly desired for skipping, and we also skip
|
|
|
|
// all SYMDEF files as these are just magical placeholders which get
|
|
|
|
// re-created when we make a new archive anyway.
|
2014-02-19 12:07:49 -06:00
|
|
|
let files = try!(fs::readdir(loc.path()));
|
Add generation of static libraries to rustc
This commit implements the support necessary for generating both intermediate
and result static rust libraries. This is an implementation of my thoughts in
https://mail.mozilla.org/pipermail/rust-dev/2013-November/006686.html.
When compiling a library, we still retain the "lib" option, although now there
are "rlib", "staticlib", and "dylib" as options for crate_type (and these are
stackable). The idea of "lib" is to generate the "compiler default" instead of
having too choose (although all are interchangeable). For now I have left the
"complier default" to be a dynamic library for size reasons.
Of the rust libraries, lib{std,extra,rustuv} will bootstrap with an
rlib/dylib pair, but lib{rustc,syntax,rustdoc,rustpkg} will only be built as a
dynamic object. I chose this for size reasons, but also because you're probably
not going to be embedding the rustc compiler anywhere any time soon.
Other than the options outlined above, there are a few defaults/preferences that
are now opinionated in the compiler:
* If both a .dylib and .rlib are found for a rust library, the compiler will
prefer the .rlib variant. This is overridable via the -Z prefer-dynamic option
* If generating a "lib", the compiler will generate a dynamic library. This is
overridable by explicitly saying what flavor you'd like (rlib, staticlib,
dylib).
* If no options are passed to the command line, and no crate_type is found in
the destination crate, then an executable is generated
With this change, you can successfully build a rust program with 0 dynamic
dependencies on rust libraries. There is still a dynamic dependency on
librustrt, but I plan on removing that in a subsequent commit.
This change includes no tests just yet. Our current testing
infrastructure/harnesses aren't very amenable to doing flavorful things with
linking, so I'm planning on adding a new mode of testing which I believe belongs
as a separate commit.
Closes #552
2013-11-15 16:03:29 -06:00
|
|
|
for file in files.iter() {
|
|
|
|
let filename = file.filename_str().unwrap();
|
Store metadata separately in rlib files
Right now whenever an rlib file is linked against, all of the metadata from the
rlib is pulled in to the final staticlib or binary. The reason for this is that
the metadata is currently stored in a section of the object file. Note that this
is intentional for dynamic libraries in order to distribute metadata bundled
with static libraries.
This commit alters the situation for rlib libraries to instead store the
metadata in a separate file in the archive. In doing so, when the archive is
passed to the linker, none of the metadata will get pulled into the result
executable. Furthermore, the metadata file is skipped when assembling rlibs into
an archive.
The snag in this implementation comes with multiple output formats. When
generating a dylib, the metadata needs to be in the object file, but when
generating an rlib this needs to be separate. In order to accomplish this, the
metadata variable is inserted into an entirely separate LLVM Module which is
then codegen'd into a different location (foo.metadata.o). This is then linked
into dynamic libraries and silently ignored for rlib files.
While changing how metadata is inserted into archives, I have also stopped
compressing metadata when inserted into rlib files. We have wanted to stop
compressing metadata, but the sections it creates in object file sections are
apparently too large. Thankfully if it's just an arbitrary file it doesn't
matter how large it is.
I have seen massive reductions in executable sizes, as well as staticlib output
sizes (to confirm that this is all working).
2013-12-03 19:41:01 -06:00
|
|
|
if skip.iter().any(|s| *s == filename) { continue }
|
Implement LTO
This commit implements LTO for rust leveraging LLVM's passes. What this means
is:
* When compiling an rlib, in addition to insdering foo.o into the archive, also
insert foo.bc (the LLVM bytecode) of the optimized module.
* When the compiler detects the -Z lto option, it will attempt to perform LTO on
a staticlib or binary output. The compiler will emit an error if a dylib or
rlib output is being generated.
* The actual act of performing LTO is as follows:
1. Force all upstream libraries to have an rlib version available.
2. Load the bytecode of each upstream library from the rlib.
3. Link all this bytecode into the current LLVM module (just using llvm
apis)
4. Run an internalization pass which internalizes all symbols except those
found reachable for the local crate of compilation.
5. Run the LLVM LTO pass manager over this entire module
6a. If assembling an archive, then add all upstream rlibs into the output
archive. This ignores all of the object/bitcode/metadata files rust
generated and placed inside the rlibs.
6b. If linking a binary, create copies of all upstream rlibs, remove the
rust-generated object-file, and then link everything as usual.
As I have explained in #10741, this process is excruciatingly slow, so this is
*not* turned on by default, and it is also why I have decided to hide it behind
a -Z flag for now. The good news is that the binary sizes are about as small as
they can be as a result of LTO, so it's definitely working.
Closes #10741
Closes #10740
2013-12-03 01:19:29 -06:00
|
|
|
if filename.contains(".SYMDEF") { continue }
|
|
|
|
|
2014-05-27 22:44:58 -05:00
|
|
|
let filename = format!("r-{}-{}", name, filename);
|
2014-06-05 17:31:45 -05:00
|
|
|
// LLDB (as mentioned in back::link) crashes on filenames of exactly
|
|
|
|
// 16 bytes in length. If we're including an object file with
|
|
|
|
// exactly 16-bytes of characters, give it some prefix so that it's
|
|
|
|
// not 16 bytes.
|
|
|
|
let filename = if filename.len() == 16 {
|
|
|
|
format!("lldb-fix-{}", filename)
|
|
|
|
} else {
|
|
|
|
filename
|
|
|
|
};
|
2014-07-03 18:03:26 -05:00
|
|
|
let new_filename = self.work_dir.path().join(filename.as_slice());
|
2014-02-19 12:07:49 -06:00
|
|
|
try!(fs::rename(file, &new_filename));
|
2014-07-03 18:03:26 -05:00
|
|
|
self.members.push(Path::new(filename));
|
Add generation of static libraries to rustc
This commit implements the support necessary for generating both intermediate
and result static rust libraries. This is an implementation of my thoughts in
https://mail.mozilla.org/pipermail/rust-dev/2013-November/006686.html.
When compiling a library, we still retain the "lib" option, although now there
are "rlib", "staticlib", and "dylib" as options for crate_type (and these are
stackable). The idea of "lib" is to generate the "compiler default" instead of
having too choose (although all are interchangeable). For now I have left the
"complier default" to be a dynamic library for size reasons.
Of the rust libraries, lib{std,extra,rustuv} will bootstrap with an
rlib/dylib pair, but lib{rustc,syntax,rustdoc,rustpkg} will only be built as a
dynamic object. I chose this for size reasons, but also because you're probably
not going to be embedding the rustc compiler anywhere any time soon.
Other than the options outlined above, there are a few defaults/preferences that
are now opinionated in the compiler:
* If both a .dylib and .rlib are found for a rust library, the compiler will
prefer the .rlib variant. This is overridable via the -Z prefer-dynamic option
* If generating a "lib", the compiler will generate a dynamic library. This is
overridable by explicitly saying what flavor you'd like (rlib, staticlib,
dylib).
* If no options are passed to the command line, and no crate_type is found in
the destination crate, then an executable is generated
With this change, you can successfully build a rust program with 0 dynamic
dependencies on rust libraries. There is still a dynamic dependency on
librustrt, but I plan on removing that in a subsequent commit.
This change includes no tests just yet. Our current testing
infrastructure/harnesses aren't very amenable to doing flavorful things with
linking, so I'm planning on adding a new mode of testing which I believe belongs
as a separate commit.
Closes #552
2013-11-15 16:03:29 -06:00
|
|
|
}
|
2014-01-29 20:42:19 -06:00
|
|
|
Ok(())
|
Add generation of static libraries to rustc
This commit implements the support necessary for generating both intermediate
and result static rust libraries. This is an implementation of my thoughts in
https://mail.mozilla.org/pipermail/rust-dev/2013-November/006686.html.
When compiling a library, we still retain the "lib" option, although now there
are "rlib", "staticlib", and "dylib" as options for crate_type (and these are
stackable). The idea of "lib" is to generate the "compiler default" instead of
having too choose (although all are interchangeable). For now I have left the
"complier default" to be a dynamic library for size reasons.
Of the rust libraries, lib{std,extra,rustuv} will bootstrap with an
rlib/dylib pair, but lib{rustc,syntax,rustdoc,rustpkg} will only be built as a
dynamic object. I chose this for size reasons, but also because you're probably
not going to be embedding the rustc compiler anywhere any time soon.
Other than the options outlined above, there are a few defaults/preferences that
are now opinionated in the compiler:
* If both a .dylib and .rlib are found for a rust library, the compiler will
prefer the .rlib variant. This is overridable via the -Z prefer-dynamic option
* If generating a "lib", the compiler will generate a dynamic library. This is
overridable by explicitly saying what flavor you'd like (rlib, staticlib,
dylib).
* If no options are passed to the command line, and no crate_type is found in
the destination crate, then an executable is generated
With this change, you can successfully build a rust program with 0 dynamic
dependencies on rust libraries. There is still a dynamic dependency on
librustrt, but I plan on removing that in a subsequent commit.
This change includes no tests just yet. Our current testing
infrastructure/harnesses aren't very amenable to doing flavorful things with
linking, so I'm planning on adding a new mode of testing which I believe belongs
as a separate commit.
Closes #552
2013-11-15 16:03:29 -06:00
|
|
|
}
|
|
|
|
}
|
2013-12-16 22:58:21 -06:00
|
|
|
|