Skip to content

Commit 5c215f8

Browse files
committed
Read docs.rs metadata when running rustdoc
- Add tests for doc runs - Only give an error when docs.rs feature is set in the test crate This allows distinguishing between 'any doc run' and 'doc run that uses docs.rs features' - Set RUSTC_BOOTSTRAP for doc runs This is required for both docs.rs features and the flags passed by docsrs_metadata to cargo. - Only use docs.rs features for libraries `docsrs_metadata` unconditionally passes `--lib` to cargo, which gives a hard error when no library is present. This also required changing the setup in `runner/tests` to pass through the full package metadata to all test runners.
1 parent b55a557 commit 5c215f8

13 files changed

+355
-36
lines changed

Cargo.lock

Lines changed: 16 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ chrono-humanize = "0.1.1"
2020
crates-index = "0.16.2"
2121
crossbeam-utils = "0.5"
2222
csv = "1.0.2"
23+
docsrs-metadata = { git = "https://github.com/rust-lang/docs.rs/" }
2324
dotenv = "0.13"
2425
failure = "0.1.3"
2526
flate2 = "1"
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
[package]
2+
name = "docs-rs-features"
3+
version = "0.1.0"
4+
authors = ["Joshua Nelson <jyn514@gmail.com>"]
5+
edition = "2018"
6+
7+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
8+
9+
[dependencies]
10+
11+
[package.metadata.docs.rs]
12+
features = ["docs_rs_feature"]
13+
14+
[features]
15+
docs_rs_feature = []
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#[cfg(feature = "docs_rs_feature")]
2+
compile_error!("oh no, a hidden regression!");
3+
4+
fn main() {
5+
println!("Hello, world!");
6+
}

src/runner/test.rs

Lines changed: 78 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@ use crate::results::{BrokenReason, EncodingType, FailureReason, TestResult, Writ
55
use crate::runner::tasks::TaskCtx;
66
use crate::runner::OverrideResult;
77
use cargo_metadata::diagnostic::DiagnosticLevel;
8-
use cargo_metadata::{Message, Metadata, PackageId};
8+
use cargo_metadata::{Message, Metadata, Package, Target};
9+
use docsrs_metadata::Metadata as DocsrsMetadata;
910
use failure::Error;
1011
use remove_dir_all::remove_dir_all;
1112
use rustwide::cmd::{CommandError, ProcessLinesActions, SandboxBuilder};
1213
use rustwide::{Build, PrepareError};
13-
use std::collections::{BTreeSet, HashSet};
14+
use std::collections::{BTreeSet, HashMap, HashSet};
1415
use std::convert::TryFrom;
1516

1617
fn failure_reason(err: &Error) -> FailureReason {
@@ -64,7 +65,7 @@ pub(super) fn detect_broken<T>(res: Result<T, Error>) -> Result<T, Error> {
6465
}
6566
}
6667

67-
fn get_local_packages(build_env: &Build) -> Fallible<HashSet<PackageId>> {
68+
fn get_local_packages(build_env: &Build) -> Fallible<Vec<Package>> {
6869
Ok(build_env
6970
.cargo()
7071
.args(&["metadata", "--no-deps", "--format-version=1"])
@@ -73,17 +74,20 @@ fn get_local_packages(build_env: &Build) -> Fallible<HashSet<PackageId>> {
7374
.stdout_lines()
7475
.iter()
7576
.filter_map(|line| serde_json::from_str::<Metadata>(line).ok())
76-
.flat_map(|metadata| metadata.packages.into_iter().map(|pkg| pkg.id))
77-
.collect::<HashSet<_>>())
77+
.flat_map(|metadata| metadata.packages)
78+
.collect())
7879
}
7980

8081
fn run_cargo<DB: WriteResults>(
8182
ctx: &TaskCtx<DB>,
8283
build_env: &Build,
8384
args: &[&str],
8485
check_errors: bool,
85-
local_packages_id: &HashSet<PackageId>,
86+
local_packages: &[Package],
87+
env: HashMap<&'static str, String>,
8688
) -> Fallible<()> {
89+
let local_packages_id: HashSet<_> = local_packages.iter().map(|p| &p.id).collect();
90+
8791
let mut rustflags = format!("--cap-lints={}", ctx.experiment.cap_lints.to_str());
8892
if let Some(ref tc_rustflags) = ctx.toolchain.rustflags {
8993
rustflags.push(' ');
@@ -151,6 +155,9 @@ fn run_cargo<DB: WriteResults>(
151155
.env("CARGO_INCREMENTAL", "0")
152156
.env("RUST_BACKTRACE", "full")
153157
.env(rustflags_env, rustflags);
158+
for (var, data) in env {
159+
command = command.env(var, data);
160+
}
154161

155162
if check_errors {
156163
command = command.process_lines(&mut detect_error);
@@ -179,7 +186,7 @@ fn run_cargo<DB: WriteResults>(
179186
pub(super) fn run_test<DB: WriteResults>(
180187
action: &str,
181188
ctx: &TaskCtx<DB>,
182-
test_fn: fn(&TaskCtx<DB>, &Build, &HashSet<PackageId>) -> Fallible<TestResult>,
189+
test_fn: fn(&TaskCtx<DB>, &Build, &[Package]) -> Fallible<TestResult>,
183190
) -> Fallible<()> {
184191
if let Some(res) = ctx
185192
.db
@@ -221,8 +228,8 @@ pub(super) fn run_test<DB: WriteResults>(
221228
}
222229

223230
detect_broken(build.run(|build| {
224-
let local_packages_id = get_local_packages(build)?;
225-
test_fn(ctx, build, &local_packages_id)
231+
let local_packages = get_local_packages(build)?;
232+
test_fn(ctx, build, &local_packages)
226233
}))
227234
},
228235
)?;
@@ -233,21 +240,23 @@ pub(super) fn run_test<DB: WriteResults>(
233240
fn build<DB: WriteResults>(
234241
ctx: &TaskCtx<DB>,
235242
build_env: &Build,
236-
local_packages_id: &HashSet<PackageId>,
243+
local_packages: &[Package],
237244
) -> Fallible<()> {
238245
run_cargo(
239246
ctx,
240247
build_env,
241248
&["build", "--frozen", "--message-format=json"],
242249
true,
243-
local_packages_id,
250+
local_packages,
251+
HashMap::default(),
244252
)?;
245253
run_cargo(
246254
ctx,
247255
build_env,
248256
&["test", "--frozen", "--no-run", "--message-format=json"],
249257
true,
250-
local_packages_id,
258+
local_packages,
259+
HashMap::default(),
251260
)?;
252261
Ok(())
253262
}
@@ -258,14 +267,15 @@ fn test<DB: WriteResults>(ctx: &TaskCtx<DB>, build_env: &Build) -> Fallible<()>
258267
build_env,
259268
&["test", "--frozen"],
260269
false,
261-
&HashSet::new(),
270+
&[],
271+
HashMap::default(),
262272
)
263273
}
264274

265275
pub(super) fn test_build_and_test<DB: WriteResults>(
266276
ctx: &TaskCtx<DB>,
267277
build_env: &Build,
268-
local_packages_id: &HashSet<PackageId>,
278+
local_packages_id: &[Package],
269279
) -> Fallible<TestResult> {
270280
let build_r = build(ctx, build_env, local_packages_id);
271281
let test_r = if build_r.is_ok() {
@@ -285,7 +295,7 @@ pub(super) fn test_build_and_test<DB: WriteResults>(
285295
pub(super) fn test_build_only<DB: WriteResults>(
286296
ctx: &TaskCtx<DB>,
287297
build_env: &Build,
288-
local_packages_id: &HashSet<PackageId>,
298+
local_packages_id: &[Package],
289299
) -> Fallible<TestResult> {
290300
if let Err(err) = build(ctx, build_env, local_packages_id) {
291301
Ok(TestResult::BuildFail(failure_reason(&err)))
@@ -297,7 +307,7 @@ pub(super) fn test_build_only<DB: WriteResults>(
297307
pub(super) fn test_check_only<DB: WriteResults>(
298308
ctx: &TaskCtx<DB>,
299309
build_env: &Build,
300-
local_packages_id: &HashSet<PackageId>,
310+
local_packages_id: &[Package],
301311
) -> Fallible<TestResult> {
302312
if let Err(err) = run_cargo(
303313
ctx,
@@ -311,6 +321,7 @@ pub(super) fn test_check_only<DB: WriteResults>(
311321
],
312322
true,
313323
local_packages_id,
324+
HashMap::default(),
314325
) {
315326
Ok(TestResult::BuildFail(failure_reason(&err)))
316327
} else {
@@ -321,7 +332,7 @@ pub(super) fn test_check_only<DB: WriteResults>(
321332
pub(super) fn test_clippy_only<DB: WriteResults>(
322333
ctx: &TaskCtx<DB>,
323334
build_env: &Build,
324-
local_packages_id: &HashSet<PackageId>,
335+
local_packages: &[Package],
325336
) -> Fallible<TestResult> {
326337
if let Err(err) = run_cargo(
327338
ctx,
@@ -334,7 +345,8 @@ pub(super) fn test_clippy_only<DB: WriteResults>(
334345
"--message-format=json",
335346
],
336347
true,
337-
local_packages_id,
348+
local_packages,
349+
HashMap::default(),
338350
) {
339351
Ok(TestResult::BuildFail(failure_reason(&err)))
340352
} else {
@@ -345,29 +357,64 @@ pub(super) fn test_clippy_only<DB: WriteResults>(
345357
pub(super) fn test_rustdoc<DB: WriteResults>(
346358
ctx: &TaskCtx<DB>,
347359
build_env: &Build,
348-
local_packages_id: &HashSet<PackageId>,
360+
local_packages: &[Package],
349361
) -> Fallible<TestResult> {
350-
let res = run_cargo(
351-
ctx,
352-
build_env,
362+
let run = |cargo_args, env| {
363+
let res = run_cargo(ctx, build_env, cargo_args, true, local_packages, env);
364+
365+
// Make sure to remove the built documentation
366+
// There is no point in storing it after the build is done
367+
remove_dir_all(&build_env.host_target_dir().join("doc"))?;
368+
369+
res
370+
};
371+
372+
// first, run a normal `cargo doc`
373+
let res = run(
353374
&[
354375
"doc",
355376
"--frozen",
356377
"--no-deps",
357378
"--document-private-items",
358379
"--message-format=json",
359380
],
360-
true,
361-
local_packages_id,
381+
HashMap::default(),
362382
);
383+
if let Err(err) = res {
384+
return Ok(TestResult::BuildFail(failure_reason(&err)));
385+
}
363386

364-
// Make sure to remove the built documentation
365-
// There is no point in storing it after the build is done
366-
remove_dir_all(&build_env.host_target_dir().join("doc"))?;
387+
// next, if this is a library, run it with docs.rs metadata applied.
388+
if local_packages
389+
.iter()
390+
.any(|p| p.targets.iter().any(is_library))
391+
{
392+
let src = build_env.host_source_dir();
393+
let metadata = DocsrsMetadata::from_crate_root(src)?;
394+
let cargo_args = metadata.cargo_args(
395+
&["--frozen".into(), "--message-format=json".into()],
396+
&["--document-private-items".into()],
397+
);
398+
assert_eq!(cargo_args[0], "rustdoc");
399+
let cargo_args: Vec<_> = cargo_args.iter().map(|s| s.as_str()).collect();
400+
let mut env = metadata.environment_variables();
401+
// docsrs-metadata requires a nightly environment, but crater sometimes runs tests on beta and
402+
// stable.
403+
env.insert("RUSTC_BOOTSTRAP", "1".to_string());
367404

368-
if let Err(err) = res {
369-
Ok(TestResult::BuildFail(failure_reason(&err)))
370-
} else {
371-
Ok(TestResult::TestPass)
405+
if let Err(err) = run(&cargo_args, env) {
406+
return Ok(TestResult::BuildFail(failure_reason(&err)));
407+
}
372408
}
409+
410+
Ok(TestResult::TestPass)
411+
}
412+
413+
fn is_library(target: &Target) -> bool {
414+
// Some examples and tests can be libraries (e.g. if they use `cdylib`).
415+
target.crate_types.iter().any(|ty| ty != "bin")
416+
&& target
417+
.kind
418+
.iter()
419+
.all(|k| !["example", "test", "bench"].contains(&k.as_str()))
373420
}

src/runner/unstable_features.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use crate::prelude::*;
22
use crate::results::TestResult;
33
use crate::results::WriteResults;
44
use crate::runner::tasks::TaskCtx;
5-
use cargo_metadata::PackageId;
5+
use cargo_metadata::Package;
66
use rustwide::Build;
77
use std::collections::HashSet;
88
use std::path::Path;
@@ -11,7 +11,7 @@ use walkdir::{DirEntry, WalkDir};
1111
pub(super) fn find_unstable_features<DB: WriteResults>(
1212
_ctx: &TaskCtx<DB>,
1313
build: &Build,
14-
_local_packages_id: &HashSet<PackageId>,
14+
_local_packages_id: &[Package],
1515
) -> Fallible<TestResult> {
1616
let mut features = HashSet::new();
1717

tests/minicrater/doc/config.toml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
[server.bot-acl]
2+
rust-teams = true
3+
github = ["pietroalbini"]
4+
5+
[server.labels]
6+
remove = "^S-"
7+
experiment-queued = "S-waiting-on-crater"
8+
experiment-completed = "S-waiting-on-review"
9+
10+
[server.distributed]
11+
chunk-size = 32
12+
13+
[demo-crates]
14+
crates = []
15+
github-repos = []
16+
local-crates = ["build-pass", "docs-rs-features"]
17+
18+
[sandbox]
19+
memory-limit = "512M"
20+
build-log-max-size = "2M"
21+
build-log-max-lines = 1000
22+
23+
[crates]
24+
25+
[github-repos]
26+
27+
[local-crates]

0 commit comments

Comments
 (0)