|
1 | 1 | //! [`BuildRunner`] is the mutable state used during the build process. |
2 | 2 |
|
3 | 3 | use std::collections::{HashMap, HashSet}; |
| 4 | +use std::ffi::OsStr; |
4 | 5 | use std::path::{Path, PathBuf}; |
5 | 6 | use std::sync::{Arc, Mutex}; |
6 | 7 |
|
@@ -230,6 +231,8 @@ impl<'a, 'gctx> BuildRunner<'a, 'gctx> { |
230 | 231 | } |
231 | 232 | } |
232 | 233 |
|
| 234 | + self.collect_doc_merge_info()?; |
| 235 | + |
233 | 236 | // Collect the result of the build into `self.compilation`. |
234 | 237 | for unit in &self.bcx.roots { |
235 | 238 | self.collect_tests_and_executables(unit)?; |
@@ -335,6 +338,132 @@ impl<'a, 'gctx> BuildRunner<'a, 'gctx> { |
335 | 338 | Ok(()) |
336 | 339 | } |
337 | 340 |
|
| 341 | + fn collect_doc_merge_info(&mut self) -> CargoResult<()> { |
| 342 | + if !self.bcx.gctx.cli_unstable().rustdoc_mergeable_info { |
| 343 | + return Ok(()); |
| 344 | + } |
| 345 | + |
| 346 | + if !self.bcx.build_config.intent.is_doc() { |
| 347 | + return Ok(()); |
| 348 | + } |
| 349 | + |
| 350 | + if self.bcx.build_config.intent.wants_doc_json_output() { |
| 351 | + // rustdoc JSON output doesn't support merge (yet?) |
| 352 | + return Ok(()); |
| 353 | + } |
| 354 | + |
| 355 | + let mut doc_merge_info = HashMap::new(); |
| 356 | + |
| 357 | + let unit_iter = if self.bcx.build_config.intent.wants_deps_docs() { |
| 358 | + itertools::Either::Left(self.bcx.unit_graph.keys()) |
| 359 | + } else { |
| 360 | + itertools::Either::Right(self.bcx.roots.iter()) |
| 361 | + }; |
| 362 | + |
| 363 | + for unit in unit_iter { |
| 364 | + let has_doc_parts = unit.mode.is_doc() |
| 365 | + && self |
| 366 | + .outputs(unit)? |
| 367 | + .iter() |
| 368 | + .any(|o| matches!(o.flavor, FileFlavor::DocParts)); |
| 369 | + if !has_doc_parts { |
| 370 | + continue; |
| 371 | + } |
| 372 | + |
| 373 | + doc_merge_info.entry(unit.kind).or_insert_with(|| { |
| 374 | + let out_dir = self |
| 375 | + .files() |
| 376 | + .layout(unit.kind) |
| 377 | + .artifact_dir() |
| 378 | + .expect("artifact-dir was not locked") |
| 379 | + .doc() |
| 380 | + .to_owned(); |
| 381 | + let docdeps_dir = self.files().docdeps_dir(unit); |
| 382 | + |
| 383 | + let mut requires_merge = false; |
| 384 | + |
| 385 | + // HACK: get mtime of crates.js to inform outside |
| 386 | + // whether we need to merge cross-crate info. |
| 387 | + // The content of `crates.js` looks like |
| 388 | + // |
| 389 | + // ``` |
| 390 | + // window.ALL_CRATES = ["cargo","cargo_util","cargo_util_schemas","crates_io"] |
| 391 | + // ``` |
| 392 | + // |
| 393 | + // and will be updated when any new crate got documented |
| 394 | + // even with the legacy `--merge=shared` mode. |
| 395 | + let crates_js = out_dir.join("crates.js"); |
| 396 | + let crates_js_mtime = paths::mtime(&crates_js); |
| 397 | + |
| 398 | + let mut num_crates = 0; |
| 399 | + |
| 400 | + for entry in walkdir::WalkDir::new(docdeps_dir).max_depth(1) { |
| 401 | + let Ok(entry) = entry else { |
| 402 | + tracing::debug!("failed to read entry at {}", docdeps_dir.display()); |
| 403 | + continue; |
| 404 | + }; |
| 405 | + |
| 406 | + if !entry.file_type().is_file() |
| 407 | + || entry.path().extension() != Some(OsStr::new("json")) |
| 408 | + { |
| 409 | + continue; |
| 410 | + } |
| 411 | + |
| 412 | + num_crates += 1; |
| 413 | + |
| 414 | + if requires_merge { |
| 415 | + continue; |
| 416 | + } |
| 417 | + |
| 418 | + let crates_js_mtime = match crates_js_mtime { |
| 419 | + Ok(mtime) => mtime, |
| 420 | + Err(ref err) => { |
| 421 | + tracing::debug!( |
| 422 | + ?err, |
| 423 | + "failed to read mtime of {}", |
| 424 | + crates_js.display() |
| 425 | + ); |
| 426 | + requires_merge = true; |
| 427 | + continue; |
| 428 | + } |
| 429 | + }; |
| 430 | + |
| 431 | + let parts_mtime = match paths::mtime(entry.path()) { |
| 432 | + Ok(mtime) => mtime, |
| 433 | + Err(err) => { |
| 434 | + tracing::debug!( |
| 435 | + ?err, |
| 436 | + "failed to read mtime of {}", |
| 437 | + entry.path().display() |
| 438 | + ); |
| 439 | + requires_merge = true; |
| 440 | + continue; |
| 441 | + } |
| 442 | + }; |
| 443 | + |
| 444 | + if parts_mtime > crates_js_mtime { |
| 445 | + requires_merge = true; |
| 446 | + continue; |
| 447 | + } |
| 448 | + } |
| 449 | + |
| 450 | + if requires_merge { |
| 451 | + compilation::DocMergeInfo::Merge { |
| 452 | + num_crates, |
| 453 | + parts_dir: docdeps_dir.to_owned(), |
| 454 | + out_dir, |
| 455 | + } |
| 456 | + } else { |
| 457 | + compilation::DocMergeInfo::Fresh |
| 458 | + } |
| 459 | + }); |
| 460 | + } |
| 461 | + |
| 462 | + self.compilation.doc_merge_info = doc_merge_info; |
| 463 | + |
| 464 | + Ok(()) |
| 465 | + } |
| 466 | + |
338 | 467 | /// Returns the executable for the specified unit (if any). |
339 | 468 | pub fn get_executable(&mut self, unit: &Unit) -> CargoResult<Option<PathBuf>> { |
340 | 469 | let is_binary = unit.target.is_executable(); |
|
0 commit comments