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:
parent
bc524d3d55
commit
eed6168a3b
|
@ -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",
|
||||
]
|
||||
|
|
|
@ -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" }
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue