Skip to content

Commit 85f015e

Browse files
committed
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.
1 parent 4418664 commit 85f015e

File tree

5 files changed

+66
-17
lines changed

5 files changed

+66
-17
lines changed

src/librustc/driver/config.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -577,7 +577,7 @@ pub fn optgroups() -> Vec<getopts::OptGroup> {
577577
always = always colorize output;
578578
never = never colorize output", "auto|always|never"),
579579
optmulti("", "extern", "Specify where an external rust library is located",
580-
"PATH"),
580+
"NAME=PATH"),
581581
)
582582
}
583583

src/librustdoc/core.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,10 @@ pub struct CrateAnalysis {
7777
pub inlined: RefCell<Option<HashSet<ast::DefId>>>,
7878
}
7979

80+
pub type Externs = HashMap<String, Vec<String>>;
81+
8082
/// Parses, resolves, and typechecks the given crate
81-
fn get_ast_and_resolve(cpath: &Path, libs: HashSet<Path>, cfgs: Vec<String>)
83+
fn get_ast_and_resolve(cpath: &Path, libs: HashSet<Path>, cfgs: Vec<String>, externs: Externs)
8284
-> (DocContext, CrateAnalysis) {
8385
use syntax::codemap::dummy_spanned;
8486
use rustc::driver::driver::{FileInput,
@@ -96,6 +98,7 @@ fn get_ast_and_resolve(cpath: &Path, libs: HashSet<Path>, cfgs: Vec<String>)
9698
addl_lib_search_paths: RefCell::new(libs),
9799
crate_types: vec!(driver::config::CrateTypeRlib),
98100
lint_opts: vec!((warning_lint, lint::Allow)),
101+
externs: externs,
99102
..rustc::driver::config::basic_options().clone()
100103
};
101104

@@ -148,9 +151,9 @@ fn get_ast_and_resolve(cpath: &Path, libs: HashSet<Path>, cfgs: Vec<String>)
148151
})
149152
}
150153

151-
pub fn run_core(libs: HashSet<Path>, cfgs: Vec<String>, path: &Path)
154+
pub fn run_core(libs: HashSet<Path>, cfgs: Vec<String>, externs: Externs, path: &Path)
152155
-> (clean::Crate, CrateAnalysis) {
153-
let (ctxt, analysis) = get_ast_and_resolve(path, libs, cfgs);
156+
let (ctxt, analysis) = get_ast_and_resolve(path, libs, cfgs, externs);
154157
let ctxt = box(GC) ctxt;
155158
super::ctxtkey.replace(Some(ctxt));
156159

src/librustdoc/lib.rs

Lines changed: 44 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ extern crate time;
3030
use std::io;
3131
use std::io::{File, MemWriter};
3232
use std::gc::Gc;
33+
use std::collections::HashMap;
3334
use serialize::{json, Decodable, Encodable};
3435
use externalfiles::ExternalHtml;
3536

@@ -104,6 +105,7 @@ pub fn opts() -> Vec<getopts::OptGroup> {
104105
optmulti("L", "library-path", "directory to add to crate search path",
105106
"DIR"),
106107
optmulti("", "cfg", "pass a --cfg to rustc", ""),
108+
optmulti("", "extern", "pass an --extern to rustc", "NAME=PATH"),
107109
optmulti("", "plugin-path", "directory to load plugins from", "DIR"),
108110
optmulti("", "passes", "space separated list of passes to also run, a \
109111
value of `list` will print available passes",
@@ -170,6 +172,13 @@ pub fn main_args(args: &[String]) -> int {
170172
let input = matches.free[0].as_slice();
171173

172174
let libs = matches.opt_strs("L").iter().map(|s| Path::new(s.as_slice())).collect();
175+
let externs = match parse_externs(&matches) {
176+
Ok(ex) => ex,
177+
Err(err) => {
178+
println!("{}", err);
179+
return 1;
180+
}
181+
};
173182

174183
let test_args = matches.opt_strs("test-args");
175184
let test_args: Vec<String> = test_args.iter()
@@ -193,10 +202,10 @@ pub fn main_args(args: &[String]) -> int {
193202

194203
match (should_test, markdown_input) {
195204
(true, true) => {
196-
return markdown::test(input, libs, test_args)
205+
return markdown::test(input, libs, externs, test_args)
197206
}
198207
(true, false) => {
199-
return test::run(input, cfgs, libs, test_args)
208+
return test::run(input, cfgs, libs, externs, test_args)
200209
}
201210
(false, true) => return markdown::render(input, output.unwrap_or(Path::new("doc")),
202211
&matches, &external_html),
@@ -215,7 +224,7 @@ pub fn main_args(args: &[String]) -> int {
215224
return 0;
216225
}
217226

218-
let (krate, res) = match acquire_input(input, &matches) {
227+
let (krate, res) = match acquire_input(input, externs, &matches) {
219228
Ok(pair) => pair,
220229
Err(s) => {
221230
println!("input error: {}", s);
@@ -252,27 +261,53 @@ pub fn main_args(args: &[String]) -> int {
252261
/// Looks inside the command line arguments to extract the relevant input format
253262
/// and files and then generates the necessary rustdoc output for formatting.
254263
fn acquire_input(input: &str,
264+
externs: core::Externs,
255265
matches: &getopts::Matches) -> Result<Output, String> {
256266
match matches.opt_str("r").as_ref().map(|s| s.as_slice()) {
257-
Some("rust") => Ok(rust_input(input, matches)),
267+
Some("rust") => Ok(rust_input(input, externs, matches)),
258268
Some("json") => json_input(input),
259269
Some(s) => Err(format!("unknown input format: {}", s)),
260270
None => {
261271
if input.ends_with(".json") {
262272
json_input(input)
263273
} else {
264-
Ok(rust_input(input, matches))
274+
Ok(rust_input(input, externs, matches))
265275
}
266276
}
267277
}
268278
}
269279

280+
/// Extracts `--extern CRATE=PATH` arguments from `matches` and
281+
/// returns a `HashMap` mapping crate names to their paths or else an
282+
/// error message.
283+
fn parse_externs(matches: &getopts::Matches) -> Result<core::Externs, String> {
284+
let mut externs = HashMap::new();
285+
for arg in matches.opt_strs("extern").iter() {
286+
let mut parts = arg.as_slice().splitn('=', 1);
287+
let name = match parts.next() {
288+
Some(s) => s,
289+
None => {
290+
return Err("--extern value must not be empty".to_string());
291+
}
292+
};
293+
let location = match parts.next() {
294+
Some(s) => s,
295+
None => {
296+
return Err("--extern value must be of the format `foo=bar`".to_string());
297+
}
298+
};
299+
let locs = externs.find_or_insert(name.to_string(), Vec::new());
300+
locs.push(location.to_string());
301+
}
302+
Ok(externs)
303+
}
304+
270305
/// Interprets the input file as a rust source file, passing it through the
271306
/// compiler all the way through the analysis passes. The rustdoc output is then
272307
/// generated from the cleaned AST of the crate.
273308
///
274309
/// This form of input will run all of the plug/cleaning passes
275-
fn rust_input(cratefile: &str, matches: &getopts::Matches) -> Output {
310+
fn rust_input(cratefile: &str, externs: core::Externs, matches: &getopts::Matches) -> Output {
276311
let mut default_passes = !matches.opt_present("no-defaults");
277312
let mut passes = matches.opt_strs("passes");
278313
let mut plugins = matches.opt_strs("plugins");
@@ -283,12 +318,14 @@ fn rust_input(cratefile: &str, matches: &getopts::Matches) -> Output {
283318
.map(|s| Path::new(s.as_slice()))
284319
.collect();
285320
let cfgs = matches.opt_strs("cfg");
321+
286322
let cr = Path::new(cratefile);
287323
info!("starting to run rustc");
288324
let (krate, analysis) = std::task::try(proc() {
289325
let cr = cr;
290-
core::run_core(libs.move_iter().map(|x| x.clone()).collect(),
326+
core::run_core(libs.move_iter().collect(),
291327
cfgs,
328+
externs,
292329
&cr)
293330
}).map_err(|boxed_any|format!("{:?}", boxed_any)).unwrap();
294331
info!("finished with rustc");

src/librustdoc/markdown.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use std::collections::HashSet;
1212
use std::io;
1313
use std::string::String;
1414

15+
use core;
1516
use getopts;
1617
use testing;
1718

@@ -129,10 +130,11 @@ pub fn render(input: &str, mut output: Path, matches: &getopts::Matches,
129130
}
130131

131132
/// Run any tests/code examples in the markdown file `input`.
132-
pub fn test(input: &str, libs: HashSet<Path>, mut test_args: Vec<String>) -> int {
133+
pub fn test(input: &str, libs: HashSet<Path>, externs: core::Externs,
134+
mut test_args: Vec<String>) -> int {
133135
let input_str = load_or_return!(input, 1, 2);
134136

135-
let mut collector = Collector::new(input.to_string(), libs, true);
137+
let mut collector = Collector::new(input.to_string(), libs, externs, true);
136138
find_testable_code(input_str.as_slice(), &mut collector);
137139
test_args.unshift("rustdoctest".to_string());
138140
testing::test_main(test_args.as_slice(), collector.tests);

src/librustdoc/test.rs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ use visit_ast::RustdocVisitor;
4040
pub fn run(input: &str,
4141
cfgs: Vec<String>,
4242
libs: HashSet<Path>,
43+
externs: core::Externs,
4344
mut test_args: Vec<String>)
4445
-> int {
4546
let input_path = Path::new(input);
@@ -49,10 +50,10 @@ pub fn run(input: &str,
4950
maybe_sysroot: Some(os::self_exe_path().unwrap().dir_path()),
5051
addl_lib_search_paths: RefCell::new(libs.clone()),
5152
crate_types: vec!(config::CrateTypeDylib),
53+
externs: externs.clone(),
5254
..config::basic_options().clone()
5355
};
5456

55-
5657
let codemap = CodeMap::new();
5758
let diagnostic_handler = diagnostic::default_handler(diagnostic::Auto, None);
5859
let span_diagnostic_handler =
@@ -92,6 +93,7 @@ pub fn run(input: &str,
9293

9394
let mut collector = Collector::new(krate.name.to_string(),
9495
libs,
96+
externs,
9597
false);
9698
collector.fold_crate(krate);
9799

@@ -102,8 +104,8 @@ pub fn run(input: &str,
102104
0
103105
}
104106

105-
fn runtest(test: &str, cratename: &str, libs: HashSet<Path>, should_fail: bool,
106-
no_run: bool, as_test_harness: bool) {
107+
fn runtest(test: &str, cratename: &str, libs: HashSet<Path>, externs: core::Externs,
108+
should_fail: bool, no_run: bool, as_test_harness: bool) {
107109
// the test harness wants its own `main` & top level functions, so
108110
// never wrap the test in `fn main() { ... }`
109111
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,
115117
crate_types: vec!(config::CrateTypeExecutable),
116118
output_types: vec!(link::OutputTypeExe),
117119
no_trans: no_run,
120+
externs: externs,
118121
cg: config::CodegenOptions {
119122
prefer_dynamic: true,
120123
.. config::basic_codegen_options()
@@ -237,19 +240,21 @@ pub struct Collector {
237240
pub tests: Vec<testing::TestDescAndFn>,
238241
names: Vec<String>,
239242
libs: HashSet<Path>,
243+
externs: core::Externs,
240244
cnt: uint,
241245
use_headers: bool,
242246
current_header: Option<String>,
243247
cratename: String,
244248
}
245249

246250
impl Collector {
247-
pub fn new(cratename: String, libs: HashSet<Path>,
251+
pub fn new(cratename: String, libs: HashSet<Path>, externs: core::Externs,
248252
use_headers: bool) -> Collector {
249253
Collector {
250254
tests: Vec::new(),
251255
names: Vec::new(),
252256
libs: libs,
257+
externs: externs,
253258
cnt: 0,
254259
use_headers: use_headers,
255260
current_header: None,
@@ -267,6 +272,7 @@ impl Collector {
267272
};
268273
self.cnt += 1;
269274
let libs = self.libs.clone();
275+
let externs = self.externs.clone();
270276
let cratename = self.cratename.to_string();
271277
debug!("Creating test {}: {}", name, test);
272278
self.tests.push(testing::TestDescAndFn {
@@ -279,6 +285,7 @@ impl Collector {
279285
runtest(test.as_slice(),
280286
cratename.as_slice(),
281287
libs,
288+
externs,
282289
should_fail,
283290
no_run,
284291
as_test_harness);

0 commit comments

Comments
 (0)