Make the compilation process more easily customisable
This commit is contained in:
parent
2e4cef4e78
commit
55d5c46d3a
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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 => {
|
||||
|
@ -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"),
|
||||
},
|
||||
};
|
||||
|
@ -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(),
|
||||
|
@ -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;
|
||||
|
@ -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 }
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user