about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2017-09-25 18:05:22 +0000
committerbors <bors@rust-lang.org>2017-09-25 18:05:22 +0000
commit3df1f7b82d9bede5122ee745cdd4e731abbce892 (patch)
tree8940e26551549d564690fef64cf207390252f45b /src
parent91dbf52af3de0436bcc032229540db1fe14b6df8 (diff)
parent843cd5bacc56dc7a9ec45d3aaebf717566f5bf36 (diff)
downloadrust-3df1f7b82d9bede5122ee745cdd4e731abbce892.tar.gz
rust-3df1f7b82d9bede5122ee745cdd4e731abbce892.zip
Auto merge of #44085 - bjorn3:no_llvm_write_metadata, r=arielb1
Allow writing metadata without llvm

# Todo:

* [x] Rebase
* [x] Fix eventual errors
* [x] <strike>Find some crate to write elf files</strike> (will do it later)

Cc #43842
Diffstat (limited to 'src')
-rw-r--r--src/Cargo.lock5
-rw-r--r--src/bootstrap/builder.rs5
-rw-r--r--src/librustc_driver/driver.rs127
-rw-r--r--src/librustc_driver/lib.rs84
-rw-r--r--src/librustc_driver/test.rs3
-rw-r--r--src/librustc_trans/back/link.rs52
-rw-r--r--src/librustc_trans/base.rs68
-rw-r--r--src/librustc_trans/lib.rs58
-rw-r--r--src/librustc_trans_utils/Cargo.toml8
-rw-r--r--src/librustc_trans_utils/lib.rs81
-rw-r--r--src/librustc_trans_utils/link.rs47
-rw-r--r--src/librustc_trans_utils/trans_crate.rs249
12 files changed, 520 insertions, 267 deletions
diff --git a/src/Cargo.lock b/src/Cargo.lock
index 807375e00af..e9a20a7c79c 100644
--- a/src/Cargo.lock
+++ b/src/Cargo.lock
@@ -1778,7 +1778,12 @@ dependencies = [
 name = "rustc_trans_utils"
 version = "0.0.0"
 dependencies = [
+ "ar 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "flate2 0.2.19 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "rustc 0.0.0",
+ "rustc_back 0.0.0",
  "syntax 0.0.0",
  "syntax_pos 0.0.0",
 ]
diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs
index 8a6c998c932..ffd959d86f5 100644
--- a/src/bootstrap/builder.rs
+++ b/src/bootstrap/builder.rs
@@ -531,7 +531,10 @@ impl<'a> Builder<'a> {
         // For other crates, however, we know that we've already got a standard
         // library up and running, so we can use the normal compiler to compile
         // build scripts in that situation.
-        if mode == Mode::Libstd {
+        //
+        // If LLVM support is disabled we need to use the snapshot compiler to compile
+        // build scripts, as the new compiler doesnt support executables.
+        if mode == Mode::Libstd || !self.build.config.llvm_enabled {
             cargo.env("RUSTC_SNAPSHOT", &self.initial_rustc)
                  .env("RUSTC_SNAPSHOT_LIBDIR", self.rustc_snapshot_libdir());
         } else {
diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs
index d9df2e1f9ad..57989f75cba 100644
--- a/src/librustc_driver/driver.rs
+++ b/src/librustc_driver/driver.rs
@@ -8,8 +8,6 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#![cfg_attr(not(feature="llvm"), allow(dead_code))]
-
 use rustc::dep_graph::DepGraph;
 use rustc::hir::{self, map as hir_map};
 use rustc::hir::lowering::lower_crate;
@@ -34,8 +32,8 @@ use rustc_incremental;
 use rustc_resolve::{MakeGlobMap, Resolver};
 use rustc_metadata::creader::CrateLoader;
 use rustc_metadata::cstore::{self, CStore};
-use rustc_trans::back::write;
 use rustc_trans as trans;
+use rustc_trans_utils::trans_crate::TransCrate;
 use rustc_typeck as typeck;
 use rustc_privacy;
 use rustc_plugin::registry::Registry;
@@ -43,6 +41,7 @@ use rustc_plugin as plugin;
 use rustc_passes::{ast_validation, no_asm, loops, consts, static_recursion, hir_stats};
 use rustc_const_eval::{self, check_match};
 use super::Compilation;
+use ::DefaultTransCrate;
 
 use serialize::json;
 
@@ -76,7 +75,8 @@ pub fn compile_input(sess: &Session,
                      output: &Option<PathBuf>,
                      addl_plugins: Option<Vec<String>>,
                      control: &CompileController) -> CompileResult {
-    use rustc_trans::back::write::OngoingCrateTranslation;
+    use rustc::session::config::CrateType;
+
     macro_rules! controller_entry_point {
         ($point: ident, $tsess: expr, $make_state: expr, $phase_result: expr) => {{
             let state = &mut $make_state;
@@ -94,17 +94,16 @@ pub fn compile_input(sess: &Session,
     }
 
     if cfg!(not(feature="llvm")) {
-        use rustc::session::config::CrateType;
-        if !sess.opts.debugging_opts.no_trans && sess.opts.output_types.should_trans() {
-            sess.err("LLVM is not supported by this rustc. Please use -Z no-trans to compile")
-        }
-
-        if sess.opts.crate_types.iter().all(|&t|{
-            t != CrateType::CrateTypeRlib && t != CrateType::CrateTypeExecutable
-        }) && !sess.opts.crate_types.is_empty() {
-            sess.err(
-                "LLVM is not supported by this rustc, so non rlib libraries are not supported"
-            );
+        for cty in sess.opts.crate_types.iter() {
+            match *cty {
+                CrateType::CrateTypeRlib | CrateType::CrateTypeDylib |
+                CrateType::CrateTypeExecutable => {},
+                _ => {
+                    sess.parse_sess.span_diagnostic.warn(
+                        &format!("LLVM unsupported, so output type {} is not supported", cty)
+                    );
+                },
+            }
         }
 
         sess.abort_if_errors();
@@ -117,7 +116,7 @@ pub fn compile_input(sess: &Session,
     // We need nested scopes here, because the intermediate results can keep
     // large chunks of memory alive and we want to free them as soon as
     // possible to keep the peak memory usage low
-    let (outputs, trans, dep_graph): (OutputFilenames, OngoingCrateTranslation, DepGraph) = {
+    let (outputs, trans, dep_graph) = {
         let krate = match phase_1_parse_input(control, sess, input) {
             Ok(krate) => krate,
             Err(mut parse_error) => {
@@ -246,7 +245,7 @@ pub fn compile_input(sess: &Session,
                 tcx.print_debug_stats();
             }
 
-            let trans = phase_4_translate_to_llvm(tcx, rx);
+            let trans = phase_4_translate_to_llvm::<DefaultTransCrate>(tcx, rx);
 
             if log_enabled!(::log::LogLevel::Info) {
                 println!("Post-trans");
@@ -264,44 +263,42 @@ pub fn compile_input(sess: &Session,
         })??
     };
 
-    if cfg!(not(feature="llvm")) {
-        let (_, _) = (outputs, trans);
-        sess.fatal("LLVM is not supported by this rustc");
+    if sess.opts.debugging_opts.print_type_sizes {
+        sess.code_stats.borrow().print_type_sizes();
     }
 
-    #[cfg(feature="llvm")]
-    {
-        if sess.opts.debugging_opts.print_type_sizes {
-            sess.code_stats.borrow().print_type_sizes();
-        }
-
-        let (phase5_result, trans) = phase_5_run_llvm_passes(sess, &dep_graph, trans);
+    let (phase5_result, trans) =
+        phase_5_run_llvm_passes::<DefaultTransCrate>(sess, &dep_graph, trans);
 
-        controller_entry_point!(after_llvm,
-                                sess,
-                                CompileState::state_after_llvm(input, sess, outdir, output, &trans),
-                                phase5_result);
-        phase5_result?;
+    controller_entry_point!(after_llvm,
+                            sess,
+                            CompileState::state_after_llvm(input, sess, outdir, output, &trans),
+                            phase5_result);
+    phase5_result?;
 
-        phase_6_link_output(sess, &trans, &outputs);
-
-        // Now that we won't touch anything in the incremental compilation directory
-        // any more, we can finalize it (which involves renaming it)
-        rustc_incremental::finalize_session_directory(sess, trans.link.crate_hash);
+    // Run the linker on any artifacts that resulted from the LLVM run.
+    // This should produce either a finished executable or library.
+    time(sess.time_passes(), "linking", || {
+        DefaultTransCrate::link_binary(sess, &trans, &outputs)
+    });
 
-        if sess.opts.debugging_opts.perf_stats {
-            sess.print_perf_stats();
-        }
+    // Now that we won't touch anything in the incremental compilation directory
+    // any more, we can finalize it (which involves renaming it)
+    #[cfg(feature="llvm")]
+    rustc_incremental::finalize_session_directory(sess, trans.link.crate_hash);
 
-        controller_entry_point!(
-            compilation_done,
-            sess,
-            CompileState::state_when_compilation_done(input, sess, outdir, output),
-            Ok(())
-        );
+    if sess.opts.debugging_opts.perf_stats {
+        sess.print_perf_stats();
+    }
 
+    controller_entry_point!(
+        compilation_done,
+        sess,
+        CompileState::state_when_compilation_done(input, sess, outdir, output),
         Ok(())
-    }
+    );
+
+    Ok(())
 }
 
 fn keep_hygiene_data(sess: &Session) -> bool {
@@ -970,7 +967,7 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session,
     mir::provide(&mut local_providers);
     reachable::provide(&mut local_providers);
     rustc_privacy::provide(&mut local_providers);
-    trans::provide_local(&mut local_providers);
+    DefaultTransCrate::provide_local(&mut local_providers);
     typeck::provide(&mut local_providers);
     ty::provide(&mut local_providers);
     traits::provide(&mut local_providers);
@@ -982,7 +979,7 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session,
 
     let mut extern_providers = ty::maps::Providers::default();
     cstore::provide(&mut extern_providers);
-    trans::provide_extern(&mut extern_providers);
+    DefaultTransCrate::provide_extern(&mut extern_providers);
     ty::provide_extern(&mut extern_providers);
     traits::provide_extern(&mut extern_providers);
     // FIXME(eddyb) get rid of this once we replace const_eval with miri.
@@ -1126,9 +1123,9 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session,
 
 /// Run the translation phase to LLVM, after which the AST and analysis can
 /// be discarded.
-pub fn phase_4_translate_to_llvm<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
+pub fn phase_4_translate_to_llvm<'a, 'tcx, Trans: TransCrate>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                                            rx: mpsc::Receiver<Box<Any + Send>>)
-                                           -> write::OngoingCrateTranslation {
+                                           -> <Trans as TransCrate>::OngoingCrateTranslation {
     let time_passes = tcx.sess.time_passes();
 
     time(time_passes,
@@ -1137,9 +1134,8 @@ pub fn phase_4_translate_to_llvm<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
 
     let translation =
         time(time_passes, "translation", move || {
-            trans::trans_crate(tcx, rx)
+            Trans::trans_crate(tcx, rx)
         });
-
     if tcx.sess.profile_queries() {
         profile::dump("profile_queries".to_string())
     }
@@ -1149,15 +1145,14 @@ pub fn phase_4_translate_to_llvm<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
 
 /// Run LLVM itself, producing a bitcode file, assembly file or object file
 /// as a side effect.
-#[cfg(feature="llvm")]
-pub fn phase_5_run_llvm_passes(sess: &Session,
+pub fn phase_5_run_llvm_passes<Trans: TransCrate>(sess: &Session,
                                dep_graph: &DepGraph,
-                               trans: write::OngoingCrateTranslation)
-                               -> (CompileResult, trans::CrateTranslation) {
-    let trans = trans.join(sess, dep_graph);
+                               trans: <Trans as TransCrate>::OngoingCrateTranslation)
+                               -> (CompileResult, <Trans as TransCrate>::TranslatedCrate) {
+    let trans = Trans::join_trans(trans, sess, dep_graph);
 
     if sess.opts.debugging_opts.incremental_info {
-        write::dump_incremental_data(&trans);
+        Trans::dump_incremental_data(&trans);
     }
 
     time(sess.time_passes(),
@@ -1167,20 +1162,6 @@ pub fn phase_5_run_llvm_passes(sess: &Session,
     (sess.compile_status(), trans)
 }
 
-/// Run the linker on any artifacts that resulted from the LLVM run.
-/// This should produce either a finished executable or library.
-#[cfg(feature="llvm")]
-pub fn phase_6_link_output(sess: &Session,
-                           trans: &trans::CrateTranslation,
-                           outputs: &OutputFilenames) {
-    time(sess.time_passes(), "linking", || {
-        ::rustc_trans::back::link::link_binary(sess,
-                                               trans,
-                                               outputs,
-                                               &trans.crate_name.as_str())
-    });
-}
-
 fn escape_dep_filename(filename: &str) -> String {
     // Apparently clang and gcc *only* escape spaces:
     // http://llvm.org/klaus/clang/commit/9d50634cfc268ecc9a7250226dd5ca0e945240d4
diff --git a/src/librustc_driver/lib.rs b/src/librustc_driver/lib.rs
index 044f4a5eaf5..6bdad0b212c 100644
--- a/src/librustc_driver/lib.rs
+++ b/src/librustc_driver/lib.rs
@@ -75,6 +75,7 @@ use rustc::middle::cstore::CrateStore;
 use rustc_metadata::locator;
 use rustc_metadata::cstore::CStore;
 use rustc::util::common::{time, ErrorReported};
+use rustc_trans_utils::trans_crate::TransCrate;
 
 use serialize::json::ToJson;
 
@@ -151,101 +152,31 @@ pub fn run<F>(run_compiler: F) -> isize
 }
 
 #[cfg(not(feature="llvm"))]
-pub use no_llvm_metadata_loader::NoLLvmMetadataLoader as MetadataLoader;
+pub use rustc_trans_utils::trans_crate::MetadataOnlyTransCrate as DefaultTransCrate;
 #[cfg(feature="llvm")]
-pub use rustc_trans::LlvmMetadataLoader as MetadataLoader;
-
-#[cfg(not(feature="llvm"))]
-mod no_llvm_metadata_loader {
-    extern crate ar;
-    extern crate owning_ref;
-
-    use rustc::middle::cstore::MetadataLoader as MetadataLoaderTrait;
-    use rustc_back::target::Target;
-    use std::io;
-    use std::fs::File;
-    use std::path::Path;
-
-    use self::ar::Archive;
-    use self::owning_ref::{OwningRef, ErasedBoxRef};
-
-    pub struct NoLLvmMetadataLoader;
-
-    impl MetadataLoaderTrait for NoLLvmMetadataLoader {
-        fn get_rlib_metadata(
-            &self,
-            _: &Target,
-            filename: &Path
-        ) -> Result<ErasedBoxRef<[u8]>, String> {
-            let file = File::open(filename).map_err(|e| {
-                format!("metadata file open err: {:?}", e)
-            })?;
-            let mut archive = Archive::new(file);
-
-            while let Some(entry_result) = archive.next_entry() {
-                let mut entry = entry_result.map_err(|e| {
-                    format!("metadata section read err: {:?}", e)
-                })?;
-                if entry.header().identifier() == "rust.metadata.bin" {
-                    let mut buf = Vec::new();
-                    io::copy(&mut entry, &mut buf).unwrap();
-                    let buf: OwningRef<Vec<u8>, [u8]> = OwningRef::new(buf).into();
-                    return Ok(buf.map_owner_box().erase_owner());
-                }
-            }
-
-            Err("Couldnt find metadata section".to_string())
-        }
-
-        fn get_dylib_metadata(&self,
-                            _target: &Target,
-                            _filename: &Path)
-                            -> Result<ErasedBoxRef<[u8]>, String> {
-            panic!("Dylib metadata loading not supported without LLVM")
-        }
-    }
-}
+pub use rustc_trans::LlvmTransCrate as DefaultTransCrate;
 
 #[cfg(not(feature="llvm"))]
 mod rustc_trans {
     use syntax_pos::symbol::Symbol;
     use rustc::session::Session;
-    use rustc::session::config::{PrintRequest, OutputFilenames};
-    use rustc::ty::{TyCtxt, CrateAnalysis};
-    use rustc::ty::maps::Providers;
-    use rustc_incremental::IncrementalHashesMap;
-
-    use self::back::write::OngoingCrateTranslation;
+    use rustc::session::config::PrintRequest;
+    pub use rustc_trans_utils::trans_crate::MetadataOnlyTransCrate as LlvmTransCrate;
+    pub use rustc_trans_utils::trans_crate::TranslatedCrate as CrateTranslation;
 
     pub fn init(_sess: &Session) {}
     pub fn enable_llvm_debug() {}
-    pub fn provide(_providers: &mut Providers) {}
     pub fn print_version() {}
     pub fn print_passes() {}
     pub fn print(_req: PrintRequest, _sess: &Session) {}
     pub fn target_features(_sess: &Session) -> Vec<Symbol> { vec![] }
 
-    pub fn trans_crate<'a, 'tcx>(
-        _tcx: TyCtxt<'a, 'tcx, 'tcx>,
-        _analysis: CrateAnalysis,
-        _incr_hashes_map: IncrementalHashesMap,
-        _output_filenames: &OutputFilenames
-    ) -> OngoingCrateTranslation {
-        OngoingCrateTranslation(())
-    }
-
-    pub struct CrateTranslation(());
-
     pub mod back {
         pub mod write {
-            pub struct OngoingCrateTranslation(pub (in ::rustc_trans) ());
-
             pub const RELOC_MODEL_ARGS: [(&'static str, ()); 0] = [];
             pub const CODE_GEN_MODEL_ARGS: [(&'static str, ()); 0] = [];
         }
     }
-
-    __build_diagnostic_array! { librustc_trans, DIAGNOSTICS }
 }
 
 // Parse args and run the compiler. This is the primary entry point for rustc.
@@ -293,7 +224,7 @@ pub fn run_compiler<'a>(args: &[String],
         },
     };
 
-    let cstore = Rc::new(CStore::new(box ::MetadataLoader));
+    let cstore = Rc::new(CStore::new(DefaultTransCrate::metadata_loader()));
 
     let loader = file_loader.unwrap_or(box RealFileLoader);
     let codemap = Rc::new(CodeMap::with_file_loader(loader, sopts.file_path_mapping()));
@@ -1331,6 +1262,7 @@ pub fn diagnostics_registry() -> errors::registry::Registry {
     all_errors.extend_from_slice(&rustc_borrowck::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);
     all_errors.extend_from_slice(&rustc_const_eval::DIAGNOSTICS);
     all_errors.extend_from_slice(&rustc_metadata::DIAGNOSTICS);
diff --git a/src/librustc_driver/test.rs b/src/librustc_driver/test.rs
index fad24e6a0f2..6de36820f0c 100644
--- a/src/librustc_driver/test.rs
+++ b/src/librustc_driver/test.rs
@@ -30,6 +30,7 @@ use rustc::hir::map as hir_map;
 use rustc::mir::transform::Passes;
 use rustc::session::{self, config};
 use rustc::session::config::{OutputFilenames, OutputTypes};
+use rustc_trans_utils::trans_crate::TransCrate;
 use std::rc::Rc;
 use syntax::ast;
 use syntax::abi::Abi;
@@ -105,7 +106,7 @@ fn test_env<F>(source_string: &str,
     options.unstable_features = UnstableFeatures::Allow;
     let diagnostic_handler = errors::Handler::with_emitter(true, false, emitter);
 
-    let cstore = Rc::new(CStore::new(box ::MetadataLoader));
+    let cstore = Rc::new(CStore::new(::DefaultTransCrate::metadata_loader()));
     let sess = session::build_session_(options,
                                        None,
                                        diagnostic_handler,
diff --git a/src/librustc_trans/back/link.rs b/src/librustc_trans/back/link.rs
index 9d13d4ce15b..1630e775991 100644
--- a/src/librustc_trans/back/link.rs
+++ b/src/librustc_trans/back/link.rs
@@ -8,8 +8,6 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-extern crate rustc_trans_utils;
-
 use super::archive::{ArchiveBuilder, ArchiveConfig};
 use super::linker::Linker;
 use super::command::Command;
@@ -20,14 +18,12 @@ use rustc::session::config::{self, NoDebugInfo, OutputFilenames, OutputType, Pri
 use rustc::session::filesearch;
 use rustc::session::search_paths::PathKind;
 use rustc::session::Session;
-use rustc::ich::Fingerprint;
-use rustc::middle::cstore::{LinkMeta, NativeLibrary, LibSource, NativeLibraryKind};
+use rustc::middle::cstore::{NativeLibrary, LibSource, NativeLibraryKind};
 use rustc::middle::dependency_format::Linkage;
 use {CrateTranslation, CrateInfo};
 use rustc::util::common::time;
 use rustc::util::fs::fix_windows_verbatim_for_gcc;
 use rustc::hir::def_id::CrateNum;
-use rustc::hir::svh::Svh;
 use rustc_back::tempdir::TempDir;
 use rustc_back::{PanicStrategy, RelroLevel};
 use context::get_reloc_model;
@@ -88,16 +84,9 @@ pub const RLIB_BYTECODE_OBJECT_V1_DATASIZE_OFFSET: usize =
 pub const RLIB_BYTECODE_OBJECT_V1_DATA_OFFSET: usize =
     RLIB_BYTECODE_OBJECT_V1_DATASIZE_OFFSET + 8;
 
-pub use self::rustc_trans_utils::link::{find_crate_name, filename_for_input,
-                                        default_output_for_target, invalid_output_for_target};
-
-pub fn build_link_meta(crate_hash: Fingerprint) -> LinkMeta {
-    let r = LinkMeta {
-        crate_hash: Svh::new(crate_hash.to_smaller_hash()),
-    };
-    info!("{:?}", r);
-    return r;
-}
+pub use rustc_trans_utils::link::{find_crate_name, filename_for_input, default_output_for_target,
+                                  invalid_output_for_target, build_link_meta, out_filename,
+                                  check_file_is_writeable};
 
 // The third parameter is for env vars, used on windows to set up the
 // path for MSVC to find its DLLs, and gcc to find its bundled
@@ -225,13 +214,6 @@ pub fn link_binary(sess: &Session,
     out_filenames
 }
 
-fn is_writeable(p: &Path) -> bool {
-    match p.metadata() {
-        Err(..) => true,
-        Ok(m) => !m.permissions().readonly()
-    }
-}
-
 fn filename_for_metadata(sess: &Session, crate_name: &str, outputs: &OutputFilenames) -> PathBuf {
     let out_filename = outputs.single_output_file.clone()
         .unwrap_or(outputs
@@ -295,32 +277,6 @@ pub fn ignored_for_lto(info: &CrateInfo, cnum: CrateNum) -> bool {
     info.is_no_builtins.contains(&cnum) || info.compiler_builtins == Some(cnum)
 }
 
-fn out_filename(sess: &Session,
-                crate_type: config::CrateType,
-                outputs: &OutputFilenames,
-                crate_name: &str)
-                -> PathBuf {
-    let default_filename = filename_for_input(sess, crate_type, crate_name, outputs);
-    let out_filename = outputs.outputs.get(&OutputType::Exe)
-                              .and_then(|s| s.to_owned())
-                              .or_else(|| outputs.single_output_file.clone())
-                              .unwrap_or(default_filename);
-
-    check_file_is_writeable(&out_filename, sess);
-
-    out_filename
-}
-
-// Make sure files are writeable.  Mac, FreeBSD, and Windows system linkers
-// check this already -- however, the Linux linker will happily overwrite a
-// read-only file.  We should be consistent.
-fn check_file_is_writeable(file: &Path, sess: &Session) {
-    if !is_writeable(file) {
-        sess.fatal(&format!("output file {} is not writeable -- check its \
-                            permissions", file.display()));
-    }
-}
-
 fn link_binary_output(sess: &Session,
                       trans: &CrateTranslation,
                       crate_type: config::CrateType,
diff --git a/src/librustc_trans/base.rs b/src/librustc_trans/base.rs
index 74d610d1d12..2d01d2947d6 100644
--- a/src/librustc_trans/base.rs
+++ b/src/librustc_trans/base.rs
@@ -43,7 +43,6 @@ use rustc::ty::{self, Ty, TyCtxt};
 use rustc::ty::maps::Providers;
 use rustc::dep_graph::{DepNode, DepKind};
 use rustc::middle::cstore::{self, LinkMeta, LinkagePreference};
-use rustc::hir::map as hir_map;
 use rustc::util::common::{time, print_time_passes_entry};
 use rustc::session::config::{self, NoDebugInfo};
 use rustc::session::Session;
@@ -95,6 +94,8 @@ use syntax::ast;
 
 use mir::lvalue::Alignment;
 
+pub use rustc_trans_utils::{find_exported_symbols, check_for_rustc_errors_attr};
+
 pub struct StatRecorder<'a, 'tcx: 'a> {
     ccx: &'a CrateContext<'a, 'tcx>,
     name: Option<String>,
@@ -660,20 +661,6 @@ pub fn set_link_section(ccx: &CrateContext,
     }
 }
 
-// check for the #[rustc_error] annotation, which forces an
-// error in trans. This is used to write compile-fail tests
-// that actually test that compilation succeeds without
-// reporting an error.
-fn check_for_rustc_errors_attr(tcx: TyCtxt) {
-    if let Some((id, span)) = *tcx.sess.entry_fn.borrow() {
-        let main_def_id = tcx.hir.local_def_id(id);
-
-        if tcx.has_attr(main_def_id, "rustc_error") {
-            tcx.sess.span_fatal(span, "compilation successful");
-        }
-    }
-}
-
 /// Create the `main` function which will initialize the rust runtime and call
 /// users main function.
 fn maybe_create_entry_wrapper(ccx: &CrateContext) {
@@ -885,59 +872,10 @@ fn iter_globals(llmod: llvm::ModuleRef) -> ValueIter {
     }
 }
 
-/// The context provided lists a set of reachable ids as calculated by
-/// middle::reachable, but this contains far more ids and symbols than we're
-/// actually exposing from the object file. This function will filter the set in
-/// the context to the set of ids which correspond to symbols that are exposed
-/// from the object file being generated.
-///
-/// This list is later used by linkers to determine the set of symbols needed to
-/// be exposed from a dynamic library and it's also encoded into the metadata.
-pub fn find_exported_symbols(tcx: TyCtxt) -> NodeSet {
-    tcx.reachable_set(LOCAL_CRATE).0.iter().cloned().filter(|&id| {
-        // Next, we want to ignore some FFI functions that are not exposed from
-        // this crate. Reachable FFI functions can be lumped into two
-        // categories:
-        //
-        // 1. Those that are included statically via a static library
-        // 2. Those included otherwise (e.g. dynamically or via a framework)
-        //
-        // Although our LLVM module is not literally emitting code for the
-        // statically included symbols, it's an export of our library which
-        // needs to be passed on to the linker and encoded in the metadata.
-        //
-        // As a result, if this id is an FFI item (foreign item) then we only
-        // let it through if it's included statically.
-        match tcx.hir.get(id) {
-            hir_map::NodeForeignItem(..) => {
-                let def_id = tcx.hir.local_def_id(id);
-                tcx.is_statically_included_foreign_item(def_id)
-            }
-
-            // Only consider nodes that actually have exported symbols.
-            hir_map::NodeItem(&hir::Item {
-                node: hir::ItemStatic(..), .. }) |
-            hir_map::NodeItem(&hir::Item {
-                node: hir::ItemFn(..), .. }) |
-            hir_map::NodeImplItem(&hir::ImplItem {
-                node: hir::ImplItemKind::Method(..), .. }) => {
-                let def_id = tcx.hir.local_def_id(id);
-                let generics = tcx.generics_of(def_id);
-                let attributes = tcx.get_attrs(def_id);
-                (generics.parent_types == 0 && generics.types.is_empty()) &&
-                // Functions marked with #[inline] are only ever translated
-                // with "internal" linkage and are never exported.
-                !attr::requests_inline(&attributes)
-            }
-
-            _ => false
-        }
-    }).collect()
-}
-
 pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                              rx: mpsc::Receiver<Box<Any + Send>>)
                              -> OngoingCrateTranslation {
+
     check_for_rustc_errors_attr(tcx);
 
 
diff --git a/src/librustc_trans/lib.rs b/src/librustc_trans/lib.rs
index 57e9f1d091b..8a2c478cea0 100644
--- a/src/librustc_trans/lib.rs
+++ b/src/librustc_trans/lib.rs
@@ -50,6 +50,7 @@ extern crate rustc_incremental;
 extern crate rustc_llvm as llvm;
 extern crate rustc_platform_intrinsics as intrinsics;
 extern crate rustc_const_math;
+extern crate rustc_trans_utils;
 extern crate rustc_demangle;
 extern crate jobserver;
 extern crate num_cpus;
@@ -137,6 +138,63 @@ mod type_;
 mod type_of;
 mod value;
 
+use std::sync::mpsc;
+use std::any::Any;
+use rustc::ty::{self, TyCtxt};
+use rustc::session::Session;
+use rustc::session::config::OutputFilenames;
+use rustc::middle::cstore::MetadataLoader;
+use rustc::dep_graph::DepGraph;
+
+pub struct LlvmTransCrate(());
+
+impl LlvmTransCrate {
+    pub fn new() -> Self {
+        LlvmTransCrate(())
+    }
+}
+
+impl rustc_trans_utils::trans_crate::TransCrate for LlvmTransCrate {
+    type MetadataLoader = metadata::LlvmMetadataLoader;
+    type OngoingCrateTranslation = back::write::OngoingCrateTranslation;
+    type TranslatedCrate = CrateTranslation;
+
+    fn metadata_loader() -> Box<MetadataLoader> {
+        box metadata::LlvmMetadataLoader
+    }
+
+    fn provide_local(providers: &mut ty::maps::Providers) {
+        provide_local(providers);
+    }
+
+    fn provide_extern(providers: &mut ty::maps::Providers) {
+        provide_extern(providers);
+    }
+
+    fn trans_crate<'a, 'tcx>(
+        tcx: TyCtxt<'a, 'tcx, 'tcx>,
+        rx: mpsc::Receiver<Box<Any + Send>>
+    ) -> Self::OngoingCrateTranslation {
+        base::trans_crate(tcx, rx)
+    }
+
+    fn join_trans(
+        trans: Self::OngoingCrateTranslation,
+        sess: &Session,
+        dep_graph: &DepGraph
+    ) -> Self::TranslatedCrate {
+        trans.join(sess, dep_graph)
+    }
+
+    fn link_binary(sess: &Session, trans: &Self::TranslatedCrate, outputs: &OutputFilenames) {
+        back::link::link_binary(sess, trans, outputs, &trans.crate_name.as_str());
+    }
+
+    fn dump_incremental_data(trans: &Self::TranslatedCrate) {
+        back::write::dump_incremental_data(trans);
+    }
+}
+
 pub struct ModuleTranslation {
     /// The name of the module. When the crate may be saved between
     /// compilations, incremental compilation requires that name be
diff --git a/src/librustc_trans_utils/Cargo.toml b/src/librustc_trans_utils/Cargo.toml
index f026d4fcbc2..bedbea00688 100644
--- a/src/librustc_trans_utils/Cargo.toml
+++ b/src/librustc_trans_utils/Cargo.toml
@@ -10,6 +10,12 @@ crate-type = ["dylib"]
 test = false
 
 [dependencies]
-rustc = { path = "../librustc" }
+ar = "0.3.0"
+flate2 = "0.2"
+owning_ref = "0.3.3"
+log = "0.3"
+
 syntax = { path = "../libsyntax" }
 syntax_pos = { path = "../libsyntax_pos" }
+rustc = { path = "../librustc" }
+rustc_back = { path = "../librustc_back" }
diff --git a/src/librustc_trans_utils/lib.rs b/src/librustc_trans_utils/lib.rs
index 5e8abe59ad9..6873befd2bf 100644
--- a/src/librustc_trans_utils/lib.rs
+++ b/src/librustc_trans_utils/lib.rs
@@ -29,8 +29,89 @@
 
 #![cfg_attr(stage0, feature(const_fn))]
 
+extern crate ar;
+extern crate flate2;
+extern crate owning_ref;
+#[macro_use]
+extern crate log;
+
+#[macro_use]
 extern crate rustc;
+extern crate rustc_back;
 extern crate syntax;
 extern crate syntax_pos;
 
+use rustc::ty::TyCtxt;
+use rustc::hir;
+use rustc::hir::def_id::LOCAL_CRATE;
+use rustc::hir::map as hir_map;
+use rustc::util::nodemap::NodeSet;
+
+use syntax::attr;
+
 pub mod link;
+pub mod trans_crate;
+
+/// check for the #[rustc_error] annotation, which forces an
+/// error in trans. This is used to write compile-fail tests
+/// that actually test that compilation succeeds without
+/// reporting an error.
+pub fn check_for_rustc_errors_attr(tcx: TyCtxt) {
+    if let Some((id, span)) = *tcx.sess.entry_fn.borrow() {
+        let main_def_id = tcx.hir.local_def_id(id);
+
+        if tcx.has_attr(main_def_id, "rustc_error") {
+            tcx.sess.span_fatal(span, "compilation successful");
+        }
+    }
+}
+
+/// The context provided lists a set of reachable ids as calculated by
+/// middle::reachable, but this contains far more ids and symbols than we're
+/// actually exposing from the object file. This function will filter the set in
+/// the context to the set of ids which correspond to symbols that are exposed
+/// from the object file being generated.
+///
+/// This list is later used by linkers to determine the set of symbols needed to
+/// be exposed from a dynamic library and it's also encoded into the metadata.
+pub fn find_exported_symbols(tcx: TyCtxt) -> NodeSet {
+    tcx.reachable_set(LOCAL_CRATE).0.iter().cloned().filter(|&id| {
+        // Next, we want to ignore some FFI functions that are not exposed from
+        // this crate. Reachable FFI functions can be lumped into two
+        // categories:
+        //
+        // 1. Those that are included statically via a static library
+        // 2. Those included otherwise (e.g. dynamically or via a framework)
+        //
+        // Although our LLVM module is not literally emitting code for the
+        // statically included symbols, it's an export of our library which
+        // needs to be passed on to the linker and encoded in the metadata.
+        //
+        // As a result, if this id is an FFI item (foreign item) then we only
+        // let it through if it's included statically.
+        match tcx.hir.get(id) {
+            hir_map::NodeForeignItem(..) => {
+                let def_id = tcx.hir.local_def_id(id);
+                tcx.is_statically_included_foreign_item(def_id)
+            }
+
+            // Only consider nodes that actually have exported symbols.
+            hir_map::NodeItem(&hir::Item {
+                node: hir::ItemStatic(..), .. }) |
+            hir_map::NodeItem(&hir::Item {
+                node: hir::ItemFn(..), .. }) |
+            hir_map::NodeImplItem(&hir::ImplItem {
+                node: hir::ImplItemKind::Method(..), .. }) => {
+                let def_id = tcx.hir.local_def_id(id);
+                let generics = tcx.generics_of(def_id);
+                let attributes = tcx.get_attrs(def_id);
+                (generics.parent_types == 0 && generics.types.is_empty()) &&
+                // Functions marked with #[inline] are only ever translated
+                // with "internal" linkage and are never exported.
+                !attr::requests_inline(&attributes)
+            }
+
+            _ => false
+        }
+    }).collect()
+}
diff --git a/src/librustc_trans_utils/link.rs b/src/librustc_trans_utils/link.rs
index aa8637fabe8..47484488fb8 100644
--- a/src/librustc_trans_utils/link.rs
+++ b/src/librustc_trans_utils/link.rs
@@ -8,13 +8,56 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+use rustc::ich::Fingerprint;
 use rustc::session::config::{self, OutputFilenames, Input, OutputType};
 use rustc::session::Session;
-use rustc::middle::cstore;
-use std::path::PathBuf;
+use rustc::middle::cstore::{self, LinkMeta};
+use rustc::hir::svh::Svh;
+use std::path::{Path, PathBuf};
 use syntax::ast;
 use syntax_pos::Span;
 
+pub fn out_filename(sess: &Session,
+                crate_type: config::CrateType,
+                outputs: &OutputFilenames,
+                crate_name: &str)
+                -> PathBuf {
+    let default_filename = filename_for_input(sess, crate_type, crate_name, outputs);
+    let out_filename = outputs.outputs.get(&OutputType::Exe)
+                              .and_then(|s| s.to_owned())
+                              .or_else(|| outputs.single_output_file.clone())
+                              .unwrap_or(default_filename);
+
+    check_file_is_writeable(&out_filename, sess);
+
+    out_filename
+}
+
+// Make sure files are writeable.  Mac, FreeBSD, and Windows system linkers
+// check this already -- however, the Linux linker will happily overwrite a
+// read-only file.  We should be consistent.
+pub fn check_file_is_writeable(file: &Path, sess: &Session) {
+    if !is_writeable(file) {
+        sess.fatal(&format!("output file {} is not writeable -- check its \
+                            permissions", file.display()));
+    }
+}
+
+fn is_writeable(p: &Path) -> bool {
+    match p.metadata() {
+        Err(..) => true,
+        Ok(m) => !m.permissions().readonly()
+    }
+}
+
+pub fn build_link_meta(crate_hash: Fingerprint) -> LinkMeta {
+    let r = LinkMeta {
+        crate_hash: Svh::new(crate_hash.to_smaller_hash()),
+    };
+    info!("{:?}", r);
+    return r;
+}
+
 pub fn find_crate_name(sess: Option<&Session>,
                        attrs: &[ast::Attribute],
                        input: &Input) -> String {
diff --git a/src/librustc_trans_utils/trans_crate.rs b/src/librustc_trans_utils/trans_crate.rs
new file mode 100644
index 00000000000..f51a463fcc2
--- /dev/null
+++ b/src/librustc_trans_utils/trans_crate.rs
@@ -0,0 +1,249 @@
+// Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT
+// 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.
+
+//! The Rust compiler.
+//!
+//! # Note
+//!
+//! This API is completely unstable and subject to change.
+
+#![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
+      html_favicon_url = "https://doc.rust-lang.org/favicon.ico",
+      html_root_url = "https://doc.rust-lang.org/nightly/")]
+#![deny(warnings)]
+
+#![feature(box_syntax)]
+
+use std::any::Any;
+use std::io::prelude::*;
+use std::io::{self, Cursor};
+use std::fs::File;
+use std::path::Path;
+use std::sync::mpsc;
+
+use owning_ref::{ErasedBoxRef, OwningRef};
+use ar::{Archive, Builder, Header};
+use flate2::Compression;
+use flate2::write::DeflateEncoder;
+
+use syntax::symbol::Symbol;
+use rustc::hir::def_id::LOCAL_CRATE;
+use rustc::session::Session;
+use rustc::session::config::{CrateType, OutputFilenames};
+use rustc::ty::TyCtxt;
+use rustc::ty::maps::Providers;
+use rustc::middle::cstore::EncodedMetadata;
+use rustc::middle::cstore::MetadataLoader as MetadataLoaderTrait;
+use rustc::dep_graph::{DepGraph, DepNode, DepKind};
+use rustc_back::target::Target;
+use link::{build_link_meta, out_filename};
+
+pub trait TransCrate {
+    type MetadataLoader: MetadataLoaderTrait;
+    type OngoingCrateTranslation;
+    type TranslatedCrate;
+
+    fn metadata_loader() -> Box<MetadataLoaderTrait>;
+    fn provide_local(_providers: &mut Providers);
+    fn provide_extern(_providers: &mut Providers);
+    fn trans_crate<'a, 'tcx>(
+        tcx: TyCtxt<'a, 'tcx, 'tcx>,
+        rx: mpsc::Receiver<Box<Any + Send>>
+    ) -> Self::OngoingCrateTranslation;
+    fn join_trans(
+        trans: Self::OngoingCrateTranslation,
+        sess: &Session,
+        dep_graph: &DepGraph
+    ) -> Self::TranslatedCrate;
+    fn link_binary(sess: &Session, trans: &Self::TranslatedCrate, outputs: &OutputFilenames);
+    fn dump_incremental_data(trans: &Self::TranslatedCrate);
+}
+
+pub struct DummyTransCrate;
+
+impl TransCrate for DummyTransCrate {
+    type MetadataLoader = DummyMetadataLoader;
+    type OngoingCrateTranslation = ();
+    type TranslatedCrate = ();
+
+    fn metadata_loader() -> Box<MetadataLoaderTrait> {
+        box DummyMetadataLoader(())
+    }
+
+    fn provide_local(_providers: &mut Providers) {
+        bug!("DummyTransCrate::provide_local");
+    }
+
+    fn provide_extern(_providers: &mut Providers) {
+        bug!("DummyTransCrate::provide_extern");
+    }
+
+    fn trans_crate<'a, 'tcx>(
+        _tcx: TyCtxt<'a, 'tcx, 'tcx>,
+        _rx: mpsc::Receiver<Box<Any + Send>>
+    ) -> Self::OngoingCrateTranslation {
+        bug!("DummyTransCrate::trans_crate");
+    }
+
+    fn join_trans(
+        _trans: Self::OngoingCrateTranslation,
+        _sess: &Session,
+        _dep_graph: &DepGraph
+    ) -> Self::TranslatedCrate {
+        bug!("DummyTransCrate::join_trans");
+    }
+
+    fn link_binary(_sess: &Session, _trans: &Self::TranslatedCrate, _outputs: &OutputFilenames) {
+        bug!("DummyTransCrate::link_binary");
+    }
+
+    fn dump_incremental_data(_trans: &Self::TranslatedCrate) {
+        bug!("DummyTransCrate::dump_incremental_data");
+    }
+}
+
+pub struct DummyMetadataLoader(());
+
+impl MetadataLoaderTrait for DummyMetadataLoader {
+    fn get_rlib_metadata(
+        &self,
+        _target: &Target,
+        _filename: &Path
+    ) -> Result<ErasedBoxRef<[u8]>, String> {
+        bug!("DummyMetadataLoader::get_rlib_metadata");
+    }
+
+    fn get_dylib_metadata(
+        &self,
+        _target: &Target,
+        _filename: &Path
+    ) -> Result<ErasedBoxRef<[u8]>, String> {
+        bug!("DummyMetadataLoader::get_dylib_metadata");
+    }
+}
+
+pub struct NoLlvmMetadataLoader;
+
+impl MetadataLoaderTrait for NoLlvmMetadataLoader {
+    fn get_rlib_metadata(&self, _: &Target, filename: &Path) -> Result<ErasedBoxRef<[u8]>, String> {
+        let file = File::open(filename)
+            .map_err(|e| format!("metadata file open err: {:?}", e))?;
+        let mut archive = Archive::new(file);
+
+        while let Some(entry_result) = archive.next_entry() {
+            let mut entry = entry_result
+                .map_err(|e| format!("metadata section read err: {:?}", e))?;
+            if entry.header().identifier() == "rust.metadata.bin" {
+                let mut buf = Vec::new();
+                io::copy(&mut entry, &mut buf).unwrap();
+                let buf: OwningRef<Vec<u8>, [u8]> = OwningRef::new(buf).into();
+                return Ok(buf.map_owner_box().erase_owner());
+            }
+        }
+
+        Err("Couldnt find metadata section".to_string())
+    }
+
+    fn get_dylib_metadata(
+        &self,
+        _target: &Target,
+        _filename: &Path,
+    ) -> Result<ErasedBoxRef<[u8]>, String> {
+        // FIXME: Support reading dylibs from llvm enabled rustc
+        self.get_rlib_metadata(_target, _filename)
+    }
+}
+
+pub struct MetadataOnlyTransCrate;
+pub struct OngoingCrateTranslation {
+    metadata: EncodedMetadata,
+    metadata_version: Vec<u8>,
+    crate_name: Symbol,
+}
+pub struct TranslatedCrate(OngoingCrateTranslation);
+
+impl MetadataOnlyTransCrate {
+    #[allow(dead_code)]
+    pub fn new() -> Self {
+        MetadataOnlyTransCrate
+    }
+}
+
+impl TransCrate for MetadataOnlyTransCrate {
+    type MetadataLoader = NoLlvmMetadataLoader;
+    type OngoingCrateTranslation = OngoingCrateTranslation;
+    type TranslatedCrate = TranslatedCrate;
+
+    fn metadata_loader() -> Box<MetadataLoaderTrait> {
+        box NoLlvmMetadataLoader
+    }
+
+    fn provide_local(_providers: &mut Providers) {}
+    fn provide_extern(_providers: &mut Providers) {}
+
+    fn trans_crate<'a, 'tcx>(
+        tcx: TyCtxt<'a, 'tcx, 'tcx>,
+        _rx: mpsc::Receiver<Box<Any + Send>>
+    ) -> Self::OngoingCrateTranslation {
+        ::check_for_rustc_errors_attr(tcx);
+        let _ = tcx.link_args(LOCAL_CRATE);
+        let _ = tcx.native_libraries(LOCAL_CRATE);
+        tcx.sess.abort_if_errors();
+
+        let crate_hash = tcx.dep_graph
+                        .fingerprint_of(&DepNode::new_no_params(DepKind::Krate));
+        let link_meta = build_link_meta(crate_hash);
+        let exported_symbols = ::find_exported_symbols(tcx);
+        let (metadata, _hashes) = tcx.encode_metadata(&link_meta, &exported_symbols);
+
+        OngoingCrateTranslation {
+            metadata: metadata,
+            metadata_version: tcx.metadata_encoding_version().to_vec(),
+            crate_name: tcx.crate_name(LOCAL_CRATE),
+        }
+    }
+
+    fn join_trans(
+        trans: Self::OngoingCrateTranslation,
+        _sess: &Session,
+        _dep_graph: &DepGraph,
+    ) -> Self::TranslatedCrate {
+        TranslatedCrate(trans)
+    }
+
+    fn link_binary(sess: &Session, trans: &Self::TranslatedCrate, outputs: &OutputFilenames) {
+        for &crate_type in sess.opts.crate_types.iter() {
+            if crate_type != CrateType::CrateTypeRlib && crate_type != CrateType::CrateTypeDylib {
+                continue;
+            }
+            let output_name =
+                out_filename(sess, crate_type, &outputs, &trans.0.crate_name.as_str());
+            let mut compressed = trans.0.metadata_version.clone();
+            let metadata = if crate_type == CrateType::CrateTypeDylib {
+                DeflateEncoder::new(&mut compressed, Compression::Fast)
+                    .write_all(&trans.0.metadata.raw_data)
+                    .unwrap();
+                &compressed
+            } else {
+                &trans.0.metadata.raw_data
+            };
+            let mut builder = Builder::new(File::create(&output_name).unwrap());
+            let header = Header::new("rust.metadata.bin".to_string(), metadata.len() as u64);
+            builder.append(&header, Cursor::new(metadata)).unwrap();
+        }
+
+        if !sess.opts.crate_types.contains(&CrateType::CrateTypeRlib)
+            && !sess.opts.crate_types.contains(&CrateType::CrateTypeDylib) {
+            sess.fatal("Executables are not supported by the metadata-only backend.");
+        }
+    }
+
+    fn dump_incremental_data(_trans: &Self::TranslatedCrate) {}
+}