-
Notifications
You must be signed in to change notification settings - Fork 2.5k
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
Implement Rustdoc versioning checks #8640
Changes from all commits
5035321
c70c2cc
51d3050
e38c3ac
7a5f784
87c1f6c
65f17e1
2fea14e
31ef7af
ebd0d58
994ccec
33a5248
94519f2
27987b6
47e19f0
8ddc56f
d2572a2
7c45021
cb21e64
3c93f6c
1af0027
7bfb3d0
e78f91c
4afa585
61c2332
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,15 @@ | ||
use crate::core::compiler::{BuildOutput, CompileKind, CompileMode, CompileTarget, CrateType}; | ||
use crate::core::compiler::{ | ||
BuildOutput, CompileKind, CompileMode, CompileTarget, Context, CrateType, | ||
}; | ||
use crate::core::{Dependency, Target, TargetKind, Workspace}; | ||
use crate::util::config::{Config, StringList, TargetConfig}; | ||
use crate::util::{CargoResult, CargoResultExt, ProcessBuilder, Rustc}; | ||
use crate::util::{paths, 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::PathBuf; | ||
use std::path::{Path, PathBuf}; | ||
use std::str::{self, FromStr}; | ||
|
||
/// Information about the platform target gleaned from querying rustc. | ||
|
@@ -748,3 +751,77 @@ 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 { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure it's really necessary to have a struct to wrap these methods. From what I can see, this can all be done in a single free function, and it should be substantially smaller. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I did it on that way so that if in the future more things need to be added to it, we already have the struct and it's easier to add functionalities. I can implement everything directly anyway if you prefer it. I was just thinking about the future, and more functionalities added to this fingerprinting feature for rustdoc. What should I do then? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think if it grows in complexity in the future, it can be reworked to be more abstract, but for the foreseeable future I wouldn't expect it to change much. |
||
/// Read the `RustDocFingerprint` info from the fingerprint file. | ||
fn read<'a, 'cfg>(cx: &Context<'a, 'cfg>) -> 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: &Vec<&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<'a, 'cfg>(cx: &Context<'a, 'cfg>) -> 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(()) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
curious, why are just using the rust version, as opposed to the full fingerprint of the build?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because the docfiles only depend on the compiler version, if it changes we always rebuild deleting the entire docs folder. Otherways, if the compiler version is the same, we know 100% sure that the docfiles are still valid and will not introduce any errors on the browser console or malfunctions.
We simply don't need anymore data than the version, that's why we don't use the full fingerprint of the build.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about if people change the feature they are building with, which causes different items to be built? Also is docs only depending on compiler version and no flags something that we guarantee?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you check the issue from which the PR was originated. The goal is to ensure that the html, css and js files are consistent with the compiler version currently used and not mix it up.
Any other thing stills being handled on the same way it was before. This has nothing to do with the content that you're documenting ie. target, features etc. But with the actual js, css and html code in the files of the docs and which version should they be used on.
Hope that makes it a bit more clear.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@guswynn Each crate has a "fingerprint" stored in the
.fingerprint
directory. The things it tracks are listed here. The fingerprint will detect if an individual crate needs to be rebuilt, but it doesn't detect when shared files (like the search index) need to be deleted and rebuilt. This adds a "global" fingerprint for building docs to detect when the rust toolchain changes (and may introduce an incompatible search index), and clears out the entire docs directory to deal with those changes. Changes to things like features only trigger the individual crate to be rebuilt, but it retains the shared files (which rustdoc updates incrementally).