Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

beta: revert #8640 - rustdoc versioning checks #9332

Merged
merged 3 commits into from
Apr 6, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions src/cargo/core/compiler/build_context/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,7 @@ use std::collections::{HashMap, HashSet};
use std::path::PathBuf;

mod target_info;
pub use self::target_info::{
FileFlavor, FileType, RustDocFingerprint, RustcTargetData, TargetInfo,
};
pub use self::target_info::{FileFlavor, FileType, RustcTargetData, TargetInfo};

/// The build context, containing all information about a build task.
///
Expand Down
83 changes: 3 additions & 80 deletions src/cargo/core/compiler/build_context/target_info.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
use crate::core::compiler::{
BuildOutput, CompileKind, CompileMode, CompileTarget, Context, CrateType,
};
use crate::core::compiler::{BuildOutput, CompileKind, CompileMode, CompileTarget, CrateType};
use crate::core::{Dependency, Target, TargetKind, Workspace};
use crate::util::config::{Config, StringList, TargetConfig};
use crate::util::{paths, CargoResult, CargoResultExt, ProcessBuilder, Rustc};
use crate::util::{CargoResult, CargoResultExt, ProcessBuilder, Rustc};
use cargo_platform::{Cfg, CfgExpr};
use serde::{Deserialize, Serialize};
use std::cell::RefCell;
use std::collections::hash_map::{Entry, HashMap};
use std::env;
use std::path::{Path, PathBuf};
use std::path::PathBuf;
use std::str::{self, FromStr};

/// Information about the platform target gleaned from querying rustc.
Expand Down Expand Up @@ -761,77 +758,3 @@ impl RustcTargetData {
self.target_config(kind).links_overrides.get(lib_name)
}
}

/// Structure used to deal with Rustdoc fingerprinting
#[derive(Debug, Serialize, Deserialize)]
pub struct RustDocFingerprint {
pub rustc_vv: String,
}

impl RustDocFingerprint {
/// Read the `RustDocFingerprint` info from the fingerprint file.
fn read(cx: &Context<'_, '_>) -> CargoResult<Self> {
let rustdoc_data = paths::read(&cx.files().host_root().join(".rustdoc_fingerprint.json"))?;
serde_json::from_str(&rustdoc_data).map_err(|e| anyhow::anyhow!("{:?}", e))
}

/// Write the `RustDocFingerprint` info into the fingerprint file.
fn write<'a, 'cfg>(&self, cx: &Context<'a, 'cfg>) -> CargoResult<()> {
paths::write(
&cx.files().host_root().join(".rustdoc_fingerprint.json"),
serde_json::to_string(&self)?.as_bytes(),
)
}

fn remove_doc_dirs(doc_dirs: &[&Path]) -> CargoResult<()> {
doc_dirs
.iter()
.filter(|path| path.exists())
.map(|path| paths::remove_dir_all(&path))
.collect::<CargoResult<()>>()
}

/// This function checks whether the latest version of `Rustc` used to compile this
/// `Workspace`'s docs was the same as the one is currently being used in this `cargo doc`
/// call.
///
/// In case it's not, it takes care of removing the `doc/` folder as well as overwriting
/// the rustdoc fingerprint info in order to guarantee that we won't end up with mixed
/// versions of the `js/html/css` files that `rustdoc` autogenerates which do not have
/// any versioning.
pub fn check_rustdoc_fingerprint(cx: &Context<'_, '_>) -> CargoResult<()> {
let actual_rustdoc_target_data = RustDocFingerprint {
rustc_vv: cx.bcx.rustc().verbose_version.clone(),
};

// Collect all of the target doc paths for which the docs need to be compiled for.
let doc_dirs: Vec<&Path> = cx
.bcx
.all_kinds
.iter()
.map(|kind| cx.files().layout(*kind).doc())
.collect();

// Check wether `.rustdoc_fingerprint.json` exists
match Self::read(cx) {
Ok(fingerprint) => {
// Check if rustc_version matches the one we just used. Otherways,
// remove the `doc` folder to trigger a re-compilation of the docs.
if fingerprint.rustc_vv != actual_rustdoc_target_data.rustc_vv {
Self::remove_doc_dirs(&doc_dirs)?;
actual_rustdoc_target_data.write(cx)?
}
}
// If the file does not exist, then we cannot assume that the docs were compiled
// with the actual Rustc instance version. Therefore, we try to remove the
// `doc` directory forcing the recompilation of the docs. If the directory doesn't
// exists neither, we simply do nothing and continue.
Err(_) => {
// We don't care if this succeeds as explained above.
let _ = Self::remove_doc_dirs(&doc_dirs);
actual_rustdoc_target_data.write(cx)?
}
}
Ok(())
}
}
16 changes: 1 addition & 15 deletions src/cargo/core/compiler/context/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,7 @@ use super::job_queue::JobQueue;
use super::layout::Layout;
use super::lto::Lto;
use super::unit_graph::UnitDep;
use super::{
BuildContext, Compilation, CompileKind, CompileMode, Executor, FileFlavor, RustDocFingerprint,
};
use super::{BuildContext, Compilation, CompileKind, CompileMode, Executor, FileFlavor};

mod compilation_files;
use self::compilation_files::CompilationFiles;
Expand Down Expand Up @@ -135,18 +133,6 @@ impl<'a, 'cfg> Context<'a, 'cfg> {
custom_build::build_map(&mut self)?;
self.check_collisions()?;

// We need to make sure that if there were any previous docs
// already compiled, they were compiled with the same Rustc version that we're currently
// using. Otherways we must remove the `doc/` folder and compile again forcing a rebuild.
//
// This is important because the `.js`/`.html` & `.css` files that are generated by Rustc don't have
// any versioning (See https://github.com/rust-lang/cargo/issues/8461).
// Therefore, we can end up with weird bugs and behaviours if we mix different
// versions of these files.
if self.bcx.build_config.mode.is_doc() {
RustDocFingerprint::check_rustdoc_fingerprint(&self)?
}

for unit in &self.bcx.roots {
// Build up a list of pending jobs, each of which represent
// compiling a particular package. No actual work is executed as
Expand Down
5 changes: 2 additions & 3 deletions src/cargo/core/compiler/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,7 @@ use lazycell::LazyCell;
use log::debug;

pub use self::build_config::{BuildConfig, CompileMode, MessageFormat};
pub use self::build_context::{
BuildContext, FileFlavor, FileType, RustDocFingerprint, RustcTargetData, TargetInfo,
};
pub use self::build_context::{BuildContext, FileFlavor, FileType, RustcTargetData, TargetInfo};
use self::build_plan::BuildPlan;
pub use self::compilation::{Compilation, Doctest, UnitOutput};
pub use self::compile_kind::{CompileKind, CompileTarget};
Expand Down Expand Up @@ -601,6 +599,7 @@ fn rustdoc(cx: &mut Context<'_, '_>, unit: &Unit) -> CargoResult<Work> {
if let CompileKind::Target(target) = unit.kind {
rustdoc.arg("--target").arg(target.rustc_target());
}

let doc_dir = cx.files().out_dir(unit);

// Create the documentation directory ahead of time as rustdoc currently has
Expand Down
4 changes: 2 additions & 2 deletions src/doc/src/reference/semver.md
Original file line number Diff line number Diff line change
Expand Up @@ -634,7 +634,7 @@ pub trait Trait<T> {}
use updated_crate::Trait;
struct Foo;
impl Trait for Foo {} // Error: wrong number of type arguments
impl Trait for Foo {} // Error: missing generics
```

Mitigating strategies:
Expand Down Expand Up @@ -943,7 +943,7 @@ pub fn foo<T, U>() {}
use updated_crate::foo;
fn main() {
foo::<u8>(); // Error: wrong number of type arguments
foo::<u8>(); // Error: this function takes 2 type arguments but only 1 type argument was supplied
}
```

Expand Down
2 changes: 1 addition & 1 deletion tests/testsuite/build_script.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ fn custom_build_script_failed() {
[ERROR] failed to run custom build command for `foo v0.5.0 ([CWD])`
Caused by:
process didn't exit successfully: `[..]/build-script-build` (exit code: 101)",
process didn't exit successfully: `[..]/build-script-build` (exit [..]: 101)",
)
.run();
}
Expand Down
177 changes: 0 additions & 177 deletions tests/testsuite/doc.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
//! Tests for the `cargo doc` command.

use cargo::core::compiler::RustDocFingerprint;
use cargo_test_support::paths::CargoPathExt;
use cargo_test_support::registry::Package;
use cargo_test_support::{basic_lib_manifest, basic_manifest, git, project};
Expand Down Expand Up @@ -1716,179 +1715,3 @@ test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out[..]
)
.run();
}

#[cargo_test]
fn doc_fingerprint_is_versioning_consistent() {
// Random rustc verbose version
let old_rustc_verbose_version = format!(
"\
rustc 1.41.1 (f3e1a954d 2020-02-24)
binary: rustc
commit-hash: f3e1a954d2ead4e2fc197c7da7d71e6c61bad196
commit-date: 2020-02-24
host: {}
release: 1.41.1
LLVM version: 9.0
",
rustc_host()
);

// Create the dummy project.
let dummy_project = project()
.file(
"Cargo.toml",
r#"
[package]
name = "foo"
version = "1.2.4"
authors = []
"#,
)
.file("src/lib.rs", "//! These are the docs!")
.build();

dummy_project.cargo("doc").run();

let fingerprint: RustDocFingerprint =
serde_json::from_str(&dummy_project.read_file("target/.rustdoc_fingerprint.json"))
.expect("JSON Serde fail");

// Check that the fingerprint contains the actual rustc version
// which has been used to compile the docs.
let output = std::process::Command::new("rustc")
.arg("-vV")
.output()
.expect("Failed to get actual rustc verbose version");
assert_eq!(
fingerprint.rustc_vv,
(String::from_utf8_lossy(&output.stdout).as_ref())
);

// As the test shows above. Now we have generated the `doc/` folder and inside
// the rustdoc fingerprint file is located with the correct rustc version.
// So we will remove it and create a new fingerprint with an old rustc version
// inside it. We will also place a bogus file inside of the `doc/` folder to ensure
// it gets removed as we expect on the next doc compilation.
dummy_project.change_file(
"target/.rustdoc_fingerprint.json",
&old_rustc_verbose_version,
);

fs::write(
dummy_project.build_dir().join("doc/bogus_file"),
String::from("This is a bogus file and should be removed!"),
)
.expect("Error writing test bogus file");

// Now if we trigger another compilation, since the fingerprint contains an old version
// of rustc, cargo should remove the entire `/doc` folder (including the fingerprint)
// and generating another one with the actual version.
// It should also remove the bogus file we created above.
dummy_project.cargo("doc").run();

assert!(!dummy_project.build_dir().join("doc/bogus_file").exists());

let fingerprint: RustDocFingerprint =
serde_json::from_str(&dummy_project.read_file("target/.rustdoc_fingerprint.json"))
.expect("JSON Serde fail");

// Check that the fingerprint contains the actual rustc version
// which has been used to compile the docs.
assert_eq!(
fingerprint.rustc_vv,
(String::from_utf8_lossy(&output.stdout).as_ref())
);
}

#[cfg(target_os = "linux")]
#[cargo_test]
fn doc_fingerprint_respects_target_paths() {
// Random rustc verbose version
let old_rustc_verbose_version = format!(
"\
rustc 1.41.1 (f3e1a954d 2020-02-24)
binary: rustc
commit-hash: f3e1a954d2ead4e2fc197c7da7d71e6c61bad196
commit-date: 2020-02-24
host: {}
release: 1.41.1
LLVM version: 9.0
",
rustc_host()
);

// Create the dummy project.
let dummy_project = project()
.file(
"Cargo.toml",
r#"
[package]
name = "foo"
version = "1.2.4"
authors = []
"#,
)
.file("src/lib.rs", "//! These are the docs!")
.build();

dummy_project
.cargo("doc --target x86_64-unknown-linux-gnu")
.run();

let fingerprint: RustDocFingerprint =
serde_json::from_str(&dummy_project.read_file("target/.rustdoc_fingerprint.json"))
.expect("JSON Serde fail");

// Check that the fingerprint contains the actual rustc version
// which has been used to compile the docs.
let output = std::process::Command::new("rustc")
.arg("-vV")
.output()
.expect("Failed to get actual rustc verbose version");
assert_eq!(
fingerprint.rustc_vv,
(String::from_utf8_lossy(&output.stdout).as_ref())
);

// As the test shows above. Now we have generated the `doc/` folder and inside
// the rustdoc fingerprint file is located with the correct rustc version.
// So we will remove it and create a new fingerprint with an old rustc version
// inside it. We will also place a bogus file inside of the `doc/` folder to ensure
// it gets removed as we expect on the next doc compilation.
dummy_project.change_file(
"target/.rustdoc_fingerprint.json",
&old_rustc_verbose_version,
);

fs::write(
dummy_project
.build_dir()
.join("x86_64-unknown-linux-gnu/doc/bogus_file"),
String::from("This is a bogus file and should be removed!"),
)
.expect("Error writing test bogus file");

// Now if we trigger another compilation, since the fingerprint contains an old version
// of rustc, cargo should remove the entire `/doc` folder (including the fingerprint)
// and generating another one with the actual version.
// It should also remove the bogus file we created above.
dummy_project
.cargo("doc --target x86_64-unknown-linux-gnu")
.run();

assert!(!dummy_project
.build_dir()
.join("x86_64-unknown-linux-gnu/doc/bogus_file")
.exists());

let fingerprint: RustDocFingerprint =
serde_json::from_str(&dummy_project.read_file("target/.rustdoc_fingerprint.json"))
.expect("JSON Serde fail");

// Check that the fingerprint contains the actual rustc version
// which has been used to compile the docs.
assert_eq!(
fingerprint.rustc_vv,
(String::from_utf8_lossy(&output.stdout).as_ref())
);
}
4 changes: 2 additions & 2 deletions tests/testsuite/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ fn exit_code() {
);
if !cfg!(unix) {
output.push_str(
"[ERROR] process didn't exit successfully: `target[..]foo[..]` (exit code: 2)",
"[ERROR] process didn't exit successfully: `target[..]foo[..]` (exit [..]: 2)",
);
}
p.cargo("run").with_status(2).with_stderr(output).run();
Expand All @@ -140,7 +140,7 @@ fn exit_code_verbose() {
);
if !cfg!(unix) {
output.push_str(
"[ERROR] process didn't exit successfully: `target[..]foo[..]` (exit code: 2)",
"[ERROR] process didn't exit successfully: `target[..]foo[..]` (exit [..]: 2)",
);
}

Expand Down