Add Emscripten-specific linker

It claims to accept most GNU linker options, but in fact most of them
have no effect and instead it requires some special options which are
easier to handle in a separate trait.

Currently added:
 - `export_symbols`: works on executables as special Emscripten case
since staticlibs/dylibs aren't compiled to JS, while exports are
required to be accessible from JS.
Fixes #39171.
 - `optimize` - translates Rust's optimization level to Emscripten
optimization level (whether passed via `-C opt-level=...` or `-O...`).
Fixes #36899.
 - `debuginfo` - translates debug info; Emscripten has 5 debug levels
while Rust has 3, so chose to translate `-C debuginfo=1` to `-g3`
(preserves whitespace, variable and function names for easy debugging).
Fixes #36901.
 - `no_default_libraries` - tells Emscripten to exlude `memcpy` and co.
This commit is contained in:
Ingvar Stepanyan 2017-02-03 14:58:13 +00:00
parent bc524d3d55
commit eed6168a3b
5 changed files with 159 additions and 3 deletions

1
src/Cargo.lock generated
View File

@ -551,6 +551,7 @@ dependencies = [
"rustc_incremental 0.0.0",
"rustc_llvm 0.0.0",
"rustc_platform_intrinsics 0.0.0",
"serialize 0.0.0",
"syntax 0.0.0",
"syntax_pos 0.0.0",
]

View File

@ -22,5 +22,6 @@ rustc_errors = { path = "../librustc_errors" }
rustc_incremental = { path = "../librustc_incremental" }
rustc_llvm = { path = "../librustc_llvm" }
rustc_platform_intrinsics = { path = "../librustc_platform_intrinsics" }
serialize = { path = "../libserialize" }
syntax = { path = "../libsyntax" }
syntax_pos = { path = "../libsyntax_pos" }

View File

@ -830,7 +830,8 @@ fn link_args(cmd: &mut Linker,
// If we're building a dynamic library then some platforms need to make sure
// that all symbols are exported correctly from the dynamic library.
if crate_type != config::CrateTypeExecutable {
if crate_type != config::CrateTypeExecutable ||
sess.target.target.options.is_like_emscripten {
cmd.export_symbols(tmpdir, crate_type);
}

View File

@ -23,8 +23,8 @@ use back::symbol_export::{self, ExportedSymbols};
use middle::dependency_format::Linkage;
use rustc::hir::def_id::{LOCAL_CRATE, CrateNum};
use session::Session;
use session::config::CrateType;
use session::config;
use session::config::{self, CrateType, OptLevel, DebugInfoLevel};
use serialize::{json, Encoder};
/// For all the linkers we support, and information they might
/// need out of the shared crate context before we get rid of it.
@ -51,6 +51,12 @@ impl<'a, 'tcx> LinkerInfo {
sess: sess,
info: self
}) as Box<Linker>
} else if sess.target.target.options.is_like_emscripten {
Box::new(EmLinker {
cmd: cmd,
sess: sess,
info: self
}) as Box<Linker>
} else {
Box::new(GnuLinker {
cmd: cmd,
@ -488,6 +494,152 @@ impl<'a> Linker for MsvcLinker<'a> {
}
}
pub struct EmLinker<'a> {
cmd: &'a mut Command,
sess: &'a Session,
info: &'a LinkerInfo
}
impl<'a> Linker for EmLinker<'a> {
fn include_path(&mut self, path: &Path) {
self.cmd.arg("-L").arg(path);
}
fn link_staticlib(&mut self, lib: &str) {
self.cmd.arg("-l").arg(lib);
}
fn output_filename(&mut self, path: &Path) {
self.cmd.arg("-o").arg(path);
}
fn add_object(&mut self, path: &Path) {
self.cmd.arg(path);
}
fn link_dylib(&mut self, lib: &str) {
// Emscripten always links statically
self.link_staticlib(lib);
}
fn link_whole_staticlib(&mut self, lib: &str, _search_path: &[PathBuf]) {
// not supported?
self.link_staticlib(lib);
}
fn link_whole_rlib(&mut self, lib: &Path) {
// not supported?
self.link_rlib(lib);
}
fn link_rust_dylib(&mut self, lib: &str, _path: &Path) {
self.link_dylib(lib);
}
fn link_rlib(&mut self, lib: &Path) {
self.add_object(lib);
}
fn position_independent_executable(&mut self) {
// noop
}
fn args(&mut self, args: &[String]) {
self.cmd.args(args);
}
fn framework_path(&mut self, _path: &Path) {
bug!("frameworks are not supported on Emscripten")
}
fn link_framework(&mut self, _framework: &str) {
bug!("frameworks are not supported on Emscripten")
}
fn gc_sections(&mut self, _keep_metadata: bool) {
// noop
}
fn optimize(&mut self) {
// Emscripten performs own optimizations
self.cmd.arg(match self.sess.opts.optimize {
OptLevel::No => "-O0",
OptLevel::Less => "-O1",
OptLevel::Default => "-O2",
OptLevel::Aggressive => "-O3",
OptLevel::Size => "-Os",
OptLevel::SizeMin => "-Oz"
});
}
fn debuginfo(&mut self) {
// Preserve names or generate source maps depending on debug info
self.cmd.arg(match self.sess.opts.debuginfo {
DebugInfoLevel::NoDebugInfo => "-g0",
DebugInfoLevel::LimitedDebugInfo => "-g3",
DebugInfoLevel::FullDebugInfo => "-g4"
});
}
fn no_default_libraries(&mut self) {
self.cmd.args(&["-s", "DEFAULT_LIBRARY_FUNCS_TO_INCLUDE=[]"]);
}
fn build_dylib(&mut self, _out_filename: &Path) {
bug!("building dynamic library is unsupported on Emscripten")
}
fn whole_archives(&mut self) {
// noop
}
fn no_whole_archives(&mut self) {
// noop
}
fn hint_static(&mut self) {
// noop
}
fn hint_dynamic(&mut self) {
// noop
}
fn export_symbols(&mut self, _tmpdir: &Path, crate_type: CrateType) {
let symbols = &self.info.exports[&crate_type];
debug!("EXPORTED SYMBOLS:");
self.cmd.arg("-s");
let mut arg = OsString::from("EXPORTED_FUNCTIONS=");
let mut encoded = String::new();
{
let mut encoder = json::Encoder::new(&mut encoded);
let res = encoder.emit_seq(symbols.len(), |encoder| {
for (i, sym) in symbols.iter().enumerate() {
encoder.emit_seq_elt(i, |encoder| {
encoder.emit_str(&("_".to_string() + sym))
})?;
}
Ok(())
});
if let Err(e) = res {
self.sess.fatal(&format!("failed to encode exported symbols: {}", e));
}
}
debug!("{}", encoded);
arg.push(encoded);
self.cmd.arg(arg);
}
fn subsystem(&mut self, _subsystem: &str) {
// noop
}
}
fn exported_symbols(scx: &SharedCrateContext,
exported_symbols: &ExportedSymbols,
crate_type: CrateType)

View File

@ -59,6 +59,7 @@ extern crate rustc_bitflags;
#[macro_use] extern crate syntax;
extern crate syntax_pos;
extern crate rustc_errors as errors;
extern crate serialize;
pub use rustc::session;
pub use rustc::middle;