//!
//! This API is completely unstable and subject to change.
-#![crate_name = "rustc_driver"]
-#![unstable(feature = "rustc_private", issue = "27812")]
-#![crate_type = "dylib"]
-#![crate_type = "rlib"]
#![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)]
-#![feature(libc)]
+#![cfg_attr(unix, feature(libc))]
#![feature(quote)]
#![feature(rustc_diagnostic_macros)]
-#![feature(rustc_private)]
#![feature(set_stdio)]
-#![feature(staged_api)]
extern crate arena;
-extern crate flate;
extern crate getopts;
extern crate graphviz;
+extern crate env_logger;
+#[cfg(unix)]
extern crate libc;
extern crate rustc;
+extern crate rustc_allocator;
extern crate rustc_back;
extern crate rustc_borrowck;
+extern crate rustc_const_eval;
+extern crate rustc_data_structures;
+extern crate rustc_errors as errors;
extern crate rustc_passes;
-extern crate rustc_front;
extern crate rustc_lint;
extern crate rustc_plugin;
extern crate rustc_privacy;
+extern crate rustc_incremental;
extern crate rustc_metadata;
extern crate rustc_mir;
extern crate rustc_resolve;
+extern crate rustc_save_analysis;
+#[cfg(feature="llvm")]
extern crate rustc_trans;
+extern crate rustc_trans_utils;
extern crate rustc_typeck;
extern crate serialize;
-extern crate rustc_llvm as llvm;
#[macro_use]
extern crate log;
-#[macro_use]
extern crate syntax;
extern crate syntax_ext;
+extern crate syntax_pos;
use driver::CompileController;
use pretty::{PpMode, UserIdentifiedItem};
use rustc_resolve as resolve;
-use rustc_trans::back::link;
-use rustc_trans::save;
-use rustc::session::{config, Session, build_session};
+use rustc_save_analysis as save;
+use rustc_save_analysis::DumpHandler;
+use rustc::session::{self, config, Session, build_session, CompileResult};
+use rustc::session::CompileIncomplete;
use rustc::session::config::{Input, PrintRequest, OutputType, ErrorOutputType};
-use rustc::middle::cstore::CrateStore;
+use rustc::session::config::nightly_options;
+use rustc::session::{early_error, early_warn};
use rustc::lint::Lint;
use rustc::lint;
-use rustc_metadata::loader;
+use rustc::middle::cstore::CrateStore;
+use rustc_metadata::locator;
use rustc_metadata::cstore::CStore;
-use rustc::util::common::time;
+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::default::Default;
use std::env;
+use std::ffi::OsString;
use std::io::{self, Read, Write};
use std::iter::repeat;
use std::path::PathBuf;
-use std::process;
+use std::process::{self, Command, Stdio};
use std::rc::Rc;
use std::str;
use std::sync::{Arc, Mutex};
use std::thread;
-use rustc::session::early_error;
-
use syntax::ast;
-use syntax::parse;
-use syntax::errors;
-use syntax::errors::emitter::Emitter;
-use syntax::diagnostics;
-use syntax::parse::token;
+use syntax::codemap::{CodeMap, FileLoader, RealFileLoader};
+use syntax::feature_gate::{GatedCfg, UnstableFeatures};
+use syntax::parse::{self, PResult};
+use syntax_pos::{DUMMY_SP, MultiSpan};
#[cfg(test)]
-pub mod test;
+mod test;
+pub mod profile;
pub mod driver;
pub mod pretty;
pub mod target_features;
-
+mod derive_registrar;
const BUG_REPORT_URL: &'static str = "https://github.com/rust-lang/rust/blob/master/CONTRIBUTING.\
md#bug-reports";
-pub fn run(args: Vec<String>) -> isize {
- monitor(move || run_compiler(&args, &mut RustcDefaultCalls));
+pub fn abort_on_err<T>(result: Result<T, CompileIncomplete>, sess: &Session) -> T {
+ match result {
+ Err(CompileIncomplete::Errored(ErrorReported)) => {
+ sess.abort_if_errors();
+ panic!("error reported but abort_if_errors didn't abort???");
+ }
+ Err(CompileIncomplete::Stopped) => {
+ sess.fatal("compilation terminated");
+ }
+ Ok(x) => x,
+ }
+}
+
+pub fn run<F>(run_compiler: F) -> isize
+ where F: FnOnce() -> (CompileResult, Option<Session>) + Send + 'static
+{
+ monitor(move || {
+ let (result, session) = run_compiler();
+ if let Err(CompileIncomplete::Errored(_)) = result {
+ match session {
+ Some(sess) => {
+ sess.abort_if_errors();
+ panic!("error reported but abort_if_errors didn't abort???");
+ }
+ None => {
+ let emitter =
+ errors::emitter::EmitterWriter::stderr(errors::ColorConfig::Auto,
+ None,
+ true);
+ let handler = errors::Handler::with_emitter(true, false, Box::new(emitter));
+ handler.emit(&MultiSpan::new(),
+ "aborting due to previous error(s)",
+ errors::Level::Fatal);
+ exit_on_err();
+ }
+ }
+ }
+ });
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;
+
+#[cfg(not(feature="llvm"))]
+mod rustc_trans {
+ use syntax_pos::symbol::Symbol;
+ use rustc::session::Session;
+ 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 print_version() {}
+ pub fn print_passes() {}
+ pub fn print(_req: PrintRequest, _sess: &Session) {}
+ pub fn target_features(_sess: &Session) -> Vec<Symbol> { vec![] }
+
+ pub mod back {
+ pub mod write {
+ pub const RELOC_MODEL_ARGS: [(&'static str, ()); 0] = [];
+ pub const CODE_GEN_MODEL_ARGS: [(&'static str, ()); 0] = [];
+ pub const TLS_MODEL_ARGS: [(&'static str, ()); 0] = [];
+ }
+ }
+}
+
// Parse args and run the compiler. This is the primary entry point for rustc.
// See comments on CompilerCalls below for details about the callbacks argument.
-pub fn run_compiler<'a>(args: &[String], callbacks: &mut CompilerCalls<'a>) {
- macro_rules! do_or_return {($expr: expr) => {
+// The FileLoader provides a way to load files from sources other than the file system.
+pub fn run_compiler<'a>(args: &[String],
+ callbacks: &mut CompilerCalls<'a>,
+ file_loader: Option<Box<FileLoader + 'static>>,
+ emitter_dest: Option<Box<Write + Send>>)
+ -> (CompileResult, Option<Session>)
+{
+ macro_rules! do_or_return {($expr: expr, $sess: expr) => {
match $expr {
- Compilation::Stop => return,
+ Compilation::Stop => return (Ok(()), $sess),
Compilation::Continue => {}
}
}}
- let matches = match handle_options(args.to_vec()) {
+ let matches = match handle_options(args) {
Some(matches) => matches,
- None => return,
+ None => return (Ok(()), None),
};
- let sopts = config::build_session_options(&matches);
+ let (sopts, cfg) = config::build_session_options_and_crate_config(&matches);
+
+ if sopts.debugging_opts.debug_llvm {
+ rustc_trans::enable_llvm_debug();
+ }
let descriptions = diagnostics_registry();
- do_or_return!(callbacks.early_callback(&matches, &descriptions, sopts.error_format));
+ do_or_return!(callbacks.early_callback(&matches,
+ &sopts,
+ &cfg,
+ &descriptions,
+ sopts.error_format),
+ None);
let (odir, ofile) = make_output(&matches);
let (input, input_file_path) = match make_input(&matches.free) {
Some((input, input_file_path)) => callbacks.some_input(input, input_file_path),
- None => match callbacks.no_input(&matches, &sopts, &odir, &ofile, &descriptions) {
+ None => match callbacks.no_input(&matches, &sopts, &cfg, &odir, &ofile, &descriptions) {
Some((input, input_file_path)) => (input, input_file_path),
- None => return,
+ None => return (Ok(()), None),
},
};
- let cstore = Rc::new(CStore::new(token::get_ident_interner()));
- let sess = build_session(sopts, input_file_path, descriptions,
- cstore.clone());
+ 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()));
+ let mut sess = session::build_session_with_codemap(
+ sopts, input_file_path, descriptions, codemap, emitter_dest,
+ );
+ rustc_trans::init(&sess);
rustc_lint::register_builtins(&mut sess.lint_store.borrow_mut(), Some(&sess));
- let mut cfg = config::build_configuration(&sess);
+
+ let mut cfg = config::build_configuration(&sess, cfg);
target_features::add_configuration(&mut cfg, &sess);
+ sess.parse_sess.config = cfg;
- do_or_return!(callbacks.late_callback(&matches, &sess, &input, &odir, &ofile));
-
- // It is somewhat unfortunate that this is hardwired in - this is forced by
- // the fact that pretty_print_input requires the session by value.
- let pretty = callbacks.parse_pretty(&sess, &matches);
- match pretty {
- Some((ppm, opt_uii)) => {
- pretty::pretty_print_input(sess, &cstore, cfg, &input, ppm, opt_uii, ofile);
- return;
- }
- None => {
- // continue
- }
- }
+ do_or_return!(callbacks.late_callback(&matches,
+ &sess,
+ &*cstore,
+ &input,
+ &odir,
+ &ofile), Some(sess));
let plugins = sess.opts.debugging_opts.extra_plugins.clone();
- let control = callbacks.build_controller(&sess);
- driver::compile_input(sess, &cstore, cfg, &input, &odir, &ofile,
- Some(plugins), control);
+ let control = callbacks.build_controller(&sess, &matches);
+ (driver::compile_input(&sess,
+ &cstore,
+ &input,
+ &odir,
+ &ofile,
+ Some(plugins),
+ &control),
+ Some(sess))
}
// Extract output directory and file from matches.
// Extract input (string or file and optional path) from matches.
fn make_input(free_matches: &[String]) -> Option<(Input, Option<PathBuf>)> {
if free_matches.len() == 1 {
- let ifile = &free_matches[0][..];
+ let ifile = &free_matches[0];
if ifile == "-" {
let mut src = String::new();
io::stdin().read_to_string(&mut src).unwrap();
- Some((Input::Str(src), None))
+ Some((Input::Str { name: driver::anon_src(), input: src },
+ None))
} else {
Some((Input::File(PathBuf::from(ifile)),
Some(PathBuf::from(ifile))))
}
}
+fn parse_pretty(sess: &Session,
+ matches: &getopts::Matches)
+ -> Option<(PpMode, Option<UserIdentifiedItem>)> {
+ let pretty = if sess.opts.debugging_opts.unstable_options {
+ matches.opt_default("pretty", "normal").map(|a| {
+ // stable pretty-print variants only
+ pretty::parse_pretty(sess, &a, false)
+ })
+ } else {
+ None
+ };
+ if pretty.is_none() && sess.unstable_options() {
+ matches.opt_str("unpretty").map(|a| {
+ // extended with unstable pretty-print variants
+ pretty::parse_pretty(sess, &a, true)
+ })
+ } else {
+ pretty
+ }
+}
+
// Whether to stop or continue compilation.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum Compilation {
// else (e.g., selecting input and output).
fn early_callback(&mut self,
_: &getopts::Matches,
- _: &diagnostics::registry::Registry,
+ _: &config::Options,
+ _: &ast::CrateConfig,
+ _: &errors::registry::Registry,
_: ErrorOutputType)
-> Compilation {
Compilation::Continue
fn late_callback(&mut self,
_: &getopts::Matches,
_: &Session,
+ _: &CrateStore,
_: &Input,
_: &Option<PathBuf>,
_: &Option<PathBuf>)
fn no_input(&mut self,
_: &getopts::Matches,
_: &config::Options,
+ _: &ast::CrateConfig,
_: &Option<PathBuf>,
_: &Option<PathBuf>,
- _: &diagnostics::registry::Registry)
+ _: &errors::registry::Registry)
-> Option<(Input, Option<PathBuf>)> {
None
}
- // Parse pretty printing information from the arguments. The implementer can
- // choose to ignore this (the default will return None) which will skip pretty
- // printing. If you do want to pretty print, it is recommended to use the
- // implementation of this method from RustcDefaultCalls.
- // FIXME, this is a terrible bit of API. Parsing of pretty printing stuff
- // should be done as part of the framework and the implementor should customise
- // handling of it. However, that is not possible atm because pretty printing
- // essentially goes off and takes another path through the compiler which
- // means the session is either moved or not depending on what parse_pretty
- // returns (we could fix this by cloning, but it's another hack). The proper
- // solution is to handle pretty printing as if it were a compiler extension,
- // extending CompileController to make this work (see for example the treatment
- // of save-analysis in RustcDefaultCalls::build_controller).
- fn parse_pretty(&mut self,
- _sess: &Session,
- _matches: &getopts::Matches)
- -> Option<(PpMode, Option<UserIdentifiedItem>)> {
- None
- }
-
// Create a CompilController struct for controlling the behaviour of
// compilation.
- fn build_controller(&mut self, &Session) -> CompileController<'a>;
+ fn build_controller(&mut self, _: &Session, _: &getopts::Matches) -> CompileController<'a>;
}
// CompilerCalls instance for a regular rustc build.
#[derive(Copy, Clone)]
pub struct RustcDefaultCalls;
+// FIXME remove these and use winapi 0.3 instead
+// Duplicates: bootstrap/compile.rs, librustc_errors/emitter.rs
+#[cfg(unix)]
+fn stdout_isatty() -> bool {
+ unsafe { libc::isatty(libc::STDOUT_FILENO) != 0 }
+}
+
+#[cfg(windows)]
+fn stdout_isatty() -> bool {
+ type DWORD = u32;
+ type BOOL = i32;
+ type HANDLE = *mut u8;
+ type LPDWORD = *mut u32;
+ const STD_OUTPUT_HANDLE: DWORD = -11i32 as DWORD;
+ extern "system" {
+ fn GetStdHandle(which: DWORD) -> HANDLE;
+ fn GetConsoleMode(hConsoleHandle: HANDLE, lpMode: LPDWORD) -> BOOL;
+ }
+ unsafe {
+ let handle = GetStdHandle(STD_OUTPUT_HANDLE);
+ let mut out = 0;
+ GetConsoleMode(handle, &mut out) != 0
+ }
+}
+
+fn handle_explain(code: &str,
+ descriptions: &errors::registry::Registry,
+ output: ErrorOutputType) {
+ let normalised = if code.starts_with("E") {
+ code.to_string()
+ } else {
+ format!("E{0:0>4}", code)
+ };
+ match descriptions.find_description(&normalised) {
+ Some(ref description) => {
+ let mut is_in_code_block = false;
+ let mut text = String::new();
+
+ // Slice off the leading newline and print.
+ for line in description[1..].lines() {
+ let indent_level = line.find(|c: char| !c.is_whitespace())
+ .unwrap_or_else(|| line.len());
+ let dedented_line = &line[indent_level..];
+ if dedented_line.starts_with("```") {
+ is_in_code_block = !is_in_code_block;
+ text.push_str(&line[..(indent_level+3)]);
+ } else if is_in_code_block && dedented_line.starts_with("# ") {
+ continue;
+ } else {
+ text.push_str(line);
+ }
+ text.push('\n');
+ }
+
+ if stdout_isatty() {
+ show_content_with_pager(&text);
+ } else {
+ print!("{}", text);
+ }
+ }
+ None => {
+ early_error(output, &format!("no extended information for {}", code));
+ }
+ }
+}
+
+fn show_content_with_pager(content: &String) {
+ let pager_name = env::var_os("PAGER").unwrap_or_else(|| if cfg!(windows) {
+ OsString::from("more.com")
+ } else {
+ OsString::from("less")
+ });
+
+ let mut fallback_to_println = false;
+
+ match Command::new(pager_name).stdin(Stdio::piped()).spawn() {
+ Ok(mut pager) => {
+ if let Some(pipe) = pager.stdin.as_mut() {
+ if pipe.write_all(content.as_bytes()).is_err() {
+ fallback_to_println = true;
+ }
+ }
+
+ if pager.wait().is_err() {
+ fallback_to_println = true;
+ }
+ }
+ Err(_) => {
+ fallback_to_println = true;
+ }
+ }
+
+ // If pager fails for whatever reason, we should still print the content
+ // to standard output
+ if fallback_to_println {
+ print!("{}", content);
+ }
+}
+
impl<'a> CompilerCalls<'a> for RustcDefaultCalls {
fn early_callback(&mut self,
matches: &getopts::Matches,
- descriptions: &diagnostics::registry::Registry,
+ _: &config::Options,
+ _: &ast::CrateConfig,
+ descriptions: &errors::registry::Registry,
output: ErrorOutputType)
-> Compilation {
- match matches.opt_str("explain") {
- Some(ref code) => {
- let normalised = if !code.starts_with("E") {
- format!("E{0:0>4}", code)
- } else {
- code.to_string()
- };
- match descriptions.find_description(&normalised) {
- Some(ref description) => {
- // Slice off the leading newline and print.
- print!("{}", &description[1..]);
- }
- None => {
- early_error(output, &format!("no extended information for {}", code));
- }
- }
- return Compilation::Stop;
- }
- None => (),
+ if let Some(ref code) = matches.opt_str("explain") {
+ handle_explain(code, descriptions, output);
+ return Compilation::Stop;
}
- return Compilation::Continue;
+ Compilation::Continue
}
fn no_input(&mut self,
matches: &getopts::Matches,
sopts: &config::Options,
+ cfg: &ast::CrateConfig,
odir: &Option<PathBuf>,
ofile: &Option<PathBuf>,
- descriptions: &diagnostics::registry::Registry)
+ descriptions: &errors::registry::Registry)
-> Option<(Input, Option<PathBuf>)> {
match matches.free.len() {
0 => {
describe_lints(&ls, false);
return None;
}
- let cstore = Rc::new(CStore::new(token::get_ident_interner()));
- let sess = build_session(sopts.clone(), None, descriptions.clone(),
- cstore.clone());
+ let mut sess = build_session(sopts.clone(),
+ None,
+ descriptions.clone());
+ rustc_trans::init(&sess);
rustc_lint::register_builtins(&mut sess.lint_store.borrow_mut(), Some(&sess));
- let should_stop = RustcDefaultCalls::print_crate_info(&sess, None, odir, ofile);
+ let mut cfg = config::build_configuration(&sess, cfg.clone());
+ target_features::add_configuration(&mut cfg, &sess);
+ sess.parse_sess.config = cfg;
+ let should_stop =
+ RustcDefaultCalls::print_crate_info(&sess, None, odir, ofile);
+
if should_stop == Compilation::Stop {
return None;
}
1 => panic!("make_input should have provided valid inputs"),
_ => early_error(sopts.error_format, "multiple input filenames provided"),
}
-
- None
- }
-
- fn parse_pretty(&mut self,
- sess: &Session,
- matches: &getopts::Matches)
- -> Option<(PpMode, Option<UserIdentifiedItem>)> {
- let pretty = if sess.opts.debugging_opts.unstable_options {
- matches.opt_default("pretty", "normal").map(|a| {
- // stable pretty-print variants only
- pretty::parse_pretty(sess, &a, false)
- })
- } else {
- None
- };
- if pretty.is_none() && sess.unstable_options() {
- matches.opt_str("unpretty").map(|a| {
- // extended with unstable pretty-print variants
- pretty::parse_pretty(sess, &a, true)
- })
- } else {
- pretty
- }
}
fn late_callback(&mut self,
matches: &getopts::Matches,
sess: &Session,
+ cstore: &CrateStore,
input: &Input,
odir: &Option<PathBuf>,
ofile: &Option<PathBuf>)
-> Compilation {
RustcDefaultCalls::print_crate_info(sess, Some(input), odir, ofile)
- .and_then(|| RustcDefaultCalls::list_metadata(sess, matches, input))
+ .and_then(|| RustcDefaultCalls::list_metadata(sess, cstore, matches, input))
}
- fn build_controller(&mut self, sess: &Session) -> CompileController<'a> {
+ fn build_controller(&mut self,
+ sess: &Session,
+ matches: &getopts::Matches)
+ -> CompileController<'a> {
let mut control = CompileController::basic();
- if sess.opts.parse_only || sess.opts.debugging_opts.show_span.is_some() ||
- sess.opts.debugging_opts.ast_json_noexpand {
- control.after_parse.stop = Compilation::Stop;
+ control.keep_ast = sess.opts.debugging_opts.keep_ast;
+ control.continue_parse_after_error = sess.opts.debugging_opts.continue_parse_after_error;
+
+ if let Some((ppm, opt_uii)) = parse_pretty(sess, matches) {
+ if ppm.needs_ast_map(&opt_uii) {
+ control.after_hir_lowering.stop = Compilation::Stop;
+
+ control.after_parse.callback = box move |state| {
+ state.krate = Some(pretty::fold_crate(state.krate.take().unwrap(), ppm));
+ };
+ control.after_hir_lowering.callback = box move |state| {
+ pretty::print_after_hir_lowering(state.session,
+ state.cstore.unwrap(),
+ state.hir_map.unwrap(),
+ state.analysis.unwrap(),
+ state.resolutions.unwrap(),
+ state.input,
+ &state.expanded_crate.take().unwrap(),
+ state.crate_name.unwrap(),
+ ppm,
+ state.arena.unwrap(),
+ state.arenas.unwrap(),
+ state.output_filenames.unwrap(),
+ opt_uii.clone(),
+ state.out_file);
+ };
+ } else {
+ control.after_parse.stop = Compilation::Stop;
+
+ control.after_parse.callback = box move |state| {
+ let krate = pretty::fold_crate(state.krate.take().unwrap(), ppm);
+ pretty::print_after_parsing(state.session,
+ state.input,
+ &krate,
+ ppm,
+ state.out_file);
+ };
+ }
+
+ return control;
}
- if sess.opts.no_analysis || sess.opts.debugging_opts.ast_json {
- control.after_write_deps.stop = Compilation::Stop;
+ if sess.opts.debugging_opts.parse_only ||
+ sess.opts.debugging_opts.show_span.is_some() ||
+ sess.opts.debugging_opts.ast_json_noexpand {
+ control.after_parse.stop = Compilation::Stop;
}
- if sess.opts.no_trans {
- control.after_analysis.stop = Compilation::Stop;
+ if sess.opts.debugging_opts.no_analysis ||
+ sess.opts.debugging_opts.ast_json {
+ control.after_hir_lowering.stop = Compilation::Stop;
}
- if !sess.opts.output_types.keys().any(|&i| i == OutputType::Exe) {
+ if !sess.opts.output_types.keys().any(|&i| i == OutputType::Exe ||
+ i == OutputType::Metadata) {
control.after_llvm.stop = Compilation::Stop;
}
- if sess.opts.debugging_opts.save_analysis {
- control.after_analysis.callback = box |state| {
- time(state.session.time_passes(), "save analysis", || {
- save::process_crate(state.tcx.unwrap(),
- state.lcx.unwrap(),
- state.krate.unwrap(),
- state.analysis.unwrap(),
- state.crate_name.unwrap(),
- state.out_dir)
- });
- };
- control.make_glob_map = resolve::MakeGlobMap::Yes;
+ if save_analysis(sess) {
+ enable_save_analysis(&mut control);
}
+ if sess.print_fuel_crate.is_some() {
+ let old_callback = control.compilation_done.callback;
+ control.compilation_done.callback = box move |state| {
+ old_callback(state);
+ let sess = state.session;
+ println!("Fuel used by {}: {}",
+ sess.print_fuel_crate.as_ref().unwrap(),
+ sess.print_fuel.get());
+ }
+ }
control
}
}
+pub fn enable_save_analysis(control: &mut CompileController) {
+ control.keep_ast = true;
+ control.after_analysis.callback = box |state| {
+ time(state.session.time_passes(), "save analysis", || {
+ save::process_crate(state.tcx.unwrap(),
+ state.expanded_crate.unwrap(),
+ state.analysis.unwrap(),
+ state.crate_name.unwrap(),
+ None,
+ DumpHandler::new(state.out_dir,
+ state.crate_name.unwrap()))
+ });
+ };
+ control.after_analysis.run_callback_on_error = true;
+ 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, matches: &getopts::Matches, input: &Input) -> Compilation {
+ pub fn list_metadata(sess: &Session,
+ cstore: &CrateStore,
+ matches: &getopts::Matches,
+ input: &Input)
+ -> Compilation {
let r = matches.opt_strs("Z");
if r.contains(&("ls".to_string())) {
match input {
&Input::File(ref ifile) => {
let path = &(*ifile);
let mut v = Vec::new();
- loader::list_file_metadata(&sess.target.target, path, &mut v)
- .unwrap();
+ locator::list_file_metadata(&sess.target.target,
+ path,
+ cstore.metadata_loader(),
+ &mut v)
+ .unwrap();
println!("{}", String::from_utf8(v).unwrap());
}
- &Input::Str(_) => {
+ &Input::Str { .. } => {
early_error(ErrorOutputType::default(), "cannot list metadata for stdin");
}
}
odir: &Option<PathBuf>,
ofile: &Option<PathBuf>)
-> Compilation {
- if sess.opts.prints.is_empty() {
+ // PrintRequest::NativeStaticLibs is special - printed during linking
+ // (empty iterator returns true)
+ if sess.opts.prints.iter().all(|&p| p==PrintRequest::NativeStaticLibs) {
return Compilation::Continue;
}
- let attrs = input.map(|input| parse_crate_attrs(sess, input));
+ let attrs = match input {
+ None => None,
+ Some(input) => {
+ let result = parse_crate_attrs(sess, input);
+ match result {
+ Ok(attrs) => Some(attrs),
+ Err(mut parse_error) => {
+ parse_error.emit();
+ return Compilation::Stop;
+ }
+ }
+ }
+ };
for req in &sess.opts.prints {
match *req {
+ PrintRequest::TargetList => {
+ let mut targets = rustc_back::target::get_targets().collect::<Vec<String>>();
+ targets.sort();
+ println!("{}", targets.join("\n"));
+ },
PrintRequest::Sysroot => println!("{}", sess.sysroot().display()),
+ PrintRequest::TargetSpec => println!("{}", sess.target.target.to_json().pretty()),
PrintRequest::FileNames |
PrintRequest::CrateName => {
let input = match input {
};
let attrs = attrs.as_ref().unwrap();
let t_outputs = driver::build_output_filenames(input, odir, ofile, attrs, sess);
- let id = link::find_crate_name(Some(sess), attrs, input);
+ let id = rustc_trans_utils::link::find_crate_name(Some(sess), attrs, input);
if *req == PrintRequest::CrateName {
println!("{}", id);
continue;
}
let crate_types = driver::collect_crate_types(sess, attrs);
- let metadata = driver::collect_crate_metadata(sess, attrs);
- *sess.crate_metadata.borrow_mut() = metadata;
for &style in &crate_types {
- let fname = link::filename_for_input(sess, style, &id, &t_outputs);
+ let fname = rustc_trans_utils::link::filename_for_input(
+ sess,
+ style,
+ &id,
+ &t_outputs
+ );
println!("{}",
fname.file_name()
.unwrap()
.to_string_lossy());
}
}
+ PrintRequest::Cfg => {
+ let allow_unstable_cfg = UnstableFeatures::from_environment()
+ .is_nightly_build();
+
+ let mut cfgs = Vec::new();
+ for &(name, ref value) in sess.parse_sess.config.iter() {
+ let gated_cfg = GatedCfg::gate(&ast::MetaItem {
+ name,
+ node: ast::MetaItemKind::Word,
+ span: DUMMY_SP,
+ });
+
+ // Note that crt-static is a specially recognized cfg
+ // directive that's printed out here as part of
+ // rust-lang/rust#37406, but in general the
+ // `target_feature` cfg is gated under
+ // rust-lang/rust#29717. For now this is just
+ // specifically allowing the crt-static cfg and that's
+ // it, this is intended to get into Cargo and then go
+ // through to build scripts.
+ let value = value.as_ref().map(|s| s.as_str());
+ let value = value.as_ref().map(|s| s.as_ref());
+ if name != "target_feature" || value != Some("crt-static") {
+ if !allow_unstable_cfg && gated_cfg.is_some() {
+ continue;
+ }
+ }
+
+ cfgs.push(if let Some(value) = value {
+ format!("{}=\"{}\"", name, value)
+ } else {
+ format!("{}", name)
+ });
+ }
+
+ cfgs.sort();
+ for cfg in cfgs {
+ println!("{}", cfg);
+ }
+ }
+ PrintRequest::RelocationModels => {
+ println!("Available relocation models:");
+ for &(name, _) in rustc_trans::back::write::RELOC_MODEL_ARGS.iter() {
+ println!(" {}", name);
+ }
+ println!("");
+ }
+ PrintRequest::CodeModels => {
+ println!("Available code models:");
+ for &(name, _) in rustc_trans::back::write::CODE_GEN_MODEL_ARGS.iter(){
+ println!(" {}", name);
+ }
+ println!("");
+ }
+ PrintRequest::TlsModels => {
+ println!("Available TLS models:");
+ for &(name, _) in rustc_trans::back::write::TLS_MODEL_ARGS.iter(){
+ println!(" {}", name);
+ }
+ println!("");
+ }
+ PrintRequest::TargetCPUs | PrintRequest::TargetFeatures => {
+ rustc_trans::print(*req, sess);
+ }
+ PrintRequest::NativeStaticLibs => {
+ println!("Native static libs can be printed only during linking");
+ }
}
}
return Compilation::Stop;
}
/// Returns a version string such as "0.12.0-dev".
-pub fn release_str() -> Option<&'static str> {
+fn release_str() -> Option<&'static str> {
option_env!("CFG_RELEASE")
}
/// Returns the full SHA1 hash of HEAD of the Git repo from which rustc was built.
-pub fn commit_hash_str() -> Option<&'static str> {
+fn commit_hash_str() -> Option<&'static str> {
option_env!("CFG_VER_HASH")
}
/// Returns the "commit date" of HEAD of the Git repo from which rustc was built as a static string.
-pub fn commit_date_str() -> Option<&'static str> {
+fn commit_date_str() -> Option<&'static str> {
option_env!("CFG_VER_DATE")
}
println!("commit-date: {}", unw(commit_date_str()));
println!("host: {}", config::host_triple());
println!("release: {}", unw(release_str()));
+ rustc_trans::print_version();
}
}
} else {
config::rustc_short_optgroups()
};
- let groups: Vec<_> = groups.into_iter()
- .filter(|x| include_unstable_options || x.is_stable())
- .map(|x| x.opt_group)
- .collect();
+ let mut options = getopts::Options::new();
+ for option in groups.iter().filter(|x| include_unstable_options || x.is_stable()) {
+ (option.apply)(&mut options);
+ }
let message = format!("Usage: rustc [OPTIONS] INPUT");
- let extra_help = if verbose {
+ let nightly_help = if nightly_options::is_nightly_build() {
+ "\n -Z help Print internal options for debugging rustc"
+ } else {
+ ""
+ };
+ let verbose_help = if verbose {
""
} else {
"\n --help -v Print the full set of options rustc accepts"
println!("{}\nAdditional help:
-C help Print codegen options
-W help \
- Print 'lint' options and default settings
- -Z help Print internal \
- options for debugging rustc{}\n",
- getopts::usage(&message, &groups),
- extra_help);
+ Print 'lint' options and default settings{}{}\n",
+ options.usage(&message),
+ nightly_help,
+ verbose_help);
}
fn describe_lints(lint_store: &lint::LintStore, loaded_plugins: bool) {
Allow <foo>
-D <foo> Deny <foo>
-F <foo> Forbid <foo> \
- (deny, and deny all overrides)
+ (deny <foo> and all attempts to override)
");
let (plugin_groups, builtin_groups): (Vec<_>, _) = lint_store.get_lint_groups()
.iter()
.cloned()
- .partition(|&(_, _, p)| p);
+ .partition(|&(.., p)| p);
let plugin_groups = sort_lint_groups(plugin_groups);
let builtin_groups = sort_lint_groups(builtin_groups);
for lint in lints {
let name = lint.name_lower().replace("_", "-");
println!(" {} {:7.7} {}",
- padded(&name[..]),
+ padded(&name),
lint.default_level.as_str(),
lint.desc);
}
for (name, to) in lints {
let name = name.to_lowercase().replace("_", "-");
let desc = to.into_iter()
- .map(|x| x.as_str().replace("_", "-"))
+ .map(|x| x.to_string().replace("_", "-"))
.collect::<Vec<String>>()
.join(", ");
- println!(" {} {}", padded(&name[..]), desc);
+ println!(" {} {}", padded(&name), desc);
}
println!("\n");
};
println!("Compiler plugins can provide additional lints and lint groups. To see a \
listing of these, re-run `rustc -W help` with a crate filename.");
}
- (false, _, _) => panic!("didn't load lint plugins but got them anyway!"),
+ (false, ..) => panic!("didn't load lint plugins but got them anyway!"),
(true, 0, 0) => println!("This crate does not load any lint plugins or lint groups."),
(true, l, g) => {
if l > 0 {
}
/// Process command line options. Emits messages as appropriate. If compilation
-/// should continue, returns a getopts::Matches object parsed from args, otherwise
-/// returns None.
-pub fn handle_options(mut args: Vec<String>) -> Option<getopts::Matches> {
+/// should continue, returns a getopts::Matches object parsed from args,
+/// otherwise returns None.
+///
+/// The compiler's handling of options is a little complicated as it ties into
+/// our stability story, and it's even *more* complicated by historical
+/// accidents. The current intention of each compiler option is to have one of
+/// three modes:
+///
+/// 1. An option is stable and can be used everywhere.
+/// 2. An option is unstable, but was historically allowed on the stable
+/// channel.
+/// 3. An option is unstable, and can only be used on nightly.
+///
+/// Like unstable library and language features, however, unstable options have
+/// always required a form of "opt in" to indicate that you're using them. This
+/// provides the easy ability to scan a code base to check to see if anything
+/// unstable is being used. Currently, this "opt in" is the `-Z` "zed" flag.
+///
+/// All options behind `-Z` are considered unstable by default. Other top-level
+/// options can also be considered unstable, and they were unlocked through the
+/// `-Z unstable-options` flag. Note that `-Z` remains to be the root of
+/// instability in both cases, though.
+///
+/// So with all that in mind, the comments below have some more detail about the
+/// contortions done here to get things to work out correctly.
+pub fn handle_options(args: &[String]) -> Option<getopts::Matches> {
// Throw away the first argument, the name of the binary
- let _binary = args.remove(0);
+ let args = &args[1..];
if args.is_empty() {
// user did not write `-v` nor `-Z unstable-options`, so do not
return None;
}
- fn allows_unstable_options(matches: &getopts::Matches) -> bool {
- let r = matches.opt_strs("Z");
- r.iter().any(|x| *x == "unstable-options")
- }
-
- fn parse_all_options(args: &Vec<String>) -> getopts::Matches {
- let all_groups: Vec<getopts::OptGroup> = config::rustc_optgroups()
- .into_iter()
- .map(|x| x.opt_group)
- .collect();
- match getopts::getopts(&args[..], &all_groups) {
- Ok(m) => {
- if !allows_unstable_options(&m) {
- // If -Z unstable-options was not specified, verify that
- // no unstable options were present.
- for opt in config::rustc_optgroups().into_iter().filter(|x| !x.is_stable()) {
- let opt_name = if !opt.opt_group.long_name.is_empty() {
- &opt.opt_group.long_name
- } else {
- &opt.opt_group.short_name
- };
- if m.opt_present(opt_name) {
- early_error(ErrorOutputType::default(),
- &format!("use of unstable option '{}' requires -Z \
- unstable-options",
- opt_name));
- }
- }
- }
- m
- }
- Err(f) => early_error(ErrorOutputType::default(), &f.to_string()),
- }
+ // Parse with *all* options defined in the compiler, we don't worry about
+ // option stability here we just want to parse as much as possible.
+ let mut options = getopts::Options::new();
+ for option in config::rustc_optgroups() {
+ (option.apply)(&mut options);
}
-
- // As a speed optimization, first try to parse the command-line using just
- // the stable options.
- let matches = match getopts::getopts(&args[..], &config::optgroups()) {
- Ok(ref m) if allows_unstable_options(m) => {
- // If -Z unstable-options was specified, redo parsing with the
- // unstable options to ensure that unstable options are defined
- // in the returned getopts::Matches.
- parse_all_options(&args)
- }
+ let matches = match options.parse(args) {
Ok(m) => m,
- Err(_) => {
- // redo option parsing, including unstable options this time,
- // in anticipation that the mishandled option was one of the
- // unstable ones.
- parse_all_options(&args)
- }
+ Err(f) => early_error(ErrorOutputType::default(), &f.to_string()),
};
+ // For all options we just parsed, we check a few aspects:
+ //
+ // * If the option is stable, we're all good
+ // * If the option wasn't passed, we're all good
+ // * If `-Z unstable-options` wasn't passed (and we're not a -Z option
+ // ourselves), then we require the `-Z unstable-options` flag to unlock
+ // this option that was passed.
+ // * If we're a nightly compiler, then unstable options are now unlocked, so
+ // we're good to go.
+ // * Otherwise, if we're a truly unstable option then we generate an error
+ // (unstable option being used on stable)
+ // * If we're a historically stable-but-should-be-unstable option then we
+ // emit a warning that we're going to turn this into an error soon.
+ nightly_options::check_nightly_options(&matches, &config::rustc_optgroups());
+
if matches.opt_present("h") || matches.opt_present("help") {
+ // Only show unstable options in --help if we *really* accept unstable
+ // options, which catches the case where we got `-Z unstable-options` on
+ // the stable channel of Rust which was accidentally allowed
+ // historically.
usage(matches.opt_present("verbose"),
- allows_unstable_options(&matches));
+ nightly_options::is_unstable_enabled(&matches));
return None;
}
// Don't handle -W help here, because we might first load plugins.
-
let r = matches.opt_strs("Z");
if r.iter().any(|x| *x == "help") {
describe_debug_flags();
return None;
}
+ if cg_flags.iter().any(|x| *x == "no-stack-check") {
+ early_warn(ErrorOutputType::default(),
+ "the --no-stack-check flag is deprecated and does nothing");
+ }
+
if cg_flags.contains(&"passes=list".to_string()) {
- unsafe {
- ::llvm::LLVMRustPrintPasses();
- }
+ rustc_trans::print_passes();
return None;
}
Some(matches)
}
-fn parse_crate_attrs(sess: &Session, input: &Input) -> Vec<ast::Attribute> {
- let result = match *input {
+fn parse_crate_attrs<'a>(sess: &'a Session, input: &Input) -> PResult<'a, Vec<ast::Attribute>> {
+ match *input {
Input::File(ref ifile) => {
- parse::parse_crate_attrs_from_file(ifile, Vec::new(), &sess.parse_sess)
+ parse::parse_crate_attrs_from_file(ifile, &sess.parse_sess)
}
- Input::Str(ref src) => {
- parse::parse_crate_attrs_from_source_str(driver::anon_src().to_string(),
- src.to_string(),
- Vec::new(),
- &sess.parse_sess)
+ Input::Str { ref name, ref input } => {
+ parse::parse_crate_attrs_from_source_str(name.clone(), input.clone(), &sess.parse_sess)
}
- };
- result.into_iter().collect()
+ }
+}
+
+/// Runs `f` in a suitable thread for running `rustc`; returns a
+/// `Result` with either the return value of `f` or -- if a panic
+/// occurs -- the panic value.
+pub fn in_rustc_thread<F, R>(f: F) -> Result<R, Box<Any + Send>>
+ where F: FnOnce() -> R + Send + 'static,
+ R: Send + 'static,
+{
+ // Temporarily have stack size set to 16MB to deal with nom-using crates failing
+ const STACK_SIZE: usize = 16 * 1024 * 1024; // 16MB
+
+ let mut cfg = thread::Builder::new().name("rustc".to_string());
+
+ // FIXME: Hacks on hacks. If the env is trying to override the stack size
+ // then *don't* set it explicitly.
+ if env::var_os("RUST_MIN_STACK").is_none() {
+ cfg = cfg.stack_size(STACK_SIZE);
+ }
+
+ let thread = cfg.spawn(f);
+ thread.unwrap().join()
}
/// Run a procedure which will detect panics in the compiler and print nicer
/// 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) {
- const STACK_SIZE: usize = 8 * 1024 * 1024; // 8MB
-
struct Sink(Arc<Mutex<Vec<u8>>>);
impl Write for Sink {
fn write(&mut self, data: &[u8]) -> io::Result<usize> {
let data = Arc::new(Mutex::new(Vec::new()));
let err = Sink(data.clone());
- let mut cfg = thread::Builder::new().name("rustc".to_string());
-
- // FIXME: Hacks on hacks. If the env is trying to override the stack size
- // then *don't* set it explicitly.
- if env::var_os("RUST_MIN_STACK").is_none() {
- cfg = cfg.stack_size(STACK_SIZE);
- }
-
- match cfg.spawn(move || {
- io::set_panic(box err);
- f()
- })
- .unwrap()
- .join() {
- Ok(()) => {
- // fallthrough
- }
- Err(value) => {
- // Thread panicked without emitting a fatal diagnostic
- if !value.is::<errors::FatalError>() {
- let mut emitter = errors::emitter::BasicEmitter::stderr(errors::ColorConfig::Auto);
-
- // a .span_bug or .bug call has already printed what
- // it wants to print.
- if !value.is::<errors::ExplicitBug>() {
- emitter.emit(None, "unexpected panic", None, errors::Level::Bug);
- }
-
- let xs = ["the compiler unexpectedly panicked. this is a bug.".to_string(),
- format!("we would appreciate a bug report: {}", BUG_REPORT_URL)];
- for note in &xs {
- emitter.emit(None, ¬e[..], None, errors::Level::Note)
- }
- if let None = env::var_os("RUST_BACKTRACE") {
- emitter.emit(None,
- "run with `RUST_BACKTRACE=1` for a backtrace",
- None,
- errors::Level::Note);
- }
+ 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>() {
+ let emitter =
+ Box::new(errors::emitter::EmitterWriter::stderr(errors::ColorConfig::Auto,
+ None,
+ false));
+ let handler = errors::Handler::with_emitter(true, false, emitter);
+
+ // a .span_bug or .bug call has already printed what
+ // it wants to print.
+ if !value.is::<errors::ExplicitBug>() {
+ handler.emit(&MultiSpan::new(),
+ "unexpected panic",
+ errors::Level::Bug);
+ }
- println!("{}", str::from_utf8(&data.lock().unwrap()).unwrap());
+ let xs = ["the compiler unexpectedly panicked. this is a bug.".to_string(),
+ format!("we would appreciate a bug report: {}", BUG_REPORT_URL),
+ format!("rustc {} running on {}",
+ option_env!("CFG_VERSION").unwrap_or("unknown_version"),
+ config::host_triple())];
+ for note in &xs {
+ handler.emit(&MultiSpan::new(),
+ ¬e,
+ 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);
}
- // 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(box io::sink());
- panic!();
+ eprintln!("{}", str::from_utf8(&data.lock().unwrap()).unwrap());
}
+
+ exit_on_err();
}
}
-pub fn diagnostics_registry() -> diagnostics::registry::Registry {
- use syntax::diagnostics::registry::Registry;
+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!();
+}
+
+pub fn diagnostics_registry() -> errors::registry::Registry {
+ use errors::registry::Registry;
let mut all_errors = Vec::new();
all_errors.extend_from_slice(&rustc::DIAGNOSTICS);
all_errors.extend_from_slice(&rustc_typeck::DIAGNOSTICS);
- 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);
+ all_errors.extend_from_slice(&rustc_passes::DIAGNOSTICS);
+ all_errors.extend_from_slice(&rustc_plugin::DIAGNOSTICS);
+ all_errors.extend_from_slice(&rustc_mir::DIAGNOSTICS);
+ all_errors.extend_from_slice(&syntax::DIAGNOSTICS);
+
+ Registry::new(&all_errors)
+}
- 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() {
- let result = run(env::args().collect());
+ env_logger::init().unwrap();
+ let result = run(|| run_compiler(&get_args(),
+ &mut RustcDefaultCalls,
+ None,
+ None));
process::exit(result as i32);
}