Skip to content

Add support for loading plugins via command line (fixes #15446) #20032

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Jan 8, 2015
13 changes: 13 additions & 0 deletions src/compiletest/header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ pub struct TestProps {
pub pretty_compare_only: bool,
// Patterns which must not appear in the output of a cfail test.
pub forbid_output: Vec<String>,
// Ignore errors which originate from a command line span
pub ignore_command_line: bool,
}

// Load any test directives embedded in the file
Expand All @@ -60,6 +62,8 @@ pub fn load_props(testfile: &Path) -> TestProps {
let mut pretty_mode = None;
let mut pretty_compare_only = false;
let mut forbid_output = Vec::new();
let mut ignore_command_line = false;

iter_header(testfile, |ln| {
match parse_error_pattern(ln) {
Some(ep) => error_patterns.push(ep),
Expand Down Expand Up @@ -102,6 +106,10 @@ pub fn load_props(testfile: &Path) -> TestProps {
pretty_compare_only = parse_pretty_compare_only(ln);
}

if !ignore_command_line {
ignore_command_line = parse_ignore_command_line(ln);
}

match parse_aux_build(ln) {
Some(ab) => { aux_builds.push(ab); }
None => {}
Expand Down Expand Up @@ -140,6 +148,7 @@ pub fn load_props(testfile: &Path) -> TestProps {
pretty_mode: pretty_mode.unwrap_or("normal".to_string()),
pretty_compare_only: pretty_compare_only,
forbid_output: forbid_output,
ignore_command_line: ignore_command_line,
}
}

Expand Down Expand Up @@ -291,6 +300,10 @@ fn parse_pretty_compare_only(line: &str) -> bool {
parse_name_directive(line, "pretty-compare-only")
}

fn parse_ignore_command_line(line: &str) -> bool {
parse_name_directive(line, "ignore-command-line")
}

fn parse_exec_env(line: &str) -> Option<(String, String)> {
parse_name_value_directive(line, "exec-env").map(|nv| {
// nv is either FOO or FOO=BAR
Expand Down
10 changes: 8 additions & 2 deletions src/compiletest/runtest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ fn run_cfail_test(config: &Config, props: &TestProps, testfile: &Path) {
if !props.error_patterns.is_empty() {
fatal("both error pattern and expected errors specified");
}
check_expected_errors(expected_errors, testfile, &proc_res);
check_expected_errors(props, expected_errors, testfile, &proc_res);
} else {
check_error_patterns(props, testfile, output_to_check.as_slice(), &proc_res);
}
Expand Down Expand Up @@ -941,7 +941,8 @@ fn check_forbid_output(props: &TestProps,
}
}

fn check_expected_errors(expected_errors: Vec<errors::ExpectedError> ,
fn check_expected_errors(props: &TestProps,
expected_errors: Vec<errors::ExpectedError> ,
testfile: &Path,
proc_res: &ProcRes) {

Expand Down Expand Up @@ -996,6 +997,11 @@ fn check_expected_errors(expected_errors: Vec<errors::ExpectedError> ,
was_expected = true;
}

if line.starts_with("<command line option>") &&
props.ignore_command_line {
was_expected = true;
}

if !was_expected && is_compiler_error_or_warning(line) {
fatal_proc_rec(format!("unexpected compiler error or warning: '{}'",
line).as_slice(),
Expand Down
30 changes: 24 additions & 6 deletions src/librustc/metadata/creader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ use syntax::ast;
use syntax::abi;
use syntax::attr;
use syntax::attr::AttrMetaMethods;
use syntax::codemap::{Span, mk_sp};
use syntax::codemap::{COMMAND_LINE_SP, Span, mk_sp};
use syntax::parse;
use syntax::parse::token::InternedString;
use syntax::parse::token;
Expand Down Expand Up @@ -445,8 +445,20 @@ impl<'a> CrateReader<'a> {
}

pub fn read_plugin_metadata<'b>(&'b mut self,
vi: &'b ast::ViewItem) -> PluginMetadata<'b> {
let info = self.extract_crate_info(vi).unwrap();
krate: CrateOrString<'b>) -> PluginMetadata<'b> {
let (info, span) = match krate {
CrateOrString::Krate(c) => {
(self.extract_crate_info(c).unwrap(), c.span)
}
CrateOrString::Str(s) => {
(CrateInfo {
name: s.to_string(),
ident: s.to_string(),
id: ast::DUMMY_NODE_ID,
should_link: true,
}, COMMAND_LINE_SP)
}
};
let target_triple = &self.sess.opts.target_triple[];
let is_cross = target_triple != config::host_triple();
let mut should_link = info.should_link && !is_cross;
Expand All @@ -455,7 +467,7 @@ impl<'a> CrateReader<'a> {
let name = info.name.clone();
let mut load_ctxt = loader::Context {
sess: self.sess,
span: vi.span,
span: span,
ident: &ident[],
crate_name: &name[],
hash: None,
Expand Down Expand Up @@ -486,7 +498,7 @@ impl<'a> CrateReader<'a> {
let metadata = if register {
// Register crate now to avoid double-reading metadata
let (_, cmd, _) = self.register_crate(&None, &info.ident[],
&info.name[], vi.span, library);
&info.name[], span, library);
PMDSource::Registered(cmd)
} else {
// Not registering the crate; just hold on to the metadata
Expand All @@ -498,12 +510,18 @@ impl<'a> CrateReader<'a> {
metadata: metadata,
dylib: dylib,
info: info,
vi_span: vi.span,
vi_span: span,
target_only: target_only,
}
}
}

#[derive(Copy)]
pub enum CrateOrString<'a> {
Krate(&'a ast::ViewItem),
Str(&'a str)
}

impl<'a> PluginMetadata<'a> {
/// Read exported macros
pub fn exported_macros(&self) -> Vec<ast::MacroDef> {
Expand Down
3 changes: 1 addition & 2 deletions src/librustc/middle/infer/region_inference/graphviz.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ use middle::ty;
use super::Constraint;
use middle::infer::SubregionOrigin;
use middle::infer::region_inference::RegionVarBindings;
use session::config;
use util::nodemap::{FnvHashMap, FnvHashSet};
use util::ppaux::Repr;

Expand Down Expand Up @@ -55,7 +54,7 @@ pub fn maybe_print_constraints_for<'a, 'tcx>(region_vars: &RegionVarBindings<'a,
subject_node: ast::NodeId) {
let tcx = region_vars.tcx;

if !region_vars.tcx.sess.debugging_opt(config::PRINT_REGION_GRAPH) {
if !region_vars.tcx.sess.opts.debugging_opts.print_region_graph {
return;
}

Expand Down
91 changes: 56 additions & 35 deletions src/librustc/plugin/load.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
//! Used by `rustc` when loading a plugin, or a crate with exported macros.

use session::Session;
use metadata::creader::CrateReader;
use metadata::creader::{CrateOrString, CrateReader};
use plugin::registry::Registry;

use std::mem;
Expand Down Expand Up @@ -44,11 +44,11 @@ pub struct Plugins {
pub registrars: Vec<PluginRegistrar>,
}

struct PluginLoader<'a> {
pub struct PluginLoader<'a> {
sess: &'a Session,
span_whitelist: HashSet<Span>,
reader: CrateReader<'a>,
plugins: Plugins,
pub plugins: Plugins,
}

impl<'a> PluginLoader<'a> {
Expand All @@ -67,7 +67,7 @@ impl<'a> PluginLoader<'a> {

/// Read plugin metadata and dynamically load registrar functions.
pub fn load_plugins(sess: &Session, krate: &ast::Crate,
addl_plugins: Option<Plugins>) -> Plugins {
addl_plugins: Option<Vec<String>>) -> Plugins {
let mut loader = PluginLoader::new(sess);

// We need to error on `#[macro_use] extern crate` when it isn't at the
Expand All @@ -79,19 +79,14 @@ pub fn load_plugins(sess: &Session, krate: &ast::Crate,

visit::walk_crate(&mut loader, krate);

let mut plugins = loader.plugins;

match addl_plugins {
Some(addl_plugins) => {
// Add in the additional plugins requested by the frontend
let Plugins { macros: addl_macros, registrars: addl_registrars } = addl_plugins;
plugins.macros.extend(addl_macros.into_iter());
plugins.registrars.extend(addl_registrars.into_iter());
if let Some(plugins) = addl_plugins {
for plugin in plugins.iter() {
loader.load_plugin(CrateOrString::Str(plugin.as_slice()),
None, None, None)
}
None => ()
}

return plugins;
return loader.plugins;
}

// note that macros aren't expanded yet, and therefore macros can't add plugins.
Expand Down Expand Up @@ -160,22 +155,39 @@ impl<'a, 'v> Visitor<'v> for PluginLoader<'a> {
}
}

self.load_plugin(CrateOrString::Krate(vi), plugin_attr, macro_selection, Some(reexport))
}

fn visit_mac(&mut self, _: &ast::Mac) {
// bummer... can't see plugins inside macros.
// do nothing.
}
}

impl<'a> PluginLoader<'a> {
pub fn load_plugin<'b>(&mut self,
c: CrateOrString<'b>,
plugin_attr: Option<P<ast::MetaItem>>,
macro_selection: Option<HashSet<token::InternedString>>,
reexport: Option<HashSet<token::InternedString>>) {
let mut macros = vec![];
let mut registrar = None;

let load_macros = match macro_selection.as_ref() {
Some(sel) => sel.len() != 0 || reexport.len() != 0,
None => true,
let load_macros = match (macro_selection.as_ref(), reexport.as_ref()) {
(Some(sel), Some(re)) => sel.len() != 0 || re.len() != 0,
_ => true,
};
let load_registrar = plugin_attr.is_some();

if load_macros && !self.span_whitelist.contains(&vi.span) {
self.sess.span_err(vi.span, "an `extern crate` loading macros must be at \
the crate root");
}
if let CrateOrString::Krate(vi) = c {
if load_macros && !self.span_whitelist.contains(&vi.span) {
self.sess.span_err(vi.span, "an `extern crate` loading macros must be at \
the crate root");
}
}

if load_macros || load_registrar {
let pmd = self.reader.read_plugin_metadata(vi);
let pmd = self.reader.read_plugin_metadata(c);
if load_macros {
macros = pmd.exported_macros();
}
Expand All @@ -190,29 +202,26 @@ impl<'a, 'v> Visitor<'v> for PluginLoader<'a> {
None => true,
Some(sel) => sel.contains(&name),
};
def.export = reexport.contains(&name);
def.export = if let Some(ref re) = reexport {
re.contains(&name)
} else {
false // Don't reexport macros from crates loaded from the command line
};
self.plugins.macros.push(def);
}

if let Some((lib, symbol)) = registrar {
let fun = self.dylink_registrar(vi, lib, symbol);
let fun = self.dylink_registrar(c, lib, symbol);
self.plugins.registrars.push(PluginRegistrar {
fun: fun,
args: plugin_attr.unwrap(),
});
}
}

fn visit_mac(&mut self, _: &ast::Mac) {
// bummer... can't see plugins inside macros.
// do nothing.
}
}

impl<'a> PluginLoader<'a> {
// Dynamically link a registrar function into the compiler process.
fn dylink_registrar(&mut self,
vi: &ast::ViewItem,
fn dylink_registrar<'b>(&mut self,
c: CrateOrString<'b>,
path: Path,
symbol: String) -> PluginRegistrarFun {
// Make sure the path contains a / or the linker will search for it.
Expand All @@ -223,7 +232,13 @@ impl<'a> PluginLoader<'a> {
// this is fatal: there are almost certainly macros we need
// inside this crate, so continue would spew "macro undefined"
// errors
Err(err) => self.sess.span_fatal(vi.span, &err[])
Err(err) => {
if let CrateOrString::Krate(cr) = c {
self.sess.span_fatal(cr.span, &err[])
} else {
self.sess.fatal(&err[])
}
}
};

unsafe {
Expand All @@ -233,7 +248,13 @@ impl<'a> PluginLoader<'a> {
mem::transmute::<*mut u8,PluginRegistrarFun>(registrar)
}
// again fatal if we can't register macros
Err(err) => self.sess.span_fatal(vi.span, &err[])
Err(err) => {
if let CrateOrString::Krate(cr) = c {
self.sess.span_fatal(cr.span, &err[])
} else {
self.sess.fatal(&err[])
}
}
};

// Intentionally leak the dynamic library. We can't ever unload it
Expand Down
Loading