[BETA] Fix doctests linking too many libs.
authorbors <bors@rust-lang.org>
Thu, 5 Jul 2018 20:06:19 +0000 (20:06 +0000)
committerEric Huss <eric@huss.org>
Thu, 5 Jul 2018 21:38:49 +0000 (14:38 -0700)
Backport of #5651.

src/cargo/core/compiler/compilation.rs
src/cargo/core/compiler/context/mod.rs
src/cargo/core/compiler/context/unit_dependencies.rs
src/cargo/core/compiler/job_queue.rs
src/cargo/core/compiler/mod.rs
src/cargo/ops/cargo_compile.rs
src/cargo/ops/cargo_test.rs
tests/testsuite/build_script.rs

index 1231de815efb7c13f95f6dedfff1909e1fb70d5c..28b2a6b17f2c96a771cb1d91b62d0d230efeafe9 100644 (file)
@@ -9,10 +9,21 @@ use core::{Feature, Package, PackageId, Target, TargetKind};
 use util::{self, join_paths, process, CargoResult, Config, ProcessBuilder};
 use super::BuildContext;
 
+pub struct Doctest {
+    /// The package being doctested.
+    pub package: Package,
+    /// The target being tested (currently always the package's lib).
+    pub target: Target,
+    /// Extern dependencies needed by `rustdoc`. The path is the location of
+    /// the compiled lib.
+    pub deps: Vec<(Target, PathBuf)>,
+}
+
 /// A structure returning the result of a compilation.
 pub struct Compilation<'cfg> {
     /// A mapping from a package to the list of libraries that need to be
     /// linked when working with that package.
+    // TODO: deprecated, remove
     pub libraries: HashMap<PackageId, HashSet<(Target, PathBuf)>>,
 
     /// An array of all tests created during this compilation.
@@ -50,7 +61,8 @@ pub struct Compilation<'cfg> {
     /// be passed to future invocations of programs.
     pub extra_env: HashMap<PackageId, Vec<(String, String)>>,
 
-    pub to_doc_test: Vec<Package>,
+    /// Libraries to test with rustdoc.
+    pub to_doc_test: Vec<Doctest>,
 
     /// Features per package enabled during this compilation.
     pub cfgs: HashMap<PackageId, HashSet<String>>,
index 0000b8950577e948dd3e9ea7623512359e291ee3..0b5c77d2277499a2dadb103c3dcf284ac631501a 100644 (file)
@@ -1,5 +1,6 @@
 #![allow(deprecated)]
 use std::collections::{HashMap, HashSet};
+use std::ffi::OsStr;
 use std::fmt::Write;
 use std::path::PathBuf;
 use std::sync::Arc;
@@ -8,6 +9,7 @@ use std::cmp::Ordering;
 use jobserver::Client;
 
 use core::{Package, PackageId, Resolve, Target};
+use core::compiler::compilation;
 use core::profiles::Profile;
 use util::errors::{CargoResult, CargoResultExt};
 use util::{internal, profile, Config, short_hash};
@@ -175,7 +177,7 @@ impl<'a, 'cfg> Context<'a, 'cfg> {
                     None => &output.path,
                 };
 
-                if unit.mode.is_any_test() && !unit.mode.is_check() {
+                if unit.mode == CompileMode::Test {
                     self.compilation.tests.push((
                         unit.pkg.clone(),
                         unit.target.kind().clone(),
@@ -227,6 +229,39 @@ impl<'a, 'cfg> Context<'a, 'cfg> {
                     );
             }
 
+            if unit.mode == CompileMode::Doctest {
+                // Note that we can *only* doctest rlib outputs here.  A
+                // staticlib output cannot be linked by the compiler (it just
+                // doesn't do that). A dylib output, however, can be linked by
+                // the compiler, but will always fail. Currently all dylibs are
+                // built as "static dylibs" where the standard library is
+                // statically linked into the dylib. The doc tests fail,
+                // however, for now as they try to link the standard library
+                // dynamically as well, causing problems. As a result we only
+                // pass `--extern` for rlib deps and skip out on all other
+                // artifacts.
+                let mut doctest_deps = Vec::new();
+                for dep in self.dep_targets(unit) {
+                    if dep.target.is_lib() && dep.mode == CompileMode::Build {
+                        let outputs = self.outputs(&dep)?;
+                        doctest_deps.extend(
+                            outputs
+                                .iter()
+                                .filter(|output| {
+                                    output.path.extension() == Some(OsStr::new("rlib"))
+                                        || dep.target.for_host()
+                                })
+                                .map(|output| (dep.target.clone(), output.path.clone())),
+                        );
+                    }
+                }
+                self.compilation.to_doc_test.push(compilation::Doctest {
+                    package: unit.pkg.clone(),
+                    target: unit.target.clone(),
+                    deps: doctest_deps,
+                });
+            }
+
             let feats = self.bcx.resolve.features(unit.pkg.package_id());
             if !feats.is_empty() {
                 self.compilation
index 8c87d0a53e5ece1264151fca113d4e90bdeff076..f522f3839a948d46d97fc3849a18b88edd997460 100644 (file)
@@ -199,19 +199,11 @@ fn compute_deps_custom_build<'a, 'cfg>(
     // 1. Compiling the build script itself
     // 2. For each immediate dependency of our package which has a `links`
     //    key, the execution of that build script.
-    let not_custom_build = unit.pkg
-        .targets()
+    let deps = deps
         .iter()
-        .find(|t| !t.is_custom_build())
-        .unwrap();
-    let tmp = Unit {
-        pkg: unit.pkg,
-        target: not_custom_build,
-        profile: unit.profile,
-        kind: unit.kind,
-        mode: CompileMode::Build,
-    };
-    let deps = deps_of(&tmp, bcx, deps, ProfileFor::Any)?;
+        .find(|(key, _deps)| key.pkg == unit.pkg && !key.target.is_custom_build())
+        .expect("can't find package deps")
+        .1;
     Ok(deps.iter()
         .filter_map(|unit| {
             if !unit.target.linkable() || unit.pkg.manifest().links().is_none() {
index de633e7af378b1d0aad3997ce55aad8b0d2fb6cd..f7612e3cedf90ea326b1c83f26f795eb209b41c5 100644 (file)
@@ -433,11 +433,15 @@ impl<'a> JobQueue<'a> {
                     }
                 }
             }
-            Fresh if self.counts[key.pkg] == 0 => {
-                self.compiled.insert(key.pkg);
-                config.shell().verbose(|c| c.status("Fresh", key.pkg))?;
+            Fresh => {
+                // If doctest is last, only print "Fresh" if nothing has been printed.
+                if self.counts[key.pkg] == 0
+                    && !(key.mode == CompileMode::Doctest && self.compiled.contains(key.pkg))
+                {
+                    self.compiled.insert(key.pkg);
+                    config.shell().verbose(|c| c.status("Fresh", key.pkg))?;
+                }
             }
-            Fresh => {}
         }
         Ok(())
     }
index 2dff328de0b86f4f650a74db8cc99265a7524f1f..2e148ef22f25ae7c5fcbc2dadcf99961c045c05d 100644 (file)
@@ -24,7 +24,7 @@ use self::output_depinfo::output_depinfo;
 
 pub use self::build_context::{BuildContext, FileFlavor, TargetConfig, TargetInfo};
 pub use self::build_config::{BuildConfig, CompileMode, MessageFormat};
-pub use self::compilation::Compilation;
+pub use self::compilation::{Compilation, Doctest};
 pub use self::context::{Context, Unit};
 pub use self::custom_build::{BuildMap, BuildOutput, BuildScripts};
 pub use self::layout::is_bad_artifact_name;
index 5e60111e8b67d9ec8d61acfbced212c42ede226e..dc16c9dbf04d72dd662903fc5f3069a17e2e4849 100644 (file)
@@ -280,7 +280,7 @@ pub fn compile_ws<'a>(
         extra_compiler_args = Some((units[0], args));
     }
 
-    let mut ret = {
+    let ret = {
         let _p = profile::start("compiling");
         let bcx = BuildContext::new(
             ws,
@@ -295,8 +295,6 @@ pub fn compile_ws<'a>(
         cx.compile(&units, export_dir.clone(), &exec)?
     };
 
-    ret.to_doc_test = to_builds.into_iter().cloned().collect();
-
     return Ok(ret);
 }
 
@@ -541,9 +539,9 @@ fn generate_targets<'a>(
                     .collect::<Vec<_>>();
                 proposals.extend(default_units);
                 if build_config.mode == CompileMode::Test {
-                    // Include the lib as it will be required for doctests.
+                    // Include doctest for lib.
                     if let Some(t) = pkg.targets().iter().find(|t| t.is_lib() && t.doctested()) {
-                        proposals.push((new_unit(pkg, t, CompileMode::Build), false));
+                        proposals.push((new_unit(pkg, t, CompileMode::Doctest), false));
                     }
                 }
             }
@@ -681,15 +679,7 @@ fn generate_default_targets(targets: &[Target], mode: CompileMode) -> Vec<&Targe
                 })
                 .collect()
         }
-        CompileMode::Doctest => {
-            // `test --doc``
-            targets
-                .iter()
-                .find(|t| t.is_lib() && t.doctested())
-                .into_iter()
-                .collect()
-        }
-        CompileMode::RunCustomBuild => panic!("Invalid mode"),
+        CompileMode::Doctest | CompileMode::RunCustomBuild => panic!("Invalid mode {:?}", mode),
     }
 }
 
index c65cc1970d47fc75eb4336348f753e4fa44d26c5..1ed480fc0b417f1d98f616db332eb6adb88b18a9 100644 (file)
@@ -1,7 +1,7 @@
-use std::ffi::{OsStr, OsString};
+use std::ffi::OsString;
 
 use ops;
-use core::compiler::Compilation;
+use core::compiler::{Compilation, Doctest};
 use util::{self, CargoTestError, ProcessError, Test};
 use util::errors::CargoResult;
 use core::Workspace;
@@ -154,86 +154,64 @@ fn run_doc_tests(
         return Ok((Test::Doc, errors));
     }
 
-    let libs = compilation.to_doc_test.iter().map(|package| {
-        (
+    for doctest_info in &compilation.to_doc_test {
+        let Doctest {
             package,
-            package
-                .targets()
-                .iter()
-                .filter(|t| t.doctested())
-                .map(|t| (t.src_path(), t.name(), t.crate_name())),
-        )
-    });
-
-    for (package, tests) in libs {
-        for (lib, name, crate_name) in tests {
-            config.shell().status("Doc-tests", name)?;
-            let mut p = compilation.rustdoc_process(package)?;
-            p.arg("--test")
-                .arg(lib)
-                .arg("--crate-name")
-                .arg(&crate_name);
-
-            for &rust_dep in &[&compilation.deps_output] {
-                let mut arg = OsString::from("dependency=");
-                arg.push(rust_dep);
-                p.arg("-L").arg(arg);
-            }
+            target,
+            deps,
+        } = doctest_info;
+        config.shell().status("Doc-tests", target.name())?;
+        let mut p = compilation.rustdoc_process(package)?;
+        p.arg("--test")
+            .arg(target.src_path())
+            .arg("--crate-name")
+            .arg(&target.crate_name());
+
+        for &rust_dep in &[&compilation.deps_output] {
+            let mut arg = OsString::from("dependency=");
+            arg.push(rust_dep);
+            p.arg("-L").arg(arg);
+        }
 
-            for native_dep in compilation.native_dirs.iter() {
-                p.arg("-L").arg(native_dep);
-            }
+        for native_dep in compilation.native_dirs.iter() {
+            p.arg("-L").arg(native_dep);
+        }
 
-            for &host_rust_dep in &[&compilation.host_deps_output] {
-                let mut arg = OsString::from("dependency=");
-                arg.push(host_rust_dep);
-                p.arg("-L").arg(arg);
-            }
+        for &host_rust_dep in &[&compilation.host_deps_output] {
+            let mut arg = OsString::from("dependency=");
+            arg.push(host_rust_dep);
+            p.arg("-L").arg(arg);
+        }
 
-            for arg in test_args {
-                p.arg("--test-args").arg(arg);
-            }
+        for arg in test_args {
+            p.arg("--test-args").arg(arg);
+        }
 
-            if let Some(cfgs) = compilation.cfgs.get(package.package_id()) {
-                for cfg in cfgs.iter() {
-                    p.arg("--cfg").arg(cfg);
-                }
+        if let Some(cfgs) = compilation.cfgs.get(package.package_id()) {
+            for cfg in cfgs.iter() {
+                p.arg("--cfg").arg(cfg);
             }
+        }
 
-            let libs = &compilation.libraries[package.package_id()];
-            for &(ref target, ref lib) in libs.iter() {
-                // Note that we can *only* doctest rlib outputs here.  A
-                // staticlib output cannot be linked by the compiler (it just
-                // doesn't do that). A dylib output, however, can be linked by
-                // the compiler, but will always fail. Currently all dylibs are
-                // built as "static dylibs" where the standard library is
-                // statically linked into the dylib. The doc tests fail,
-                // however, for now as they try to link the standard library
-                // dynamically as well, causing problems. As a result we only
-                // pass `--extern` for rlib deps and skip out on all other
-                // artifacts.
-                if lib.extension() != Some(OsStr::new("rlib")) && !target.for_host() {
-                    continue;
-                }
-                let mut arg = OsString::from(target.crate_name());
-                arg.push("=");
-                arg.push(lib);
-                p.arg("--extern").arg(&arg);
-            }
+        for &(ref target, ref lib) in deps.iter() {
+            let mut arg = OsString::from(target.crate_name());
+            arg.push("=");
+            arg.push(lib);
+            p.arg("--extern").arg(&arg);
+        }
 
-            if let Some(flags) = compilation.rustdocflags.get(package.package_id()) {
-                p.args(flags);
-            }
+        if let Some(flags) = compilation.rustdocflags.get(package.package_id()) {
+            p.args(flags);
+        }
 
-            config
-                .shell()
-                .verbose(|shell| shell.status("Running", p.to_string()))?;
-            if let Err(e) = p.exec() {
-                let e = e.downcast::<ProcessError>()?;
-                errors.push(e);
-                if !options.no_fail_fast {
-                    return Ok((Test::Doc, errors));
-                }
+        config
+            .shell()
+            .verbose(|shell| shell.status("Running", p.to_string()))?;
+        if let Err(e) = p.exec() {
+            let e = e.downcast::<ProcessError>()?;
+            errors.push(e);
+            if !options.no_fail_fast {
+                return Ok((Test::Doc, errors));
             }
         }
     }
index a5e32f5023fe1cc031a2bee097fa6abe7989b001..1754123364e831eb296d2fc8a8b4494efdf0ca16 100644 (file)
@@ -3044,6 +3044,7 @@ fn panic_abort_with_build_scripts() {
             "src/lib.rs",
             "#[allow(unused_extern_crates)] extern crate a;",
         )
+        .file("build.rs","fn main() {}")
         .file(
             "a/Cargo.toml",
             r#"
@@ -3078,6 +3079,11 @@ fn panic_abort_with_build_scripts() {
         p.cargo("build").arg("-v").arg("--release"),
         execs().with_status(0),
     );
+
+    assert_that(
+        p.cargo("test --release"),
+        execs().with_status(0)
+    );
 }
 
 #[test]