Make the compilation process more easily customisable

This commit is contained in:
Nick Cameron
2015-01-11 15:03:34 +13:00
parent 2e4cef4e78
commit 55d5c46d3a
8 changed files with 268 additions and 81 deletions

View File

@@ -24,7 +24,6 @@ use rustc_borrowck as borrowck;
use rustc_resolve as resolve; use rustc_resolve as resolve;
use rustc_trans::back::link; use rustc_trans::back::link;
use rustc_trans::back::write; use rustc_trans::back::write;
use rustc_trans::save;
use rustc_trans::trans; use rustc_trans::trans;
use rustc_typeck as typeck; use rustc_typeck as typeck;
@@ -47,23 +46,43 @@ pub fn compile_input(sess: Session,
input: &Input, input: &Input,
outdir: &Option<Path>, outdir: &Option<Path>,
output: &Option<Path>, output: &Option<Path>,
addl_plugins: Option<Vec<String>>) { addl_plugins: Option<Vec<String>>,
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 // 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 // large chunks of memory alive and we want to free them as soon as
// possible to keep the peak memory usage low // possible to keep the peak memory usage low
let (outputs, trans, sess) = { let (outputs, trans, sess) = {
let (outputs, expanded_crate, id) = { let (outputs, expanded_crate, id) = {
let krate = phase_1_parse_input(&sess, cfg, input); 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, let outputs = build_output_filenames(input,
outdir, outdir,
output, output,
&krate.attrs[], &krate.attrs[],
&sess); &sess);
let id = link::find_crate_name(Some(&sess), &krate.attrs[], let id = link::find_crate_name(Some(&sess),
&krate.attrs[],
input); input);
let expanded_crate let expanded_crate
= match phase_2_configure_and_expand(&sess, krate, &id[], = match phase_2_configure_and_expand(&sess,
krate,
&id[],
addl_plugins) { addl_plugins) {
None => return, None => return,
Some(k) => k Some(k) => k
@@ -72,23 +91,37 @@ pub fn compile_input(sess: Session,
(outputs, expanded_crate, id) (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 mut forest = ast_map::Forest::new(expanded_crate);
let ast_map = assign_node_ids_and_map(&sess, &mut forest); let ast_map = assign_node_ids_and_map(&sess, &mut forest);
write_out_deps(&sess, input, &outputs, &id[]); write_out_deps(&sess, input, &outputs, &id[]);
if stop_after_phase_2(&sess) { return; }
let arenas = ty::CtxtArenas::new(); let arenas = ty::CtxtArenas::new();
let analysis = phase_3_run_analysis_passes(sess, ast_map, &arenas, id); let analysis = phase_3_run_analysis_passes(sess,
phase_save_analysis(&analysis.ty_cx.sess, analysis.ty_cx.map.krate(), &analysis, outdir); 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) { if log_enabled!(::log::INFO) {
println!("Pre-trans"); println!("Pre-trans");
analysis.ty_cx.print_debug_stats(); 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); let (tcx, trans) = phase_4_translate_to_llvm(analysis);
if log_enabled!(::log::INFO) { if log_enabled!(::log::INFO) {
@@ -102,7 +135,13 @@ pub fn compile_input(sess: Session,
(outputs, trans, tcx.sess) (outputs, trans, tcx.sess)
}; };
phase_5_run_llvm_passes(&sess, &trans, &outputs); 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); 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<Fn(CompileState) -> () + '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<Path>)
-> 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<Path>,
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<Path>,
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<Path>,
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<Path>,
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) pub fn phase_1_parse_input(sess: &Session, cfg: ast::CrateConfig, input: &Input)
-> ast::Crate { -> ast::Crate {
// These may be left in an incoherent state after a previous compile. // 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, pub fn phase_3_run_analysis_passes<'tcx>(sess: Session,
ast_map: ast_map::Map<'tcx>, ast_map: ast_map::Map<'tcx>,
arenas: &'tcx ty::CtxtArenas<'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 time_passes = sess.time_passes();
let krate = ast_map.krate(); 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", (), |_| let lang_items = time(time_passes, "language item collection", (), |_|
middle::lang_items::collect_language_items(krate, &sess)); 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 { let resolve::CrateMap {
def_map, def_map,
freevars, 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<Path>) {
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 /// Run the translation phase to LLVM, after which the AST and analysis can
/// be discarded. /// be discarded.
pub fn phase_4_translate_to_llvm<'tcx>(analysis: ty::CrateAnalysis<'tcx>) 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); 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 { fn escape_dep_filename(filename: &str) -> String {
// Apparently clang and gcc *only* escape spaces: // Apparently clang and gcc *only* escape spaces:
// http://llvm.org/klaus/clang/commit/9d50634cfc268ecc9a7250226dd5ca0e945240d4 // http://llvm.org/klaus/clang/commit/9d50634cfc268ecc9a7250226dd5ca0e945240d4

View File

@@ -48,7 +48,11 @@ extern crate "rustc_llvm" as llvm;
pub use syntax::diagnostic; pub use syntax::diagnostic;
use driver::CompileController;
use rustc_resolve as resolve;
use rustc_trans::back::link; use rustc_trans::back::link;
use rustc_trans::save;
use rustc::session::{config, Session, build_session}; use rustc::session::{config, Session, build_session};
use rustc::session::config::{Input, PrintRequest, UnstableFeatures}; use rustc::session::config::{Input, PrintRequest, UnstableFeatures};
use rustc::lint::Lint; use rustc::lint::Lint;
@@ -56,6 +60,7 @@ use rustc::lint;
use rustc::metadata; use rustc::metadata;
use rustc::metadata::creader::CrateOrString::Str; use rustc::metadata::creader::CrateOrString::Str;
use rustc::DIAGNOSTICS; use rustc::DIAGNOSTICS;
use rustc::util::common::time;
use std::cmp::Ordering::Equal; use std::cmp::Ordering::Equal;
use std::io; use std::io;
@@ -188,7 +193,43 @@ fn run_compiler(args: &[String]) {
} }
let plugins = sess.opts.debugging_opts.extra_plugins.clone(); 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 { pub fn get_unstable_features_setting() -> UnstableFeatures {

View File

@@ -27,6 +27,7 @@ use rustc::session::config::Input;
use rustc::util::ppaux; use rustc::util::ppaux;
use rustc_borrowck as borrowck; use rustc_borrowck as borrowck;
use rustc_borrowck::graphviz as borrowck_dot; use rustc_borrowck::graphviz as borrowck_dot;
use rustc_resolve as resolve;
use syntax::ast; use syntax::ast;
use syntax::ast_map::{self, blocks, NodePrinter}; use syntax::ast_map::{self, blocks, NodePrinter};
@@ -133,7 +134,11 @@ impl PpSourceMode {
} }
PpmTyped => { PpmTyped => {
let ast_map = ast_map.expect("--pretty=typed missing ast_map"); 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 }; let annotation = TypedAnnotation { analysis: analysis };
f(&annotation, payload) f(&annotation, payload)
} }
@@ -603,7 +608,11 @@ pub fn pretty_print_input(sess: Session,
match code { match code {
Some(code) => { Some(code) => {
let variants = gather_flowgraph_variants(&sess); 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) print_flowgraph(variants, analysis, code, out)
} }
None => { None => {

View File

@@ -1505,7 +1505,7 @@ impl<'l, 'tcx, 'v> Visitor<'v> for DxrVisitor<'l, 'tcx> {
pub fn process_crate(sess: &Session, pub fn process_crate(sess: &Session,
krate: &ast::Crate, krate: &ast::Crate,
analysis: &ty::CrateAnalysis, analysis: &ty::CrateAnalysis,
odir: &Option<Path>) { odir: Option<&Path>) {
if generated_code(krate.span) { if generated_code(krate.span) {
return; return;
} }
@@ -1524,8 +1524,8 @@ pub fn process_crate(sess: &Session,
// find a path to dump our data to // find a path to dump our data to
let mut root_path = match os::getenv("DXR_RUST_TEMP_FOLDER") { let mut root_path = match os::getenv("DXR_RUST_TEMP_FOLDER") {
Some(val) => Path::new(val), Some(val) => Path::new(val),
None => match *odir { None => match odir {
Some(ref val) => val.join("dxr"), Some(val) => val.join("dxr"),
None => Path::new("dxr-temp"), None => Path::new("dxr-temp"),
}, },
}; };

View File

@@ -16,6 +16,7 @@ use rustc::session::search_paths::SearchPaths;
use rustc::middle::{privacy, ty}; use rustc::middle::{privacy, ty};
use rustc::lint; use rustc::lint;
use rustc_trans::back::link; use rustc_trans::back::link;
use rustc_resolve as resolve;
use syntax::{ast, ast_map, codemap, diagnostic}; use syntax::{ast, ast_map, codemap, diagnostic};
@@ -126,7 +127,11 @@ pub fn run_core(search_paths: SearchPaths, cfgs: Vec<String>, externs: Externs,
let arenas = ty::CtxtArenas::new(); let arenas = ty::CtxtArenas::new();
let ty::CrateAnalysis { let ty::CrateAnalysis {
exported_items, public_items, ty_cx, .. 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 { let ctxt = DocContext {
krate: ty_cx.map.krate(), krate: ty_cx.map.krate(),

View File

@@ -27,6 +27,7 @@ extern crate libc;
extern crate rustc; extern crate rustc;
extern crate rustc_trans; extern crate rustc_trans;
extern crate rustc_driver; extern crate rustc_driver;
extern crate rustc_resolve;
extern crate serialize; extern crate serialize;
extern crate syntax; extern crate syntax;
extern crate "test" as testing; extern crate "test" as testing;

View File

@@ -123,7 +123,6 @@ fn runtest(test: &str, cratename: &str, libs: SearchPaths,
search_paths: libs, search_paths: libs,
crate_types: vec!(config::CrateTypeExecutable), crate_types: vec!(config::CrateTypeExecutable),
output_types: vec!(config::OutputTypeExe), output_types: vec!(config::OutputTypeExe),
no_trans: no_run,
externs: externs, externs: externs,
cg: config::CodegenOptions { cg: config::CodegenOptions {
prefer_dynamic: true, prefer_dynamic: true,
@@ -170,14 +169,18 @@ fn runtest(test: &str, cratename: &str, libs: SearchPaths,
diagnostic::mk_span_handler(diagnostic_handler, codemap); diagnostic::mk_span_handler(diagnostic_handler, codemap);
let sess = session::build_session_(sessopts, let sess = session::build_session_(sessopts,
None, None,
span_diagnostic_handler); span_diagnostic_handler);
let outdir = TempDir::new("rustdoctest").ok().expect("rustdoc needs a tempdir"); let outdir = TempDir::new("rustdoctest").ok().expect("rustdoc needs a tempdir");
let out = Some(outdir.path().clone()); let out = Some(outdir.path().clone());
let cfg = config::build_configuration(&sess); let cfg = config::build_configuration(&sess);
let libdir = sess.target_filesearch(PathKind::All).get_lib_path(); 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 } if no_run { return }

View File

@@ -14,7 +14,7 @@ extern crate syntax;
use rustc::session::{build_session, Session}; use rustc::session::{build_session, Session};
use rustc::session::config::{basic_options, build_configuration, Input, OutputTypeExe}; 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; use syntax::diagnostics::registry::Registry;
fn main() { fn main() {
@@ -52,11 +52,13 @@ fn basic_sess(sysroot: Path) -> Session {
fn compile(code: String, output: Path, sysroot: Path) { fn compile(code: String, output: Path, sysroot: Path) {
let sess = basic_sess(sysroot); let sess = basic_sess(sysroot);
let cfg = build_configuration(&sess); let cfg = build_configuration(&sess);
let control = CompileController::basic();
compile_input(sess, compile_input(sess,
cfg, cfg,
&Input::Str(code), &Input::Str(code),
&None, &None,
&Some(output), &Some(output),
None); None,
control);
} }