about summary refs log tree commit diff
diff options
context:
space:
mode:
authorTom Jakubowski <tom@crystae.net>2014-07-19 23:02:14 -0700
committerAlex Crichton <alex@alexcrichton.com>2014-07-21 09:54:29 -0700
commitec70f2bb6e8cd3dd187abba351e6cf9eb14a37c1 (patch)
tree0f181e1bdc3cc89102421429f7b86199f0a51673
parent97ca98f5ccda65589049397723662e634ada04a4 (diff)
downloadrust-ec70f2bb6e8cd3dd187abba351e6cf9eb14a37c1.tar.gz
rust-ec70f2bb6e8cd3dd187abba351e6cf9eb14a37c1.zip
rustdoc: Add an --extern flag analagous to rustc's
This adds an `--extern` flag to `rustdoc` much like the compiler's to
specify the path where a given crate can be found.
-rw-r--r--src/librustc/driver/config.rs2
-rw-r--r--src/librustdoc/core.rs9
-rw-r--r--src/librustdoc/lib.rs51
-rw-r--r--src/librustdoc/markdown.rs6
-rw-r--r--src/librustdoc/test.rs15
5 files changed, 66 insertions, 17 deletions
diff --git a/src/librustc/driver/config.rs b/src/librustc/driver/config.rs
index 2d200c82a94..dd951d963e0 100644
--- a/src/librustc/driver/config.rs
+++ b/src/librustc/driver/config.rs
@@ -582,7 +582,7 @@ pub fn optgroups() -> Vec<getopts::OptGroup> {
             always = always colorize output;
             never  = never colorize output", "auto|always|never"),
         optmulti("", "extern", "Specify where an external rust library is located",
-                 "PATH"),
+                 "NAME=PATH"),
     )
 }
 
diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs
index b1c715ae5b3..7f021510f4a 100644
--- a/src/librustdoc/core.rs
+++ b/src/librustdoc/core.rs
@@ -77,8 +77,10 @@ pub struct CrateAnalysis {
     pub inlined: RefCell<Option<HashSet<ast::DefId>>>,
 }
 
+pub type Externs = HashMap<String, Vec<String>>;
+
 /// Parses, resolves, and typechecks the given crate
-fn get_ast_and_resolve(cpath: &Path, libs: HashSet<Path>, cfgs: Vec<String>)
+fn get_ast_and_resolve(cpath: &Path, libs: HashSet<Path>, cfgs: Vec<String>, externs: Externs)
                        -> (DocContext, CrateAnalysis) {
     use syntax::codemap::dummy_spanned;
     use rustc::driver::driver::{FileInput,
@@ -96,6 +98,7 @@ fn get_ast_and_resolve(cpath: &Path, libs: HashSet<Path>, cfgs: Vec<String>)
         addl_lib_search_paths: RefCell::new(libs),
         crate_types: vec!(driver::config::CrateTypeRlib),
         lint_opts: vec!((warning_lint, lint::Allow)),
+        externs: externs,
         ..rustc::driver::config::basic_options().clone()
     };
 
@@ -148,9 +151,9 @@ fn get_ast_and_resolve(cpath: &Path, libs: HashSet<Path>, cfgs: Vec<String>)
     })
 }
 
-pub fn run_core(libs: HashSet<Path>, cfgs: Vec<String>, path: &Path)
+pub fn run_core(libs: HashSet<Path>, cfgs: Vec<String>, externs: Externs, path: &Path)
                 -> (clean::Crate, CrateAnalysis) {
-    let (ctxt, analysis) = get_ast_and_resolve(path, libs, cfgs);
+    let (ctxt, analysis) = get_ast_and_resolve(path, libs, cfgs, externs);
     let ctxt = box(GC) ctxt;
     super::ctxtkey.replace(Some(ctxt));
 
diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs
index 245dc9a0a34..a7c9ac10118 100644
--- a/src/librustdoc/lib.rs
+++ b/src/librustdoc/lib.rs
@@ -30,6 +30,7 @@ extern crate time;
 use std::io;
 use std::io::{File, MemWriter};
 use std::gc::Gc;
+use std::collections::HashMap;
 use serialize::{json, Decodable, Encodable};
 use externalfiles::ExternalHtml;
 
@@ -104,6 +105,7 @@ pub fn opts() -> Vec<getopts::OptGroup> {
         optmulti("L", "library-path", "directory to add to crate search path",
                  "DIR"),
         optmulti("", "cfg", "pass a --cfg to rustc", ""),
+        optmulti("", "extern", "pass an --extern to rustc", "NAME=PATH"),
         optmulti("", "plugin-path", "directory to load plugins from", "DIR"),
         optmulti("", "passes", "space separated list of passes to also run, a \
                                 value of `list` will print available passes",
@@ -170,6 +172,13 @@ pub fn main_args(args: &[String]) -> int {
     let input = matches.free[0].as_slice();
 
     let libs = matches.opt_strs("L").iter().map(|s| Path::new(s.as_slice())).collect();
+    let externs = match parse_externs(&matches) {
+        Ok(ex) => ex,
+        Err(err) => {
+            println!("{}", err);
+            return 1;
+        }
+    };
 
     let test_args = matches.opt_strs("test-args");
     let test_args: Vec<String> = test_args.iter()
@@ -193,10 +202,10 @@ pub fn main_args(args: &[String]) -> int {
 
     match (should_test, markdown_input) {
         (true, true) => {
-            return markdown::test(input, libs, test_args)
+            return markdown::test(input, libs, externs, test_args)
         }
         (true, false) => {
-            return test::run(input, cfgs, libs, test_args)
+            return test::run(input, cfgs, libs, externs, test_args)
         }
         (false, true) => return markdown::render(input, output.unwrap_or(Path::new("doc")),
                                                  &matches, &external_html),
@@ -215,7 +224,7 @@ pub fn main_args(args: &[String]) -> int {
         return 0;
     }
 
-    let (krate, res) = match acquire_input(input, &matches) {
+    let (krate, res) = match acquire_input(input, externs, &matches) {
         Ok(pair) => pair,
         Err(s) => {
             println!("input error: {}", s);
@@ -252,27 +261,53 @@ pub fn main_args(args: &[String]) -> int {
 /// Looks inside the command line arguments to extract the relevant input format
 /// and files and then generates the necessary rustdoc output for formatting.
 fn acquire_input(input: &str,
+                 externs: core::Externs,
                  matches: &getopts::Matches) -> Result<Output, String> {
     match matches.opt_str("r").as_ref().map(|s| s.as_slice()) {
-        Some("rust") => Ok(rust_input(input, matches)),
+        Some("rust") => Ok(rust_input(input, externs, matches)),
         Some("json") => json_input(input),
         Some(s) => Err(format!("unknown input format: {}", s)),
         None => {
             if input.ends_with(".json") {
                 json_input(input)
             } else {
-                Ok(rust_input(input, matches))
+                Ok(rust_input(input, externs, matches))
             }
         }
     }
 }
 
+/// Extracts `--extern CRATE=PATH` arguments from `matches` and
+/// returns a `HashMap` mapping crate names to their paths or else an
+/// error message.
+fn parse_externs(matches: &getopts::Matches) -> Result<core::Externs, String> {
+    let mut externs = HashMap::new();
+    for arg in matches.opt_strs("extern").iter() {
+        let mut parts = arg.as_slice().splitn('=', 1);
+        let name = match parts.next() {
+            Some(s) => s,
+            None => {
+                return Err("--extern value must not be empty".to_string());
+            }
+        };
+        let location = match parts.next() {
+            Some(s) => s,
+            None => {
+                return Err("--extern value must be of the format `foo=bar`".to_string());
+            }
+        };
+        let locs = externs.find_or_insert(name.to_string(), Vec::new());
+        locs.push(location.to_string());
+    }
+    Ok(externs)
+}
+
 /// Interprets the input file as a rust source file, passing it through the
 /// compiler all the way through the analysis passes. The rustdoc output is then
 /// generated from the cleaned AST of the crate.
 ///
 /// This form of input will run all of the plug/cleaning passes
-fn rust_input(cratefile: &str, matches: &getopts::Matches) -> Output {
+fn rust_input(cratefile: &str, externs: core::Externs, matches: &getopts::Matches) -> Output {
     let mut default_passes = !matches.opt_present("no-defaults");
     let mut passes = matches.opt_strs("passes");
     let mut plugins = matches.opt_strs("plugins");
@@ -283,12 +318,14 @@ fn rust_input(cratefile: &str, matches: &getopts::Matches) -> Output {
                                  .map(|s| Path::new(s.as_slice()))
                                  .collect();
     let cfgs = matches.opt_strs("cfg");
+
     let cr = Path::new(cratefile);
     info!("starting to run rustc");
     let (krate, analysis) = std::task::try(proc() {
         let cr = cr;
-        core::run_core(libs.move_iter().map(|x| x.clone()).collect(),
+        core::run_core(libs.move_iter().collect(),
                        cfgs,
+                       externs,
                        &cr)
     }).map_err(|boxed_any|format!("{:?}", boxed_any)).unwrap();
     info!("finished with rustc");
diff --git a/src/librustdoc/markdown.rs b/src/librustdoc/markdown.rs
index f9bc59888ae..47009c1f2cc 100644
--- a/src/librustdoc/markdown.rs
+++ b/src/librustdoc/markdown.rs
@@ -12,6 +12,7 @@ use std::collections::HashSet;
 use std::io;
 use std::string::String;
 
+use core;
 use getopts;
 use testing;
 
@@ -129,10 +130,11 @@ pub fn render(input: &str, mut output: Path, matches: &getopts::Matches,
 }
 
 /// Run any tests/code examples in the markdown file `input`.
-pub fn test(input: &str, libs: HashSet<Path>, mut test_args: Vec<String>) -> int {
+pub fn test(input: &str, libs: HashSet<Path>, externs: core::Externs,
+            mut test_args: Vec<String>) -> int {
     let input_str = load_or_return!(input, 1, 2);
 
-    let mut collector = Collector::new(input.to_string(), libs, true);
+    let mut collector = Collector::new(input.to_string(), libs, externs, true);
     find_testable_code(input_str.as_slice(), &mut collector);
     test_args.unshift("rustdoctest".to_string());
     testing::test_main(test_args.as_slice(), collector.tests);
diff --git a/src/librustdoc/test.rs b/src/librustdoc/test.rs
index 8fe28d1eab8..2ed35469dfa 100644
--- a/src/librustdoc/test.rs
+++ b/src/librustdoc/test.rs
@@ -40,6 +40,7 @@ use visit_ast::RustdocVisitor;
 pub fn run(input: &str,
            cfgs: Vec<String>,
            libs: HashSet<Path>,
+           externs: core::Externs,
            mut test_args: Vec<String>)
            -> int {
     let input_path = Path::new(input);
@@ -49,10 +50,10 @@ pub fn run(input: &str,
         maybe_sysroot: Some(os::self_exe_path().unwrap().dir_path()),
         addl_lib_search_paths: RefCell::new(libs.clone()),
         crate_types: vec!(config::CrateTypeDylib),
+        externs: externs.clone(),
         ..config::basic_options().clone()
     };
 
-
     let codemap = CodeMap::new();
     let diagnostic_handler = diagnostic::default_handler(diagnostic::Auto, None);
     let span_diagnostic_handler =
@@ -92,6 +93,7 @@ pub fn run(input: &str,
 
     let mut collector = Collector::new(krate.name.to_string(),
                                        libs,
+                                       externs,
                                        false);
     collector.fold_crate(krate);
 
@@ -102,8 +104,8 @@ pub fn run(input: &str,
     0
 }
 
-fn runtest(test: &str, cratename: &str, libs: HashSet<Path>, should_fail: bool,
-           no_run: bool, as_test_harness: bool) {
+fn runtest(test: &str, cratename: &str, libs: HashSet<Path>, externs: core::Externs,
+           should_fail: bool, no_run: bool, as_test_harness: bool) {
     // the test harness wants its own `main` & top level functions, so
     // never wrap the test in `fn main() { ... }`
     let test = maketest(test, Some(cratename), true, as_test_harness);
@@ -115,6 +117,7 @@ fn runtest(test: &str, cratename: &str, libs: HashSet<Path>, should_fail: bool,
         crate_types: vec!(config::CrateTypeExecutable),
         output_types: vec!(link::OutputTypeExe),
         no_trans: no_run,
+        externs: externs,
         cg: config::CodegenOptions {
             prefer_dynamic: true,
             .. config::basic_codegen_options()
@@ -237,6 +240,7 @@ pub struct Collector {
     pub tests: Vec<testing::TestDescAndFn>,
     names: Vec<String>,
     libs: HashSet<Path>,
+    externs: core::Externs,
     cnt: uint,
     use_headers: bool,
     current_header: Option<String>,
@@ -244,12 +248,13 @@ pub struct Collector {
 }
 
 impl Collector {
-    pub fn new(cratename: String, libs: HashSet<Path>,
+    pub fn new(cratename: String, libs: HashSet<Path>, externs: core::Externs,
                use_headers: bool) -> Collector {
         Collector {
             tests: Vec::new(),
             names: Vec::new(),
             libs: libs,
+            externs: externs,
             cnt: 0,
             use_headers: use_headers,
             current_header: None,
@@ -267,6 +272,7 @@ impl Collector {
         };
         self.cnt += 1;
         let libs = self.libs.clone();
+        let externs = self.externs.clone();
         let cratename = self.cratename.to_string();
         debug!("Creating test {}: {}", name, test);
         self.tests.push(testing::TestDescAndFn {
@@ -279,6 +285,7 @@ impl Collector {
                 runtest(test.as_slice(),
                         cratename.as_slice(),
                         libs,
+                        externs,
                         should_fail,
                         no_run,
                         as_test_harness);