Skip to content

Commit cb019b6

Browse files
committed
Auto merge of #5687 - ehuss:beta-panic-rustdoc, r=alexcrichton
[BETA] Fix doctests linking too many libs. Backport of #5651.
2 parents e0ed18d + 5bd3910 commit cb019b6

File tree

8 files changed

+123
-106
lines changed

8 files changed

+123
-106
lines changed

src/cargo/core/compiler/compilation.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,21 @@ use core::{Feature, Package, PackageId, Target, TargetKind};
99
use util::{self, join_paths, process, CargoResult, Config, ProcessBuilder};
1010
use super::BuildContext;
1111

12+
pub struct Doctest {
13+
/// The package being doctested.
14+
pub package: Package,
15+
/// The target being tested (currently always the package's lib).
16+
pub target: Target,
17+
/// Extern dependencies needed by `rustdoc`. The path is the location of
18+
/// the compiled lib.
19+
pub deps: Vec<(Target, PathBuf)>,
20+
}
21+
1222
/// A structure returning the result of a compilation.
1323
pub struct Compilation<'cfg> {
1424
/// A mapping from a package to the list of libraries that need to be
1525
/// linked when working with that package.
26+
// TODO: deprecated, remove
1627
pub libraries: HashMap<PackageId, HashSet<(Target, PathBuf)>>,
1728

1829
/// An array of all tests created during this compilation.
@@ -50,7 +61,8 @@ pub struct Compilation<'cfg> {
5061
/// be passed to future invocations of programs.
5162
pub extra_env: HashMap<PackageId, Vec<(String, String)>>,
5263

53-
pub to_doc_test: Vec<Package>,
64+
/// Libraries to test with rustdoc.
65+
pub to_doc_test: Vec<Doctest>,
5466

5567
/// Features per package enabled during this compilation.
5668
pub cfgs: HashMap<PackageId, HashSet<String>>,

src/cargo/core/compiler/context/mod.rs

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#![allow(deprecated)]
22
use std::collections::{HashMap, HashSet};
3+
use std::ffi::OsStr;
34
use std::fmt::Write;
45
use std::path::PathBuf;
56
use std::sync::Arc;
@@ -8,6 +9,7 @@ use std::cmp::Ordering;
89
use jobserver::Client;
910

1011
use core::{Package, PackageId, Resolve, Target};
12+
use core::compiler::compilation;
1113
use core::profiles::Profile;
1214
use util::errors::{CargoResult, CargoResultExt};
1315
use util::{internal, profile, Config, short_hash};
@@ -175,7 +177,7 @@ impl<'a, 'cfg> Context<'a, 'cfg> {
175177
None => &output.path,
176178
};
177179

178-
if unit.mode.is_any_test() && !unit.mode.is_check() {
180+
if unit.mode == CompileMode::Test {
179181
self.compilation.tests.push((
180182
unit.pkg.clone(),
181183
unit.target.kind().clone(),
@@ -227,6 +229,39 @@ impl<'a, 'cfg> Context<'a, 'cfg> {
227229
);
228230
}
229231

232+
if unit.mode == CompileMode::Doctest {
233+
// Note that we can *only* doctest rlib outputs here. A
234+
// staticlib output cannot be linked by the compiler (it just
235+
// doesn't do that). A dylib output, however, can be linked by
236+
// the compiler, but will always fail. Currently all dylibs are
237+
// built as "static dylibs" where the standard library is
238+
// statically linked into the dylib. The doc tests fail,
239+
// however, for now as they try to link the standard library
240+
// dynamically as well, causing problems. As a result we only
241+
// pass `--extern` for rlib deps and skip out on all other
242+
// artifacts.
243+
let mut doctest_deps = Vec::new();
244+
for dep in self.dep_targets(unit) {
245+
if dep.target.is_lib() && dep.mode == CompileMode::Build {
246+
let outputs = self.outputs(&dep)?;
247+
doctest_deps.extend(
248+
outputs
249+
.iter()
250+
.filter(|output| {
251+
output.path.extension() == Some(OsStr::new("rlib"))
252+
|| dep.target.for_host()
253+
})
254+
.map(|output| (dep.target.clone(), output.path.clone())),
255+
);
256+
}
257+
}
258+
self.compilation.to_doc_test.push(compilation::Doctest {
259+
package: unit.pkg.clone(),
260+
target: unit.target.clone(),
261+
deps: doctest_deps,
262+
});
263+
}
264+
230265
let feats = self.bcx.resolve.features(unit.pkg.package_id());
231266
if !feats.is_empty() {
232267
self.compilation

src/cargo/core/compiler/context/unit_dependencies.rs

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -199,19 +199,11 @@ fn compute_deps_custom_build<'a, 'cfg>(
199199
// 1. Compiling the build script itself
200200
// 2. For each immediate dependency of our package which has a `links`
201201
// key, the execution of that build script.
202-
let not_custom_build = unit.pkg
203-
.targets()
202+
let deps = deps
204203
.iter()
205-
.find(|t| !t.is_custom_build())
206-
.unwrap();
207-
let tmp = Unit {
208-
pkg: unit.pkg,
209-
target: not_custom_build,
210-
profile: unit.profile,
211-
kind: unit.kind,
212-
mode: CompileMode::Build,
213-
};
214-
let deps = deps_of(&tmp, bcx, deps, ProfileFor::Any)?;
204+
.find(|(key, _deps)| key.pkg == unit.pkg && !key.target.is_custom_build())
205+
.expect("can't find package deps")
206+
.1;
215207
Ok(deps.iter()
216208
.filter_map(|unit| {
217209
if !unit.target.linkable() || unit.pkg.manifest().links().is_none() {

src/cargo/core/compiler/job_queue.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -433,11 +433,15 @@ impl<'a> JobQueue<'a> {
433433
}
434434
}
435435
}
436-
Fresh if self.counts[key.pkg] == 0 => {
437-
self.compiled.insert(key.pkg);
438-
config.shell().verbose(|c| c.status("Fresh", key.pkg))?;
436+
Fresh => {
437+
// If doctest is last, only print "Fresh" if nothing has been printed.
438+
if self.counts[key.pkg] == 0
439+
&& !(key.mode == CompileMode::Doctest && self.compiled.contains(key.pkg))
440+
{
441+
self.compiled.insert(key.pkg);
442+
config.shell().verbose(|c| c.status("Fresh", key.pkg))?;
443+
}
439444
}
440-
Fresh => {}
441445
}
442446
Ok(())
443447
}

src/cargo/core/compiler/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ use self::output_depinfo::output_depinfo;
2424

2525
pub use self::build_context::{BuildContext, FileFlavor, TargetConfig, TargetInfo};
2626
pub use self::build_config::{BuildConfig, CompileMode, MessageFormat};
27-
pub use self::compilation::Compilation;
27+
pub use self::compilation::{Compilation, Doctest};
2828
pub use self::context::{Context, Unit};
2929
pub use self::custom_build::{BuildMap, BuildOutput, BuildScripts};
3030
pub use self::layout::is_bad_artifact_name;

src/cargo/ops/cargo_compile.rs

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,7 @@ pub fn compile_ws<'a>(
280280
extra_compiler_args = Some((units[0], args));
281281
}
282282

283-
let mut ret = {
283+
let ret = {
284284
let _p = profile::start("compiling");
285285
let bcx = BuildContext::new(
286286
ws,
@@ -295,8 +295,6 @@ pub fn compile_ws<'a>(
295295
cx.compile(&units, export_dir.clone(), &exec)?
296296
};
297297

298-
ret.to_doc_test = to_builds.into_iter().cloned().collect();
299-
300298
return Ok(ret);
301299
}
302300

@@ -541,9 +539,9 @@ fn generate_targets<'a>(
541539
.collect::<Vec<_>>();
542540
proposals.extend(default_units);
543541
if build_config.mode == CompileMode::Test {
544-
// Include the lib as it will be required for doctests.
542+
// Include doctest for lib.
545543
if let Some(t) = pkg.targets().iter().find(|t| t.is_lib() && t.doctested()) {
546-
proposals.push((new_unit(pkg, t, CompileMode::Build), false));
544+
proposals.push((new_unit(pkg, t, CompileMode::Doctest), false));
547545
}
548546
}
549547
}
@@ -681,15 +679,7 @@ fn generate_default_targets(targets: &[Target], mode: CompileMode) -> Vec<&Targe
681679
})
682680
.collect()
683681
}
684-
CompileMode::Doctest => {
685-
// `test --doc``
686-
targets
687-
.iter()
688-
.find(|t| t.is_lib() && t.doctested())
689-
.into_iter()
690-
.collect()
691-
}
692-
CompileMode::RunCustomBuild => panic!("Invalid mode"),
682+
CompileMode::Doctest | CompileMode::RunCustomBuild => panic!("Invalid mode {:?}", mode),
693683
}
694684
}
695685

src/cargo/ops/cargo_test.rs

Lines changed: 51 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
use std::ffi::{OsStr, OsString};
1+
use std::ffi::OsString;
22

33
use ops;
4-
use core::compiler::Compilation;
4+
use core::compiler::{Compilation, Doctest};
55
use util::{self, CargoTestError, ProcessError, Test};
66
use util::errors::CargoResult;
77
use core::Workspace;
@@ -154,86 +154,64 @@ fn run_doc_tests(
154154
return Ok((Test::Doc, errors));
155155
}
156156

157-
let libs = compilation.to_doc_test.iter().map(|package| {
158-
(
157+
for doctest_info in &compilation.to_doc_test {
158+
let Doctest {
159159
package,
160-
package
161-
.targets()
162-
.iter()
163-
.filter(|t| t.doctested())
164-
.map(|t| (t.src_path(), t.name(), t.crate_name())),
165-
)
166-
});
167-
168-
for (package, tests) in libs {
169-
for (lib, name, crate_name) in tests {
170-
config.shell().status("Doc-tests", name)?;
171-
let mut p = compilation.rustdoc_process(package)?;
172-
p.arg("--test")
173-
.arg(lib)
174-
.arg("--crate-name")
175-
.arg(&crate_name);
176-
177-
for &rust_dep in &[&compilation.deps_output] {
178-
let mut arg = OsString::from("dependency=");
179-
arg.push(rust_dep);
180-
p.arg("-L").arg(arg);
181-
}
160+
target,
161+
deps,
162+
} = doctest_info;
163+
config.shell().status("Doc-tests", target.name())?;
164+
let mut p = compilation.rustdoc_process(package)?;
165+
p.arg("--test")
166+
.arg(target.src_path())
167+
.arg("--crate-name")
168+
.arg(&target.crate_name());
169+
170+
for &rust_dep in &[&compilation.deps_output] {
171+
let mut arg = OsString::from("dependency=");
172+
arg.push(rust_dep);
173+
p.arg("-L").arg(arg);
174+
}
182175

183-
for native_dep in compilation.native_dirs.iter() {
184-
p.arg("-L").arg(native_dep);
185-
}
176+
for native_dep in compilation.native_dirs.iter() {
177+
p.arg("-L").arg(native_dep);
178+
}
186179

187-
for &host_rust_dep in &[&compilation.host_deps_output] {
188-
let mut arg = OsString::from("dependency=");
189-
arg.push(host_rust_dep);
190-
p.arg("-L").arg(arg);
191-
}
180+
for &host_rust_dep in &[&compilation.host_deps_output] {
181+
let mut arg = OsString::from("dependency=");
182+
arg.push(host_rust_dep);
183+
p.arg("-L").arg(arg);
184+
}
192185

193-
for arg in test_args {
194-
p.arg("--test-args").arg(arg);
195-
}
186+
for arg in test_args {
187+
p.arg("--test-args").arg(arg);
188+
}
196189

197-
if let Some(cfgs) = compilation.cfgs.get(package.package_id()) {
198-
for cfg in cfgs.iter() {
199-
p.arg("--cfg").arg(cfg);
200-
}
190+
if let Some(cfgs) = compilation.cfgs.get(package.package_id()) {
191+
for cfg in cfgs.iter() {
192+
p.arg("--cfg").arg(cfg);
201193
}
194+
}
202195

203-
let libs = &compilation.libraries[package.package_id()];
204-
for &(ref target, ref lib) in libs.iter() {
205-
// Note that we can *only* doctest rlib outputs here. A
206-
// staticlib output cannot be linked by the compiler (it just
207-
// doesn't do that). A dylib output, however, can be linked by
208-
// the compiler, but will always fail. Currently all dylibs are
209-
// built as "static dylibs" where the standard library is
210-
// statically linked into the dylib. The doc tests fail,
211-
// however, for now as they try to link the standard library
212-
// dynamically as well, causing problems. As a result we only
213-
// pass `--extern` for rlib deps and skip out on all other
214-
// artifacts.
215-
if lib.extension() != Some(OsStr::new("rlib")) && !target.for_host() {
216-
continue;
217-
}
218-
let mut arg = OsString::from(target.crate_name());
219-
arg.push("=");
220-
arg.push(lib);
221-
p.arg("--extern").arg(&arg);
222-
}
196+
for &(ref target, ref lib) in deps.iter() {
197+
let mut arg = OsString::from(target.crate_name());
198+
arg.push("=");
199+
arg.push(lib);
200+
p.arg("--extern").arg(&arg);
201+
}
223202

224-
if let Some(flags) = compilation.rustdocflags.get(package.package_id()) {
225-
p.args(flags);
226-
}
203+
if let Some(flags) = compilation.rustdocflags.get(package.package_id()) {
204+
p.args(flags);
205+
}
227206

228-
config
229-
.shell()
230-
.verbose(|shell| shell.status("Running", p.to_string()))?;
231-
if let Err(e) = p.exec() {
232-
let e = e.downcast::<ProcessError>()?;
233-
errors.push(e);
234-
if !options.no_fail_fast {
235-
return Ok((Test::Doc, errors));
236-
}
207+
config
208+
.shell()
209+
.verbose(|shell| shell.status("Running", p.to_string()))?;
210+
if let Err(e) = p.exec() {
211+
let e = e.downcast::<ProcessError>()?;
212+
errors.push(e);
213+
if !options.no_fail_fast {
214+
return Ok((Test::Doc, errors));
237215
}
238216
}
239217
}

tests/testsuite/build_script.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3044,6 +3044,7 @@ fn panic_abort_with_build_scripts() {
30443044
"src/lib.rs",
30453045
"#[allow(unused_extern_crates)] extern crate a;",
30463046
)
3047+
.file("build.rs","fn main() {}")
30473048
.file(
30483049
"a/Cargo.toml",
30493050
r#"
@@ -3078,6 +3079,11 @@ fn panic_abort_with_build_scripts() {
30783079
p.cargo("build").arg("-v").arg("--release"),
30793080
execs().with_status(0),
30803081
);
3082+
3083+
assert_that(
3084+
p.cargo("test --release"),
3085+
execs().with_status(0)
3086+
);
30813087
}
30823088

30833089
#[test]

0 commit comments

Comments
 (0)