]> git.lizzy.rs Git - rust.git/blobdiff - src/librustc_driver/lib.rs
rustc: Load the `rustc_trans` crate at runtime
[rust.git] / src / librustc_driver / lib.rs
index 14d14ab591e3a59dea0cee114de4f5af8790ceb3..029cceda532e56add40a15bc1a50be1c5fc5ae43 100644 (file)
@@ -47,8 +47,6 @@
 extern crate rustc_mir;
 extern crate rustc_resolve;
 extern crate rustc_save_analysis;
-#[cfg(feature="llvm")]
-pub extern crate rustc_trans;
 extern crate rustc_trans_utils;
 extern crate rustc_typeck;
 extern crate serialize;
 use rustc::session::CompileIncomplete;
 use rustc::session::config::{Input, PrintRequest, ErrorOutputType};
 use rustc::session::config::nightly_options;
+use rustc::session::filesearch;
 use rustc::session::{early_error, early_warn};
 use rustc::lint::Lint;
 use rustc::lint;
 use rustc::middle::cstore::CrateStore;
 use rustc_metadata::locator;
 use rustc_metadata::cstore::CStore;
+use rustc_metadata::dynamic_lib::DynamicLibrary;
 use rustc::util::common::{time, ErrorReported};
 use rustc_trans_utils::trans_crate::TransCrate;
 
 use serialize::json::ToJson;
 
 use std::any::Any;
-use std::cmp::max;
 use std::cmp::Ordering::Equal;
+use std::cmp::max;
 use std::default::Default;
+use std::env::consts::{DLL_PREFIX, DLL_SUFFIX};
 use std::env;
 use std::ffi::OsString;
 use std::io::{self, Read, Write};
 use std::iter::repeat;
-use std::path::PathBuf;
+use std::mem;
+use std::panic;
+use std::path::{PathBuf, Path};
 use std::process::{self, Command, Stdio};
 use std::rc::Rc;
 use std::str;
-use std::sync::{Arc, Mutex};
+use std::sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT, Ordering};
+use std::sync::{Once, ONCE_INIT};
 use std::thread;
 
 use syntax::ast;
@@ -168,7 +172,7 @@ pub fn run<F>(run_compiler: F) -> isize
                     handler.emit(&MultiSpan::new(),
                                  "aborting due to previous error(s)",
                                  errors::Level::Fatal);
-                    exit_on_err();
+                    panic::resume_unwind(Box::new(errors::FatalErrorMarker));
                 }
             }
         }
@@ -176,106 +180,247 @@ pub fn run<F>(run_compiler: F) -> isize
     0
 }
 
-#[cfg(not(feature="llvm"))]
-pub use rustc_trans_utils::trans_crate::MetadataOnlyTransCrate as DefaultTransCrate;
-#[cfg(feature="llvm")]
-pub use rustc_trans::LlvmTransCrate as DefaultTransCrate;
+fn load_backend_from_dylib(path: &Path) -> fn() -> Box<TransCrate> {
+    // Note that we're specifically using `open_global_now` here rather than
+    // `open`, namely we want the behavior on Unix of RTLD_GLOBAL and RTLD_NOW,
+    // where NOW means "bind everything right now" because we don't want
+    // surprises later on and RTLD_GLOBAL allows the symbols to be made
+    // available for future dynamic libraries opened. This is currently used by
+    // loading LLVM and then making its symbols available for other dynamic
+    // libraries.
+    let lib = match DynamicLibrary::open_global_now(path) {
+        Ok(lib) => lib,
+        Err(err) => {
+            let err = format!("couldn't load codegen backend {:?}: {:?}",
+                              path,
+                              err);
+            early_error(ErrorOutputType::default(), &err);
+        }
+    };
+    unsafe {
+        match lib.symbol("__rustc_codegen_backend") {
+            Ok(f) => {
+                mem::forget(lib);
+                mem::transmute::<*mut u8, _>(f)
+            }
+            Err(e) => {
+                let err = format!("couldn't load codegen backend as it \
+                                   doesn't export the `__rustc_codegen_backend` \
+                                   symbol: {:?}", e);
+                early_error(ErrorOutputType::default(), &err);
+            }
+        }
+    }
+}
 
-#[cfg(not(feature="llvm"))]
-pub mod rustc_trans {
-    pub use rustc_trans_utils::trans_crate::MetadataOnlyTransCrate as LlvmTransCrate;
+pub fn get_trans(sess: &Session) -> Box<TransCrate> {
+    static INIT: Once = ONCE_INIT;
+    static mut LOAD: fn() -> Box<TransCrate> = || unreachable!();
+
+    INIT.call_once(|| {
+        let trans_name = sess.opts.debugging_opts.codegen_backend.as_ref();
+        let backend = match trans_name.map(|s| &**s) {
+            None |
+            Some("llvm") => get_trans_default(),
+            Some("metadata_only") => {
+                rustc_trans_utils::trans_crate::MetadataOnlyTransCrate::new
+            }
+            Some(filename) if filename.contains(".") => {
+                load_backend_from_dylib(filename.as_ref())
+            }
+            Some(trans_name) => {
+                sess.fatal(&format!("unknown codegen backend {}", trans_name));
+            }
+        };
 
-    pub fn print_version() {}
-    pub fn print_passes() {}
+        unsafe {
+            LOAD = backend;
+        }
+    });
+    let backend = unsafe { LOAD() };
+    backend.init(sess);
+    backend
 }
 
-fn load_backend_from_dylib(sess: &Session, backend_name: &str) -> Box<TransCrate> {
-    use std::sync::mpsc;
-    use std::path::Path;
-    use syntax::symbol::Symbol;
-    use rustc::session::config::OutputFilenames;
-    use rustc::ty::TyCtxt;
-    use rustc::ty::maps::Providers;
-    use rustc::middle::cstore::MetadataLoader;
-    use rustc::dep_graph::DepGraph;
-    use rustc_metadata::dynamic_lib::DynamicLibrary;
-    /// This prevents the dylib from being unloaded when there is still a TransCrate open
-    struct ExternTransCrate {
-        _lib: DynamicLibrary,
-        trans: Box<TransCrate>,
+fn get_trans_default() -> fn() -> Box<TransCrate> {
+    // For now we only allow this function to be called once as it'll dlopen a
+    // few things, which seems to work best if we only do that once. In
+    // general this assertion never trips due to the once guard in `get_trans`,
+    // but there's a few manual calls to this function in this file we protect
+    // against.
+    static LOADED: AtomicBool = ATOMIC_BOOL_INIT;
+    assert!(!LOADED.fetch_or(true, Ordering::SeqCst),
+            "cannot load the default trans backend twice");
+
+    // When we're compiling this library with `--test` it'll run as a binary but
+    // not actually exercise much functionality. As a result most of the logic
+    // here is defunkt (it assumes we're a dynamic library in a sysroot) so
+    // let's just return a dummy creation function which won't be used in
+    // general anyway.
+    if cfg!(test) {
+        return rustc_trans_utils::trans_crate::MetadataOnlyTransCrate::new
     }
-    impl TransCrate for ExternTransCrate {
-        fn print(&self, req: PrintRequest, sess: &Session) {
-            self.trans.print(req, sess);
-        }
-        fn target_features(&self, sess: &Session) -> Vec<Symbol> {
-            self.trans.target_features((sess))
+
+    let target = session::config::host_triple();
+    let mut sysroot_candidates = vec![filesearch::get_or_default_sysroot()];
+    let path = current_dll_path()
+        .and_then(|s| s.canonicalize().ok());
+    if let Some(dll) = path {
+        // use `parent` twice to chop off the file name and then also the
+        // directory containing the dll which should be either `lib` or `bin`.
+        if let Some(path) = dll.parent().and_then(|p| p.parent()) {
+            // The original `path` pointed at the `rustc_driver` crate's dll.
+            // Now that dll should only be in one of two locations. The first is
+            // in the compiler's libdir, for example `$sysroot/lib/*.dll`. The
+            // other is the target's libdir, for example
+            // `$sysroot/lib/rustlib/$target/lib/*.dll`.
+            //
+            // We don't know which, so let's assume that if our `path` above
+            // ends in `$target` we *could* be in the target libdir, and always
+            // assume that we may be in the main libdir.
+            sysroot_candidates.push(path.to_owned());
+
+            if path.ends_with(target) {
+                sysroot_candidates.extend(path.parent() // chop off `$target`
+                    .and_then(|p| p.parent())           // chop off `rustlib`
+                    .and_then(|p| p.parent())           // chop off `lib`
+                    .map(|s| s.to_owned()));
+            }
         }
+    }
 
-        fn metadata_loader(&self) -> Box<MetadataLoader> {
-            self.trans.metadata_loader()
+    let sysroot = sysroot_candidates.iter()
+        .map(|sysroot| {
+            let libdir = filesearch::relative_target_lib_path(&sysroot, &target);
+            sysroot.join(&libdir).join("codegen-backends")
+        })
+        .filter(|f| {
+            info!("codegen backend candidate: {}", f.display());
+            f.exists()
+        })
+        .next();
+    let sysroot = match sysroot {
+        Some(path) => path,
+        None => {
+            let candidates = sysroot_candidates.iter()
+                .map(|p| p.display().to_string())
+                .collect::<Vec<_>>()
+                .join("\n* ");
+            let err = format!("failed to find a `codegen-backends` folder \
+                               in the sysroot candidates:\n* {}", candidates);
+            early_error(ErrorOutputType::default(), &err);
+        }
+    };
+    info!("probing {} for a codegen backend", sysroot.display());
+
+    let d = match sysroot.read_dir() {
+        Ok(d) => d,
+        Err(e) => {
+            let err = format!("failed to load default codegen backend, couldn't \
+                               read `{}`: {}", sysroot.display(), e);
+            early_error(ErrorOutputType::default(), &err);
         }
-        fn provide(&self, providers: &mut Providers) {
-            self.trans.provide(providers)
+    };
+
+    let mut file: Option<PathBuf> = None;
+
+    for entry in d.filter_map(|e| e.ok()) {
+        let path = entry.path();
+        let filename = match path.file_name().and_then(|s| s.to_str()) {
+            Some(s) => s,
+            None => continue,
+        };
+        if !(filename.starts_with(DLL_PREFIX) && filename.ends_with(DLL_SUFFIX)) {
+            continue
         }
-        fn provide_extern(&self, providers: &mut Providers) {
-            self.trans.provide_extern(providers)
+        let name = &filename[DLL_PREFIX.len() .. filename.len() - DLL_SUFFIX.len()];
+        if !name.starts_with("rustc_trans") {
+            continue
         }
-        fn trans_crate<'a, 'tcx>(
-            &self,
-            tcx: TyCtxt<'a, 'tcx, 'tcx>,
-            rx: mpsc::Receiver<Box<Any + Send>>
-        ) -> Box<Any> {
-            self.trans.trans_crate(tcx, rx)
+        if let Some(ref prev) = file {
+            let err = format!("duplicate codegen backends found\n\
+                first:  {}\n\
+                second: {}\n\
+            ", prev.display(), path.display());
+            early_error(ErrorOutputType::default(), &err);
         }
+        file = Some(path.clone());
+    }
 
-        fn join_trans_and_link(
-            &self,
-            trans: Box<Any>,
-            sess: &Session,
-            dep_graph: &DepGraph,
-            outputs: &OutputFilenames,
-        ) -> Result<(), CompileIncomplete> {
-            self.trans.join_trans_and_link(trans, sess, dep_graph, outputs)
+    match file {
+        Some(ref s) => return load_backend_from_dylib(s),
+        None => {
+            let err = format!("failed to load default codegen backend, no appropriate \
+                               codegen dylib found in `{}`", sysroot.display());
+            early_error(ErrorOutputType::default(), &err);
         }
     }
 
-    match DynamicLibrary::open(Some(Path::new(backend_name))) {
-        Ok(lib) => {
-            unsafe {
-                let trans = {
-                    let __rustc_codegen_backend: unsafe fn(&Session) -> Box<TransCrate>;
-                    __rustc_codegen_backend = match lib.symbol("__rustc_codegen_backend") {
-                        Ok(f) => ::std::mem::transmute::<*mut u8, _>(f),
-                        Err(e) => sess.fatal(&format!("Couldnt load codegen backend as it\
-                        doesn't export the __rustc_backend_new symbol: {:?}", e)),
-                    };
-                    __rustc_codegen_backend(sess)
-                };
-                Box::new(ExternTransCrate {
-                    _lib: lib,
-                    trans
-                })
+    #[cfg(unix)]
+    fn current_dll_path() -> Option<PathBuf> {
+        use std::ffi::{OsStr, CStr};
+        use std::os::unix::prelude::*;
+
+        unsafe {
+            let addr = current_dll_path as usize as *mut _;
+            let mut info = mem::zeroed();
+            if libc::dladdr(addr, &mut info) == 0 {
+                info!("dladdr failed");
+                return None
             }
-        }
-        Err(err) => {
-            sess.fatal(&format!("Couldnt load codegen backend {:?}: {:?}", backend_name, err));
+            if info.dli_fname.is_null() {
+                info!("dladdr returned null pointer");
+                return None
+            }
+            let bytes = CStr::from_ptr(info.dli_fname).to_bytes();
+            let os = OsStr::from_bytes(bytes);
+            Some(PathBuf::from(os))
         }
     }
-}
 
-pub fn get_trans(sess: &Session) -> Box<TransCrate> {
-    let trans_name = sess.opts.debugging_opts.codegen_backend.clone();
-    match trans_name.as_ref().map(|s|&**s) {
-        None => DefaultTransCrate::new(&sess),
-        Some("llvm") => rustc_trans::LlvmTransCrate::new(&sess),
-        Some("metadata_only") => {
-            rustc_trans_utils::trans_crate::MetadataOnlyTransCrate::new(&sess)
+    #[cfg(windows)]
+    fn current_dll_path() -> Option<PathBuf> {
+        use std::ffi::OsString;
+        use std::os::windows::prelude::*;
+
+        extern "system" {
+            fn GetModuleHandleExW(dwFlags: u32,
+                                  lpModuleName: usize,
+                                  phModule: *mut usize) -> i32;
+            fn GetModuleFileNameW(hModule: usize,
+                                  lpFilename: *mut u16,
+                                  nSize: u32) -> u32;
         }
-        Some(filename) if filename.contains(".") => {
-            load_backend_from_dylib(&sess, &filename)
+
+        const GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS: u32 = 0x00000004;
+
+        unsafe {
+            let mut module = 0;
+            let r = GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
+                                       current_dll_path as usize,
+                                       &mut module);
+            if r == 0 {
+                info!("GetModuleHandleExW failed: {}", io::Error::last_os_error());
+                return None
+            }
+            let mut space = Vec::with_capacity(1024);
+            let r = GetModuleFileNameW(module,
+                                       space.as_mut_ptr(),
+                                       space.capacity() as u32);
+            if r == 0 {
+                info!("GetModuleFileNameW failed: {}", io::Error::last_os_error());
+                return None
+            }
+            let r = r as usize;
+            if r >= space.capacity() {
+                info!("our buffer was too small? {}",
+                      io::Error::last_os_error());
+                return None
+            }
+            space.set_len(r);
+            let os = OsString::from_wide(&space);
+            Some(PathBuf::from(os))
         }
-        Some(trans_name) => sess.fatal(&format!("Unknown codegen backend {}", trans_name)),
     }
 }
 
@@ -396,8 +541,9 @@ fn parse_pretty(sess: &Session,
     } else {
         None
     };
-    if pretty.is_none() && sess.unstable_options() {
-        matches.opt_str("unpretty").map(|a| {
+
+    if pretty.is_none() {
+        sess.opts.debugging_opts.unpretty.as_ref().map(|a| {
             // extended with unstable pretty-print variants
             pretty::parse_pretty(sess, &a, true)
         })
@@ -718,7 +864,7 @@ fn build_controller(&mut self,
             control.after_hir_lowering.stop = Compilation::Stop;
         }
 
-        if save_analysis(sess) {
+        if sess.opts.debugging_opts.save_analysis {
             enable_save_analysis(&mut control);
         }
 
@@ -753,10 +899,6 @@ pub fn enable_save_analysis(control: &mut CompileController) {
     control.make_glob_map = resolve::MakeGlobMap::Yes;
 }
 
-fn save_analysis(sess: &Session) -> bool {
-    sess.opts.debugging_opts.save_analysis
-}
-
 impl RustcDefaultCalls {
     pub fn list_metadata(sess: &Session,
                          cstore: &CrateStore,
@@ -930,7 +1072,7 @@ fn unw(x: Option<&str>) -> &str {
         println!("commit-date: {}", unw(commit_date_str()));
         println!("host: {}", config::host_triple());
         println!("release: {}", unw(release_str()));
-        rustc_trans::print_version();
+        get_trans_default()().print_version();
     }
 }
 
@@ -1227,7 +1369,7 @@ pub fn handle_options(args: &[String]) -> Option<getopts::Matches> {
     }
 
     if cg_flags.contains(&"passes=list".to_string()) {
-        rustc_trans::print_passes();
+        get_trans_default()().print_passes();
         return None;
     }
 
@@ -1280,27 +1422,16 @@ pub fn in_rustc_thread<F, R>(f: F) -> Result<R, Box<Any + Send>>
 /// The diagnostic emitter yielded to the procedure should be used for reporting
 /// errors of the compiler.
 pub fn monitor<F: FnOnce() + Send + 'static>(f: F) {
-    struct Sink(Arc<Mutex<Vec<u8>>>);
-    impl Write for Sink {
-        fn write(&mut self, data: &[u8]) -> io::Result<usize> {
-            Write::write(&mut *self.0.lock().unwrap(), data)
-        }
-        fn flush(&mut self) -> io::Result<()> {
-            Ok(())
-        }
-    }
-
-    let data = Arc::new(Mutex::new(Vec::new()));
-    let err = Sink(data.clone());
-
     let result = in_rustc_thread(move || {
-        io::set_panic(Some(box err));
         f()
     });
 
     if let Err(value) = result {
         // Thread panicked without emitting a fatal diagnostic
-        if !value.is::<errors::FatalError>() {
+        if !value.is::<errors::FatalErrorMarker>() {
+            // Emit a newline
+            eprintln!("");
+
             let emitter =
                 Box::new(errors::emitter::EmitterWriter::stderr(errors::ColorConfig::Auto,
                                                                 None,
@@ -1325,30 +1456,12 @@ fn flush(&mut self) -> io::Result<()> {
                              &note,
                              errors::Level::Note);
             }
-            if match env::var_os("RUST_BACKTRACE") {
-                Some(val) => &val != "0",
-                None => false,
-            } {
-                handler.emit(&MultiSpan::new(),
-                             "run with `RUST_BACKTRACE=1` for a backtrace",
-                             errors::Level::Note);
-            }
-
-            eprintln!("{}", str::from_utf8(&data.lock().unwrap()).unwrap());
         }
 
-        exit_on_err();
+        panic::resume_unwind(Box::new(errors::FatalErrorMarker));
     }
 }
 
-fn exit_on_err() -> ! {
-    // Panic so the process returns a failure code, but don't pollute the
-    // output with some unnecessary panic messages, we've already
-    // printed everything that we needed to.
-    io::set_panic(Some(box io::sink()));
-    panic!();
-}
-
 #[cfg(stage0)]
 pub fn diagnostics_registry() -> errors::registry::Registry {
     use errors::registry::Registry;
@@ -1365,8 +1478,8 @@ pub fn diagnostics_registry() -> errors::registry::Registry {
     all_errors.extend_from_slice(&rustc_typeck::DIAGNOSTICS);
     all_errors.extend_from_slice(&rustc_resolve::DIAGNOSTICS);
     all_errors.extend_from_slice(&rustc_privacy::DIAGNOSTICS);
-    #[cfg(feature="llvm")]
-    all_errors.extend_from_slice(&rustc_trans::DIAGNOSTICS);
+    // FIXME: need to figure out a way to get these back in here
+    // all_errors.extend_from_slice(get_trans(sess).diagnostics());
     all_errors.extend_from_slice(&rustc_trans_utils::DIAGNOSTICS);
     all_errors.extend_from_slice(&rustc_const_eval::DIAGNOSTICS);
     all_errors.extend_from_slice(&rustc_metadata::DIAGNOSTICS);
@@ -1378,20 +1491,19 @@ pub fn diagnostics_registry() -> errors::registry::Registry {
     Registry::new(&all_errors)
 }
 
-pub fn get_args() -> Vec<String> {
-    env::args_os().enumerate()
-        .map(|(i, arg)| arg.into_string().unwrap_or_else(|arg| {
-             early_error(ErrorOutputType::default(),
-                         &format!("Argument {} is not valid Unicode: {:?}", i, arg))
-         }))
-        .collect()
-}
-
 pub fn main() {
     env_logger::init().unwrap();
-    let result = run(|| run_compiler(&get_args(),
-                                     &mut RustcDefaultCalls,
-                                     None,
-                                     None));
+    let result = run(|| {
+        let args = env::args_os().enumerate()
+            .map(|(i, arg)| arg.into_string().unwrap_or_else(|arg| {
+                early_error(ErrorOutputType::default(),
+                            &format!("Argument {} is not valid Unicode: {:?}", i, arg))
+            }))
+            .collect::<Vec<_>>();
+        run_compiler(&args,
+                     &mut RustcDefaultCalls,
+                     None,
+                     None)
+    });
     process::exit(result as i32);
 }