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_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<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
// 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<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)
-> 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<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
/// 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

View File

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

View File

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

View File

@ -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<Path>) {
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"),
},
};

View File

@ -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<String>, 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(),

View File

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

View File

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

View File

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