From 55d5c46d3a87caa1e38f1a3f1489001e35727998 Mon Sep 17 00:00:00 2001 From: Nick Cameron Date: Sun, 11 Jan 2015 15:03:34 +1300 Subject: [PATCH] Make the compilation process more easily customisable --- src/librustc_driver/driver.rs | 262 ++++++++++++++++++++------- src/librustc_driver/lib.rs | 43 ++++- src/librustc_driver/pretty.rs | 13 +- src/librustc_trans/save/mod.rs | 6 +- src/librustdoc/core.rs | 7 +- src/librustdoc/lib.rs | 1 + src/librustdoc/test.rs | 11 +- src/test/run-make/issue-19371/foo.rs | 6 +- 8 files changed, 268 insertions(+), 81 deletions(-) diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index c2af4315b06a2..f35f8ab1b40f5 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -24,7 +24,6 @@ use rustc_borrowck as borrowck; use rustc_resolve as resolve; use rustc_trans::back::link; use rustc_trans::back::write; -use rustc_trans::save; use rustc_trans::trans; use rustc_typeck as typeck; @@ -47,23 +46,43 @@ pub fn compile_input(sess: Session, input: &Input, outdir: &Option, output: &Option, - addl_plugins: Option>) { + addl_plugins: Option>, + control: CompileController) { + macro_rules! controller_entry_point{($point: ident, $make_state: expr) => ({ + { + let state = $make_state; + (control.$point.callback)(state); + } + if control.$point.stop { + return; + } + })} + // 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, sess) = { let (outputs, expanded_crate, id) = { let krate = phase_1_parse_input(&sess, cfg, input); - if stop_after_phase_1(&sess) { return; } + + controller_entry_point!(after_parse, + CompileState::state_after_parse(input, + &sess, + outdir, + &krate)); + let outputs = build_output_filenames(input, outdir, output, &krate.attrs[], &sess); - let id = link::find_crate_name(Some(&sess), &krate.attrs[], + let id = link::find_crate_name(Some(&sess), + &krate.attrs[], input); let expanded_crate - = match phase_2_configure_and_expand(&sess, krate, &id[], + = match phase_2_configure_and_expand(&sess, + krate, + &id[], addl_plugins) { None => return, Some(k) => k @@ -72,23 +91,37 @@ pub fn compile_input(sess: Session, (outputs, expanded_crate, id) }; + controller_entry_point!(after_expand, + CompileState::state_after_expand(input, + &sess, + outdir, + &expanded_crate, + &id[])); + let mut forest = ast_map::Forest::new(expanded_crate); let ast_map = assign_node_ids_and_map(&sess, &mut forest); write_out_deps(&sess, input, &outputs, &id[]); - if stop_after_phase_2(&sess) { return; } - let arenas = ty::CtxtArenas::new(); - let analysis = phase_3_run_analysis_passes(sess, ast_map, &arenas, id); - phase_save_analysis(&analysis.ty_cx.sess, analysis.ty_cx.map.krate(), &analysis, outdir); + let analysis = phase_3_run_analysis_passes(sess, + ast_map, + &arenas, + id, + control.make_glob_map); + + controller_entry_point!(after_analysis, + CompileState::state_after_analysis(input, + &analysis.ty_cx.sess, + outdir, + analysis.ty_cx.map.krate(), + &analysis, + &analysis.ty_cx)); if log_enabled!(::log::INFO) { println!("Pre-trans"); analysis.ty_cx.print_debug_stats(); } - - if stop_after_phase_3(&analysis.ty_cx.sess) { return; } let (tcx, trans) = phase_4_translate_to_llvm(analysis); if log_enabled!(::log::INFO) { @@ -102,7 +135,13 @@ pub fn compile_input(sess: Session, (outputs, trans, tcx.sess) }; phase_5_run_llvm_passes(&sess, &trans, &outputs); - if stop_after_phase_5(&sess) { return; } + + controller_entry_point!(after_llvm, + CompileState::state_after_llvm(input, + &sess, + outdir, + &trans)); + phase_6_link_output(&sess, &trans, &outputs); } @@ -120,6 +159,146 @@ pub fn source_name(input: &Input) -> String { } } +/// CompileController is used to customise compilation, it allows compilation to +/// be stopped and/or to call arbitrary code at various points in compilation. +/// It also allows for various flags to be set to influence what information gets +/// colelcted during compilation. +/// +/// This is a somewhat higher level controller than a Session - the Session +/// controls what happens in each phase, whereas the CompileController controls +/// whether a phase is run at all and whether other code (from outside the +/// the compiler) is run between phases. +/// +/// Note that if compilation is set to stop and a callback is provided for a +/// given entry point, the callback is called before compilation is stopped. +/// +/// Expect more entry points to be added in the future. +pub struct CompileController<'a> { + pub after_parse: PhaseController<'a>, + pub after_expand: PhaseController<'a>, + pub after_analysis: PhaseController<'a>, + pub after_llvm: PhaseController<'a>, + + pub make_glob_map: resolve::MakeGlobMap, +} + +impl<'a> CompileController<'a> { + pub fn basic() -> CompileController<'a> { + CompileController { + after_parse: PhaseController::basic(), + after_expand: PhaseController::basic(), + after_analysis: PhaseController::basic(), + after_llvm: PhaseController::basic(), + make_glob_map: resolve::MakeGlobMap::No, + } + } +} + +pub struct PhaseController<'a> { + pub stop: bool, + pub callback: Box () + 'a>, +} + +impl<'a> PhaseController<'a> { + pub fn basic() -> PhaseController<'a> { + PhaseController { + stop: false, + callback: box |&: _| {}, + } + } +} + +/// State that is passed to a callback. What state is available depends on when +/// during compilation the callback is made. See the various constructor methods +/// (`state_*`) in the impl to see which data is provided for any given entry point. +pub struct CompileState<'a, 'ast: 'a, 'tcx: 'a> { + pub input: &'a Input, + pub session: &'a Session, + pub cfg: Option<&'a ast::CrateConfig>, + pub krate: Option<&'a ast::Crate>, + pub crate_name: Option<&'a str>, + pub output_filenames: Option<&'a OutputFilenames>, + pub out_dir: Option<&'a Path>, + pub expanded_crate: Option<&'a ast::Crate>, + pub ast_map: Option<&'a ast_map::Map<'ast>>, + pub analysis: Option<&'a ty::CrateAnalysis<'tcx>>, + pub tcx: Option<&'a ty::ctxt<'tcx>>, + pub trans: Option<&'a trans::CrateTranslation>, +} + +impl<'a, 'ast, 'tcx> CompileState<'a, 'ast, 'tcx> { + fn empty(input: &'a Input, + session: &'a Session, + out_dir: &'a Option) + -> CompileState<'a, 'ast, 'tcx> { + CompileState { + input: input, + session: session, + out_dir: out_dir.as_ref(), + cfg: None, + krate: None, + crate_name: None, + output_filenames: None, + expanded_crate: None, + ast_map: None, + analysis: None, + tcx: None, + trans: None, + } + } + + fn state_after_parse(input: &'a Input, + session: &'a Session, + out_dir: &'a Option, + krate: &'a ast::Crate) + -> CompileState<'a, 'ast, 'tcx> { + CompileState { + krate: Some(krate), + .. CompileState::empty(input, session, out_dir) + } + } + + fn state_after_expand(input: &'a Input, + session: &'a Session, + out_dir: &'a Option, + expanded_crate: &'a ast::Crate, + crate_name: &'a str) + -> CompileState<'a, 'ast, 'tcx> { + CompileState { + crate_name: Some(crate_name), + expanded_crate: Some(expanded_crate), + .. CompileState::empty(input, session, out_dir) + } + } + + fn state_after_analysis(input: &'a Input, + session: &'a Session, + out_dir: &'a Option, + krate: &'a ast::Crate, + analysis: &'a ty::CrateAnalysis<'tcx>, + tcx: &'a ty::ctxt<'tcx>) + -> CompileState<'a, 'ast, 'tcx> { + CompileState { + analysis: Some(analysis), + tcx: Some(tcx), + krate: Some(krate), + .. CompileState::empty(input, session, out_dir) + } + } + + + fn state_after_llvm(input: &'a Input, + session: &'a Session, + out_dir: &'a Option, + trans: &'a trans::CrateTranslation) + -> CompileState<'a, 'ast, 'tcx> { + CompileState { + trans: Some(trans), + .. CompileState::empty(input, session, out_dir) + } + } +} + pub fn phase_1_parse_input(sess: &Session, cfg: ast::CrateConfig, input: &Input) -> ast::Crate { // These may be left in an incoherent state after a previous compile. @@ -347,7 +526,9 @@ pub fn assign_node_ids_and_map<'ast>(sess: &Session, pub fn phase_3_run_analysis_passes<'tcx>(sess: Session, ast_map: ast_map::Map<'tcx>, arenas: &'tcx ty::CtxtArenas<'tcx>, - name: String) -> ty::CrateAnalysis<'tcx> { + name: String, + make_glob_map: resolve::MakeGlobMap) + -> ty::CrateAnalysis<'tcx> { let time_passes = sess.time_passes(); let krate = ast_map.krate(); @@ -357,11 +538,6 @@ pub fn phase_3_run_analysis_passes<'tcx>(sess: Session, let lang_items = time(time_passes, "language item collection", (), |_| middle::lang_items::collect_language_items(krate, &sess)); - let make_glob_map = if save_analysis(&sess) { - resolve::MakeGlobMap::Yes - } else { - resolve::MakeGlobMap::No - }; let resolve::CrateMap { def_map, freevars, @@ -483,21 +659,6 @@ pub fn phase_3_run_analysis_passes<'tcx>(sess: Session, } } -fn save_analysis(sess: &Session) -> bool { - sess.opts.debugging_opts.save_analysis -} - -pub fn phase_save_analysis(sess: &Session, - krate: &ast::Crate, - analysis: &ty::CrateAnalysis, - odir: &Option) { - if !save_analysis(sess) { - return; - } - time(sess.time_passes(), "save analysis", krate, |krate| - save::process_crate(sess, krate, analysis, odir)); -} - /// Run the translation phase to LLVM, after which the AST and analysis can /// be discarded. pub fn phase_4_translate_to_llvm<'tcx>(analysis: ty::CrateAnalysis<'tcx>) @@ -559,41 +720,6 @@ pub fn phase_6_link_output(sess: &Session, os::setenv("PATH", old_path); } -pub fn stop_after_phase_3(sess: &Session) -> bool { - if sess.opts.no_trans { - debug!("invoked with --no-trans, returning early from compile_input"); - return true; - } - return false; -} - -pub fn stop_after_phase_1(sess: &Session) -> bool { - if sess.opts.parse_only { - debug!("invoked with --parse-only, returning early from compile_input"); - return true; - } - if sess.opts.show_span.is_some() { - return true; - } - return sess.opts.debugging_opts.ast_json_noexpand; -} - -pub fn stop_after_phase_2(sess: &Session) -> bool { - if sess.opts.no_analysis { - debug!("invoked with --no-analysis, returning early from compile_input"); - return true; - } - return sess.opts.debugging_opts.ast_json; -} - -pub fn stop_after_phase_5(sess: &Session) -> bool { - if !sess.opts.output_types.iter().any(|&i| i == config::OutputTypeExe) { - debug!("not building executable, returning early from compile_input"); - return true; - } - return false; -} - 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 9122a34a79362..ce2b03e17448a 100644 --- a/src/librustc_driver/lib.rs +++ b/src/librustc_driver/lib.rs @@ -48,7 +48,11 @@ extern crate "rustc_llvm" as llvm; pub use syntax::diagnostic; +use driver::CompileController; + +use rustc_resolve as resolve; use rustc_trans::back::link; +use rustc_trans::save; use rustc::session::{config, Session, build_session}; use rustc::session::config::{Input, PrintRequest, UnstableFeatures}; use rustc::lint::Lint; @@ -56,6 +60,7 @@ use rustc::lint; use rustc::metadata; use rustc::metadata::creader::CrateOrString::Str; use rustc::DIAGNOSTICS; +use rustc::util::common::time; use std::cmp::Ordering::Equal; use std::io; @@ -188,7 +193,43 @@ fn run_compiler(args: &[String]) { } let plugins = sess.opts.debugging_opts.extra_plugins.clone(); - driver::compile_input(sess, cfg, &input, &odir, &ofile, Some(plugins)); + let control = build_controller(&sess); + driver::compile_input(sess, cfg, &input, &odir, &ofile, Some(plugins), control); +} + +fn build_controller<'a>(sess: &Session) -> CompileController<'a> { + let mut control = CompileController::basic(); + + if sess.opts.parse_only || + sess.opts.show_span.is_some() || + sess.opts.debugging_opts.ast_json_noexpand { + control.after_parse.stop = true; + } + + if sess.opts.no_analysis || sess.opts.debugging_opts.ast_json { + control.after_expand.stop = true; + } + + if sess.opts.no_trans { + control.after_analysis.stop = true; + } + + if !sess.opts.output_types.iter().any(|&i| i == config::OutputTypeExe) { + control.after_llvm.stop = true; + } + + if sess.opts.debugging_opts.save_analysis { + control.after_analysis.callback = box |state| { + time(state.session.time_passes(), "save analysis", state.krate.unwrap(), |krate| + save::process_crate(state.session, + krate, + state.analysis.unwrap(), + state.out_dir)); + }; + control.make_glob_map = resolve::MakeGlobMap::Yes; + } + + control } pub fn get_unstable_features_setting() -> UnstableFeatures { diff --git a/src/librustc_driver/pretty.rs b/src/librustc_driver/pretty.rs index 1765c80f9437b..6429c5f59856e 100644 --- a/src/librustc_driver/pretty.rs +++ b/src/librustc_driver/pretty.rs @@ -27,6 +27,7 @@ use rustc::session::config::Input; use rustc::util::ppaux; use rustc_borrowck as borrowck; use rustc_borrowck::graphviz as borrowck_dot; +use rustc_resolve as resolve; use syntax::ast; use syntax::ast_map::{self, blocks, NodePrinter}; @@ -133,7 +134,11 @@ impl PpSourceMode { } PpmTyped => { let ast_map = ast_map.expect("--pretty=typed missing ast_map"); - let analysis = driver::phase_3_run_analysis_passes(sess, ast_map, arenas, id); + let analysis = driver::phase_3_run_analysis_passes(sess, + ast_map, + arenas, + id, + resolve::MakeGlobMap::No); let annotation = TypedAnnotation { analysis: analysis }; f(&annotation, payload) } @@ -603,7 +608,11 @@ pub fn pretty_print_input(sess: Session, match code { Some(code) => { let variants = gather_flowgraph_variants(&sess); - let analysis = driver::phase_3_run_analysis_passes(sess, ast_map, &arenas, id); + let analysis = driver::phase_3_run_analysis_passes(sess, + ast_map, + &arenas, + id, + resolve::MakeGlobMap::No); print_flowgraph(variants, analysis, code, out) } None => { diff --git a/src/librustc_trans/save/mod.rs b/src/librustc_trans/save/mod.rs index eb163ed7406f2..1bd8e01817485 100644 --- a/src/librustc_trans/save/mod.rs +++ b/src/librustc_trans/save/mod.rs @@ -1505,7 +1505,7 @@ impl<'l, 'tcx, 'v> Visitor<'v> for DxrVisitor<'l, 'tcx> { pub fn process_crate(sess: &Session, krate: &ast::Crate, analysis: &ty::CrateAnalysis, - odir: &Option) { + odir: Option<&Path>) { if generated_code(krate.span) { return; } @@ -1524,8 +1524,8 @@ pub fn process_crate(sess: &Session, // find a path to dump our data to let mut root_path = match os::getenv("DXR_RUST_TEMP_FOLDER") { Some(val) => Path::new(val), - None => match *odir { - Some(ref val) => val.join("dxr"), + None => match odir { + Some(val) => val.join("dxr"), None => Path::new("dxr-temp"), }, }; diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index 4885bd373eb1d..5bef0195874b6 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -16,6 +16,7 @@ use rustc::session::search_paths::SearchPaths; use rustc::middle::{privacy, ty}; use rustc::lint; use rustc_trans::back::link; +use rustc_resolve as resolve; use syntax::{ast, ast_map, codemap, diagnostic}; @@ -126,7 +127,11 @@ pub fn run_core(search_paths: SearchPaths, cfgs: Vec, externs: Externs, let arenas = ty::CtxtArenas::new(); let ty::CrateAnalysis { exported_items, public_items, ty_cx, .. - } = driver::phase_3_run_analysis_passes(sess, ast_map, &arenas, name); + } = driver::phase_3_run_analysis_passes(sess, + ast_map, + &arenas, + name, + resolve::MakeGlobMap::No); let ctxt = DocContext { krate: ty_cx.map.krate(), diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index 6d5df3d777d86..e6eed4806338b 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -27,6 +27,7 @@ extern crate libc; extern crate rustc; extern crate rustc_trans; extern crate rustc_driver; +extern crate rustc_resolve; extern crate serialize; extern crate syntax; extern crate "test" as testing; diff --git a/src/librustdoc/test.rs b/src/librustdoc/test.rs index 38ce738954665..9b8d220acc394 100644 --- a/src/librustdoc/test.rs +++ b/src/librustdoc/test.rs @@ -123,7 +123,6 @@ fn runtest(test: &str, cratename: &str, libs: SearchPaths, search_paths: libs, crate_types: vec!(config::CrateTypeExecutable), output_types: vec!(config::OutputTypeExe), - no_trans: no_run, externs: externs, cg: config::CodegenOptions { prefer_dynamic: true, @@ -170,14 +169,18 @@ fn runtest(test: &str, cratename: &str, libs: SearchPaths, diagnostic::mk_span_handler(diagnostic_handler, codemap); let sess = session::build_session_(sessopts, - None, - span_diagnostic_handler); + None, + span_diagnostic_handler); let outdir = TempDir::new("rustdoctest").ok().expect("rustdoc needs a tempdir"); let out = Some(outdir.path().clone()); let cfg = config::build_configuration(&sess); let libdir = sess.target_filesearch(PathKind::All).get_lib_path(); - driver::compile_input(sess, cfg, &input, &out, &None, None); + let mut control = driver::CompileController::basic(); + if no_run { + control.after_analysis.stop = true; + } + driver::compile_input(sess, cfg, &input, &out, &None, None, control); if no_run { return } diff --git a/src/test/run-make/issue-19371/foo.rs b/src/test/run-make/issue-19371/foo.rs index 8a0c14d2d7e36..fe7df6411599c 100644 --- a/src/test/run-make/issue-19371/foo.rs +++ b/src/test/run-make/issue-19371/foo.rs @@ -14,7 +14,7 @@ extern crate syntax; use rustc::session::{build_session, Session}; use rustc::session::config::{basic_options, build_configuration, Input, OutputTypeExe}; -use rustc_driver::driver::{compile_input}; +use rustc_driver::driver::{compile_input, CompileController}; use syntax::diagnostics::registry::Registry; fn main() { @@ -52,11 +52,13 @@ fn basic_sess(sysroot: Path) -> Session { fn compile(code: String, output: Path, sysroot: Path) { let sess = basic_sess(sysroot); let cfg = build_configuration(&sess); + let control = CompileController::basic(); compile_input(sess, cfg, &Input::Str(code), &None, &Some(output), - None); + None, + control); }