Skip to content

Commit 1eb4260

Browse files
committed
Change timeout to apply to the overall build process, not per-build
1 parent d8ea483 commit 1eb4260

3 files changed

+140
-29
lines changed

.sqlx/query-a4cf6f9051e04118ca5ed67813d26c5deee3e17a11a070b6e8d0879b93e68152.json

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

.sqlx/query-bc0b3932dc2f8bd2b8a9f5a312262eafefd3b80b3322116448901aa55f2d89e7.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/docbuilder/rustwide_builder.rs

Lines changed: 119 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -302,9 +302,20 @@ impl RustwideBuilder {
302302
.build(&self.toolchain, &krate, self.prepare_sandbox(&limits))
303303
.run(|build| {
304304
let metadata = Metadata::from_crate_root(build.host_source_dir())?;
305+
let deadline = Instant::now()
306+
.checked_add(limits.timeout())
307+
.context("deadline is not representable")?;
305308

306-
let res =
307-
self.execute_build(HOST_TARGET, true, build, &limits, &metadata, true, false)?;
309+
let res = self.execute_build(
310+
HOST_TARGET,
311+
true,
312+
build,
313+
&limits,
314+
&metadata,
315+
true,
316+
false,
317+
deadline,
318+
)?;
308319
if !res.result.successful {
309320
bail!("failed to build dummy crate for {}", rustc_version);
310321
}
@@ -431,6 +442,7 @@ impl RustwideBuilder {
431442
}
432443

433444
let limits = self.get_limits(name)?;
445+
434446
#[cfg(target_os = "linux")]
435447
if !self.config.disable_memory_limit {
436448
use anyhow::Context;
@@ -509,21 +521,50 @@ impl RustwideBuilder {
509521
default_target,
510522
other_targets,
511523
} = metadata.targets(self.config.include_default_targets);
512-
let mut targets = vec![default_target];
513-
targets.extend(&other_targets);
524+
525+
let cargo_args = metadata.cargo_args(&[], &[]);
526+
let has_build_std = cargo_args.iter().any(|arg| arg.starts_with("-Zbuild-std"))
527+
|| cargo_args
528+
.windows(2)
529+
.any(|args| args[0] == "-Z" && args[1].starts_with("build-std"));
530+
531+
let other_targets: Vec<_> = other_targets
532+
.into_iter()
533+
.filter(|target| {
534+
// If the explicit target is not a tier one target, we need to install it.
535+
if !docsrs_metadata::DEFAULT_TARGETS.contains(target) && !has_build_std {
536+
// This is a no-op if the target is already installed.
537+
if let Err(e) = self.toolchain.add_target(&self.workspace, target) {
538+
info!("Skipping target {target} since it failed to install: {e}");
539+
return false;
540+
}
541+
}
542+
true
543+
})
544+
// Limit the number of targets so that no one can try to build all 200000 possible targets
545+
.take(limits.targets())
546+
.collect();
514547

515548
{
516549
let _span = info_span!("fetch_build_std_dependencies").entered();
517550
// Fetch this before we enter the sandbox, so networking isn't blocked.
518-
build.fetch_build_std_dependencies(&targets)?;
551+
build.fetch_build_std_dependencies(&{
552+
let mut targets = other_targets.clone();
553+
targets.push(default_target);
554+
targets
555+
})?;
519556
}
520557

521558
let mut has_docs = false;
522559
let mut successful_targets = Vec::new();
523560

561+
let deadline = Instant::now()
562+
.checked_add(limits.timeout())
563+
.context("deadline is not representable")?;
564+
524565
// Perform an initial build
525566
let mut res =
526-
self.execute_build(default_target, true, build, &limits, &metadata, false, collect_metrics)?;
567+
self.execute_build(default_target, true, build, &limits, &metadata, false, collect_metrics, deadline)?;
527568

528569
// If the build fails with the lockfile given, try using only the dependencies listed in Cargo.toml.
529570
let cargo_lock = build.host_source_dir().join("Cargo.lock");
@@ -545,7 +586,7 @@ impl RustwideBuilder {
545586
.run_capture()?;
546587
}
547588
res =
548-
self.execute_build(default_target, true, build, &limits, &metadata, false, collect_metrics)?;
589+
self.execute_build(default_target, true, build, &limits, &metadata, false, collect_metrics, deadline)?;
549590
}
550591

551592
if res.result.successful {
@@ -572,8 +613,7 @@ impl RustwideBuilder {
572613
successful_targets.push(res.target.clone());
573614

574615
// Then build the documentation for all the targets
575-
// Limit the number of targets so that no one can try to build all 200000 possible targets
576-
for target in other_targets.into_iter().take(limits.targets()) {
616+
for target in other_targets {
577617
debug!("building package {} {} for {}", name, version, target);
578618
let target_res = self.build_target(
579619
target,
@@ -583,6 +623,7 @@ impl RustwideBuilder {
583623
&mut successful_targets,
584624
&metadata,
585625
collect_metrics,
626+
deadline,
586627
)?;
587628
target_build_logs.insert(target, target_res.build_log);
588629
}
@@ -758,6 +799,7 @@ impl RustwideBuilder {
758799
successful_targets: &mut Vec<String>,
759800
metadata: &Metadata,
760801
collect_metrics: bool,
802+
deadline: Instant,
761803
) -> Result<FullBuildResult> {
762804
let target_res = self.execute_build(
763805
target,
@@ -767,6 +809,7 @@ impl RustwideBuilder {
767809
metadata,
768810
false,
769811
collect_metrics,
812+
deadline,
770813
)?;
771814
if target_res.result.successful {
772815
// Cargo is not giving any error and not generating documentation of some crates
@@ -787,7 +830,7 @@ impl RustwideBuilder {
787830
target: &str,
788831
build: &Build,
789832
metadata: &Metadata,
790-
limits: &Limits,
833+
deadline: Instant,
791834
) -> Result<Option<DocCoverage>> {
792835
let rustdoc_flags = vec![
793836
"--output-format".to_string(),
@@ -810,7 +853,7 @@ impl RustwideBuilder {
810853
items_with_examples: 0,
811854
};
812855

813-
self.prepare_command(build, target, metadata, limits, rustdoc_flags, false)?
856+
self.prepare_command(build, target, metadata, rustdoc_flags, false, deadline)?
814857
.process_lines(&mut |line, _| {
815858
if line.starts_with('{') && line.ends_with('}') {
816859
let parsed = match serde_json::from_str::<HashMap<String, FileCoverage>>(line) {
@@ -848,6 +891,7 @@ impl RustwideBuilder {
848891
metadata: &Metadata,
849892
create_essential_files: bool,
850893
collect_metrics: bool,
894+
deadline: Instant,
851895
) -> Result<FullBuildResult> {
852896
let cargo_metadata = CargoMetadata::load_from_rustwide(
853897
&self.workspace,
@@ -874,7 +918,7 @@ impl RustwideBuilder {
874918
// we have to run coverage before the doc-build because currently it
875919
// deletes the doc-target folder.
876920
// https://github.com/rust-lang/cargo/issues/9447
877-
let doc_coverage = match self.get_coverage(target, build, metadata, limits) {
921+
let doc_coverage = match self.get_coverage(target, build, metadata, deadline) {
878922
Ok(cov) => cov,
879923
Err(err) => {
880924
info!("error when trying to get coverage: {}", err);
@@ -890,9 +934,9 @@ impl RustwideBuilder {
890934
build,
891935
target,
892936
metadata,
893-
limits,
894937
rustdoc_flags,
895938
collect_metrics,
939+
deadline,
896940
)
897941
.and_then(|command| command.run().map_err(Error::from))
898942
.is_ok()
@@ -949,10 +993,14 @@ impl RustwideBuilder {
949993
build: &'ws Build,
950994
target: &str,
951995
metadata: &Metadata,
952-
limits: &Limits,
953996
mut rustdoc_flags_extras: Vec<String>,
954997
collect_metrics: bool,
998+
deadline: Instant,
955999
) -> Result<Command<'ws, 'pl>> {
1000+
let timeout = deadline
1001+
.checked_duration_since(Instant::now())
1002+
.context("exceeded deadline")?;
1003+
9561004
// Add docs.rs specific arguments
9571005
let mut cargo_args = vec![
9581006
"--offline".into(),
@@ -1000,20 +1048,7 @@ impl RustwideBuilder {
10001048
rustdoc_flags_extras.extend(UNCONDITIONAL_ARGS.iter().map(|&s| s.to_owned()));
10011049
let mut cargo_args = metadata.cargo_args(&cargo_args, &rustdoc_flags_extras);
10021050

1003-
// If the explicit target is not a tier one target, we need to install it.
1004-
let has_build_std = cargo_args.windows(2).any(|args| {
1005-
args[0].starts_with("-Zbuild-std")
1006-
|| (args[0] == "-Z" && args[1].starts_with("build-std"))
1007-
}) || cargo_args.last().unwrap().starts_with("-Zbuild-std");
1008-
if !docsrs_metadata::DEFAULT_TARGETS.contains(&target) && !has_build_std {
1009-
// This is a no-op if the target is already installed.
1010-
self.toolchain.add_target(&self.workspace, target)?;
1011-
}
1012-
1013-
let mut command = build
1014-
.cargo()
1015-
.timeout(Some(limits.timeout()))
1016-
.no_output_timeout(None);
1051+
let mut command = build.cargo().timeout(Some(timeout)).no_output_timeout(None);
10171052

10181053
for (key, val) in metadata.environment_variables() {
10191054
command = command.env(key, val);
@@ -1117,6 +1152,7 @@ mod tests {
11171152
use std::iter;
11181153

11191154
use super::*;
1155+
use crate::db::Overrides;
11201156
use crate::db::types::Feature;
11211157
use crate::registry_api::ReleaseData;
11221158
use crate::storage::CompressionAlgorithm;
@@ -1741,6 +1777,61 @@ mod tests {
17411777
});
17421778
}
17431779

1780+
#[test]
1781+
#[ignore]
1782+
fn test_timeout_skips_some_targets() {
1783+
wrapper(|env| {
1784+
let crate_ = "bs58";
1785+
let version = "0.5.0";
1786+
let mut builder = RustwideBuilder::init(env).unwrap();
1787+
let get_targets =
1788+
|| -> i32 {
1789+
env.runtime().block_on(async {
1790+
sqlx::query!(
1791+
"SELECT json_array_length(releases.doc_targets) as length FROM releases;",
1792+
)
1793+
.fetch_one(&mut *env.async_db().await.async_conn().await)
1794+
.await.unwrap()
1795+
.length.unwrap()
1796+
})
1797+
};
1798+
1799+
// Build once to time it and count how many targets are built
1800+
let start = Instant::now();
1801+
assert!(
1802+
builder
1803+
.build_package(crate_, version, PackageKind::CratesIo, false)?
1804+
.successful
1805+
);
1806+
let timeout = start.elapsed() / 2;
1807+
let original_targets = get_targets();
1808+
1809+
// Build again with half the time and count how many targets are built
1810+
env.runtime().block_on(async {
1811+
Overrides::save(
1812+
&mut *env.async_db().await.async_conn().await,
1813+
crate_,
1814+
Overrides {
1815+
memory: None,
1816+
targets: Some(original_targets as usize),
1817+
timeout: Some(timeout),
1818+
},
1819+
)
1820+
.await
1821+
})?;
1822+
assert!(
1823+
builder
1824+
.build_package(crate_, version, PackageKind::CratesIo, false)?
1825+
.successful
1826+
);
1827+
let new_targets = get_targets();
1828+
1829+
assert!(new_targets < original_targets);
1830+
1831+
Ok(())
1832+
});
1833+
}
1834+
17441835
#[test]
17451836
#[ignore]
17461837
fn test_implicit_features_for_optional_dependencies() {

0 commit comments

Comments
 (0)