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
This commit is contained in:
bors 2017-09-25 18:05:22 +00:00
commit 3df1f7b82d
13 changed files with 526 additions and 270 deletions

3
.gitignore vendored
View File

@ -103,3 +103,6 @@ version.texi
.cargo
!src/vendor/**
/src/target/
no_llvm_build

5
src/Cargo.lock generated
View File

@ -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",
]

View File

@ -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 {

View File

@ -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();
}
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?;
// 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)
});
// 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")]
{
if sess.opts.debugging_opts.print_type_sizes {
sess.code_stats.borrow().print_type_sizes();
}
rustc_incremental::finalize_session_directory(sess, trans.link.crate_hash);
let (phase5_result, trans) = phase_5_run_llvm_passes(sess, &dep_graph, trans);
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);
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(())
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

View File

@ -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);

View File

@ -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,

View File

@ -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,

View File

@ -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);

View File

@ -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

View File

@ -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" }

View File

@ -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()
}

View File

@ -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 {

View File

@ -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) {}
}