diff --git a/Cargo.lock b/Cargo.lock index 4d4cd8000..9ad207600 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,5 +1,7 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +version = 3 + [[package]] name = "addr2line" version = "0.14.0" @@ -445,6 +447,7 @@ dependencies = [ "csv", "ctrlc", "difference", + "docsrs-metadata", "dotenv", "env_logger", "failure", @@ -686,6 +689,16 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" +[[package]] +name = "docsrs-metadata" +version = "0.1.0" +source = "git+https://github.com/rust-lang/docs.rs/#97080011f4e3007c22a0b181d9820e6e980c5bf7" +dependencies = [ + "serde", + "thiserror", + "toml 0.5.7", +] + [[package]] name = "dotenv" version = "0.13.0" @@ -1301,13 +1314,13 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "lexical-core" -version = "0.7.4" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db65c6da02e61f55dae90a0ae427b2a5f6b3e8db09f58d10efab23af92592616" +checksum = "6607c62aa161d23d17a9072cc5da0be67cdfc89d3afb1e8d9c842bebc2525ffe" dependencies = [ "arrayvec", "bitflags", - "cfg-if 0.1.10", + "cfg-if 1.0.0", "ryu", "static_assertions", ] diff --git a/Cargo.toml b/Cargo.toml index 19fdfa983..169135924 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,7 @@ chrono-humanize = "0.1.1" crates-index = "0.16.2" crossbeam-utils = "0.5" csv = "1.0.2" +docsrs-metadata = { git = "https://github.com/rust-lang/docs.rs/" } dotenv = "0.13" failure = "0.1.3" flate2 = "1" diff --git a/local-crates/docs-rs-features/Cargo.toml b/local-crates/docs-rs-features/Cargo.toml new file mode 100644 index 000000000..b9ff60158 --- /dev/null +++ b/local-crates/docs-rs-features/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "docs-rs-features" +version = "0.1.0" +authors = ["Joshua Nelson "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] + +[package.metadata.docs.rs] +features = ["docs_rs_feature"] + +[features] +docs_rs_feature = [] diff --git a/local-crates/docs-rs-features/src/lib.rs b/local-crates/docs-rs-features/src/lib.rs new file mode 100644 index 000000000..74d165969 --- /dev/null +++ b/local-crates/docs-rs-features/src/lib.rs @@ -0,0 +1,6 @@ +#[cfg(feature = "docs_rs_feature")] +compile_error!("oh no, a hidden regression!"); + +fn main() { + println!("Hello, world!"); +} diff --git a/src/runner/test.rs b/src/runner/test.rs index 59b7d913c..f5b99e765 100644 --- a/src/runner/test.rs +++ b/src/runner/test.rs @@ -5,12 +5,13 @@ use crate::results::{BrokenReason, EncodingType, FailureReason, TestResult, Writ use crate::runner::tasks::TaskCtx; use crate::runner::OverrideResult; use cargo_metadata::diagnostic::DiagnosticLevel; -use cargo_metadata::{Message, Metadata, PackageId}; +use cargo_metadata::{Message, Metadata, Package, Target}; +use docsrs_metadata::Metadata as DocsrsMetadata; use failure::Error; use remove_dir_all::remove_dir_all; use rustwide::cmd::{CommandError, ProcessLinesActions, SandboxBuilder}; use rustwide::{Build, PrepareError}; -use std::collections::{BTreeSet, HashSet}; +use std::collections::{BTreeSet, HashMap, HashSet}; use std::convert::TryFrom; fn failure_reason(err: &Error) -> FailureReason { @@ -64,7 +65,7 @@ pub(super) fn detect_broken(res: Result) -> Result { } } -fn get_local_packages(build_env: &Build) -> Fallible> { +fn get_local_packages(build_env: &Build) -> Fallible> { Ok(build_env .cargo() .args(&["metadata", "--no-deps", "--format-version=1"]) @@ -73,8 +74,8 @@ fn get_local_packages(build_env: &Build) -> Fallible> { .stdout_lines() .iter() .filter_map(|line| serde_json::from_str::(line).ok()) - .flat_map(|metadata| metadata.packages.into_iter().map(|pkg| pkg.id)) - .collect::>()) + .flat_map(|metadata| metadata.packages) + .collect()) } fn run_cargo( @@ -82,8 +83,11 @@ fn run_cargo( build_env: &Build, args: &[&str], check_errors: bool, - local_packages_id: &HashSet, + local_packages: &[Package], + env: HashMap<&'static str, String>, ) -> Fallible<()> { + let local_packages_id: HashSet<_> = local_packages.iter().map(|p| &p.id).collect(); + let mut rustflags = format!("--cap-lints={}", ctx.experiment.cap_lints.to_str()); if let Some(ref tc_rustflags) = ctx.toolchain.rustflags { rustflags.push(' '); @@ -151,6 +155,9 @@ fn run_cargo( .env("CARGO_INCREMENTAL", "0") .env("RUST_BACKTRACE", "full") .env(rustflags_env, rustflags); + for (var, data) in env { + command = command.env(var, data); + } if check_errors { command = command.process_lines(&mut detect_error); @@ -179,7 +186,7 @@ fn run_cargo( pub(super) fn run_test( action: &str, ctx: &TaskCtx, - test_fn: fn(&TaskCtx, &Build, &HashSet) -> Fallible, + test_fn: fn(&TaskCtx, &Build, &[Package]) -> Fallible, ) -> Fallible<()> { if let Some(res) = ctx .db @@ -221,8 +228,8 @@ pub(super) fn run_test( } detect_broken(build.run(|build| { - let local_packages_id = get_local_packages(build)?; - test_fn(ctx, build, &local_packages_id) + let local_packages = get_local_packages(build)?; + test_fn(ctx, build, &local_packages) })) }, )?; @@ -233,21 +240,23 @@ pub(super) fn run_test( fn build( ctx: &TaskCtx, build_env: &Build, - local_packages_id: &HashSet, + local_packages: &[Package], ) -> Fallible<()> { run_cargo( ctx, build_env, &["build", "--frozen", "--message-format=json"], true, - local_packages_id, + local_packages, + HashMap::default(), )?; run_cargo( ctx, build_env, &["test", "--frozen", "--no-run", "--message-format=json"], true, - local_packages_id, + local_packages, + HashMap::default(), )?; Ok(()) } @@ -258,14 +267,15 @@ fn test(ctx: &TaskCtx, build_env: &Build) -> Fallible<()> build_env, &["test", "--frozen"], false, - &HashSet::new(), + &[], + HashMap::default(), ) } pub(super) fn test_build_and_test( ctx: &TaskCtx, build_env: &Build, - local_packages_id: &HashSet, + local_packages_id: &[Package], ) -> Fallible { let build_r = build(ctx, build_env, local_packages_id); let test_r = if build_r.is_ok() { @@ -285,7 +295,7 @@ pub(super) fn test_build_and_test( pub(super) fn test_build_only( ctx: &TaskCtx, build_env: &Build, - local_packages_id: &HashSet, + local_packages_id: &[Package], ) -> Fallible { if let Err(err) = build(ctx, build_env, local_packages_id) { Ok(TestResult::BuildFail(failure_reason(&err))) @@ -297,7 +307,7 @@ pub(super) fn test_build_only( pub(super) fn test_check_only( ctx: &TaskCtx, build_env: &Build, - local_packages_id: &HashSet, + local_packages_id: &[Package], ) -> Fallible { if let Err(err) = run_cargo( ctx, @@ -311,6 +321,7 @@ pub(super) fn test_check_only( ], true, local_packages_id, + HashMap::default(), ) { Ok(TestResult::BuildFail(failure_reason(&err))) } else { @@ -321,7 +332,7 @@ pub(super) fn test_check_only( pub(super) fn test_clippy_only( ctx: &TaskCtx, build_env: &Build, - local_packages_id: &HashSet, + local_packages: &[Package], ) -> Fallible { if let Err(err) = run_cargo( ctx, @@ -334,7 +345,8 @@ pub(super) fn test_clippy_only( "--message-format=json", ], true, - local_packages_id, + local_packages, + HashMap::default(), ) { Ok(TestResult::BuildFail(failure_reason(&err))) } else { @@ -345,11 +357,20 @@ pub(super) fn test_clippy_only( pub(super) fn test_rustdoc( ctx: &TaskCtx, build_env: &Build, - local_packages_id: &HashSet, + local_packages: &[Package], ) -> Fallible { - let res = run_cargo( - ctx, - build_env, + let run = |cargo_args, env| { + let res = run_cargo(ctx, build_env, cargo_args, true, local_packages, env); + + // Make sure to remove the built documentation + // There is no point in storing it after the build is done + remove_dir_all(&build_env.host_target_dir().join("doc"))?; + + res + }; + + // first, run a normal `cargo doc` + let res = run( &[ "doc", "--frozen", @@ -357,17 +378,43 @@ pub(super) fn test_rustdoc( "--document-private-items", "--message-format=json", ], - true, - local_packages_id, + HashMap::default(), ); + if let Err(err) = res { + return Ok(TestResult::BuildFail(failure_reason(&err))); + } - // Make sure to remove the built documentation - // There is no point in storing it after the build is done - remove_dir_all(&build_env.host_target_dir().join("doc"))?; + // next, if this is a library, run it with docs.rs metadata applied. + if local_packages + .iter() + .any(|p| p.targets.iter().any(is_library)) + { + let src = build_env.host_source_dir(); + let metadata = DocsrsMetadata::from_crate_root(src)?; + let cargo_args = metadata.cargo_args( + &["--frozen".into(), "--message-format=json".into()], + &["--document-private-items".into()], + ); + assert_eq!(cargo_args[0], "rustdoc"); + let cargo_args: Vec<_> = cargo_args.iter().map(|s| s.as_str()).collect(); + let mut env = metadata.environment_variables(); + // docsrs-metadata requires a nightly environment, but crater sometimes runs tests on beta and + // stable. + env.insert("RUSTC_BOOTSTRAP", "1".to_string()); - if let Err(err) = res { - Ok(TestResult::BuildFail(failure_reason(&err))) - } else { - Ok(TestResult::TestPass) + if let Err(err) = run(&cargo_args, env) { + return Ok(TestResult::BuildFail(failure_reason(&err))); + } } + + Ok(TestResult::TestPass) +} + +fn is_library(target: &Target) -> bool { + // Some examples and tests can be libraries (e.g. if they use `cdylib`). + target.crate_types.iter().any(|ty| ty != "bin") + && target + .kind + .iter() + .all(|k| !["example", "test", "bench"].contains(&k.as_str())) } diff --git a/src/runner/unstable_features.rs b/src/runner/unstable_features.rs index 864c326bb..2149a2df4 100644 --- a/src/runner/unstable_features.rs +++ b/src/runner/unstable_features.rs @@ -2,7 +2,7 @@ use crate::prelude::*; use crate::results::TestResult; use crate::results::WriteResults; use crate::runner::tasks::TaskCtx; -use cargo_metadata::PackageId; +use cargo_metadata::Package; use rustwide::Build; use std::collections::HashSet; use std::path::Path; @@ -11,7 +11,7 @@ use walkdir::{DirEntry, WalkDir}; pub(super) fn find_unstable_features( _ctx: &TaskCtx, build: &Build, - _local_packages_id: &HashSet, + _local_packages_id: &[Package], ) -> Fallible { let mut features = HashSet::new(); diff --git a/tests/minicrater/doc/config.toml b/tests/minicrater/doc/config.toml new file mode 100644 index 000000000..efe718c91 --- /dev/null +++ b/tests/minicrater/doc/config.toml @@ -0,0 +1,27 @@ +[server.bot-acl] +rust-teams = true +github = ["pietroalbini"] + +[server.labels] +remove = "^S-" +experiment-queued = "S-waiting-on-crater" +experiment-completed = "S-waiting-on-review" + +[server.distributed] +chunk-size = 32 + +[demo-crates] +crates = [] +github-repos = [] +local-crates = ["build-pass", "docs-rs-features"] + +[sandbox] +memory-limit = "512M" +build-log-max-size = "2M" +build-log-max-lines = 1000 + +[crates] + +[github-repos] + +[local-crates] diff --git a/tests/minicrater/doc/downloads.html.context.expected.json b/tests/minicrater/doc/downloads.html.context.expected.json new file mode 100644 index 000000000..8f86d19a4 --- /dev/null +++ b/tests/minicrater/doc/downloads.html.context.expected.json @@ -0,0 +1,34 @@ +{ + "available_archives": [ + { + "name": "All the crates", + "path": "logs-archives/all.tar.gz" + }, + { + "name": "test-pass crates", + "path": "logs-archives/test-pass.tar.gz" + }, + { + "name": "build-fail crates", + "path": "logs-archives/build-fail.tar.gz" + } + ], + "crates_count": 2, + "nav": [ + { + "active": false, + "label": "Summary", + "url": "index.html" + }, + { + "active": false, + "label": "Full report", + "url": "full.html" + }, + { + "active": true, + "label": "Downloads", + "url": "downloads.html" + } + ] +} diff --git a/tests/minicrater/doc/full.html.context.expected.json b/tests/minicrater/doc/full.html.context.expected.json new file mode 100644 index 000000000..1c3645e2c --- /dev/null +++ b/tests/minicrater/doc/full.html.context.expected.json @@ -0,0 +1,91 @@ +{ + "categories": [ + [ + "test-pass", + { + "Plain": [ + { + "name": "build-pass (local)", + "res": "test-pass", + "runs": [ + { + "log": "stable/local/build-pass", + "res": 0 + }, + { + "log": "beta/local/build-pass", + "res": 0 + } + ], + "url": "https://github.com/rust-lang/crater/tree/master/local-crates/build-pass" + } + ] + } + ], + [ + "build-fail", + { + "Plain": [ + { + "name": "docs-rs-features (local)", + "res": "build-fail", + "runs": [ + { + "log": "stable/local/docs-rs-features", + "res": 1 + }, + { + "log": "beta/local/docs-rs-features", + "res": 1 + } + ], + "url": "https://github.com/rust-lang/crater/tree/master/local-crates/docs-rs-features" + } + ] + } + ] + ], + "comparison_colors": { + "build-fail": { + "Single": "#65461e" + }, + "test-pass": { + "Single": "#72a156" + } + }, + "crates_count": 2, + "full": true, + "info": { + "build-fail": 1, + "test-pass": 1 + }, + "nav": [ + { + "active": false, + "label": "Summary", + "url": "index.html" + }, + { + "active": true, + "label": "Full report", + "url": "full.html" + }, + { + "active": false, + "label": "Downloads", + "url": "downloads.html" + } + ], + "result_colors": [ + { + "Single": "#62a156" + }, + { + "Single": "#db3026" + } + ], + "result_names": [ + "test passed", + "build failed (unknown)" + ] +} diff --git a/tests/minicrater/doc/index.html.context.expected.json b/tests/minicrater/doc/index.html.context.expected.json new file mode 100644 index 000000000..9e7889185 --- /dev/null +++ b/tests/minicrater/doc/index.html.context.expected.json @@ -0,0 +1,29 @@ +{ + "categories": [], + "comparison_colors": {}, + "crates_count": 2, + "full": false, + "info": { + "build-fail": 1, + "test-pass": 1 + }, + "nav": [ + { + "active": true, + "label": "Summary", + "url": "index.html" + }, + { + "active": false, + "label": "Full report", + "url": "full.html" + }, + { + "active": false, + "label": "Downloads", + "url": "downloads.html" + } + ], + "result_colors": [], + "result_names": [] +} diff --git a/tests/minicrater/doc/markdown.md.context.expected.json b/tests/minicrater/doc/markdown.md.context.expected.json new file mode 100644 index 000000000..d7f7ea047 --- /dev/null +++ b/tests/minicrater/doc/markdown.md.context.expected.json @@ -0,0 +1,9 @@ +{ + "categories": [], + "crates_count": 2, + "full": false, + "info": { + "build-fail": 1, + "test-pass": 1 + } +} diff --git a/tests/minicrater/doc/results.expected.json b/tests/minicrater/doc/results.expected.json new file mode 100644 index 000000000..c4641eba5 --- /dev/null +++ b/tests/minicrater/doc/results.expected.json @@ -0,0 +1,40 @@ +{ + "crates": [ + { + "krate": { + "Local": "build-pass" + }, + "name": "build-pass (local)", + "res": "test-pass", + "runs": [ + { + "log": "stable/local/build-pass", + "res": "test-pass" + }, + { + "log": "beta/local/build-pass", + "res": "test-pass" + } + ], + "url": "https://github.com/rust-lang/crater/tree/master/local-crates/build-pass" + }, + { + "krate": { + "Local": "docs-rs-features" + }, + "name": "docs-rs-features (local)", + "res": "build-fail", + "runs": [ + { + "log": "stable/local/docs-rs-features", + "res": "build-fail:unknown" + }, + { + "log": "beta/local/docs-rs-features", + "res": "build-fail:unknown" + } + ], + "url": "https://github.com/rust-lang/crater/tree/master/local-crates/docs-rs-features" + } + ] +} diff --git a/tests/minicrater/mod.rs b/tests/minicrater/mod.rs index dd387d937..6f1f8f06b 100644 --- a/tests/minicrater/mod.rs +++ b/tests/minicrater/mod.rs @@ -42,6 +42,13 @@ minicrater! { ..Default::default() }, + doc_small { + ex: "doc", + crate_select: "demo", + mode: "rustdoc", + ..Default::default() + }, + single_thread_missing_repo { ex: "missing-repo", crate_select: "dummy",