@@ -23,9 +23,12 @@ use rustwide::cmd::{Command, CommandError, SandboxBuilder, SandboxImage};
23
23
use rustwide:: logging:: { self , LogStorage } ;
24
24
use rustwide:: toolchain:: ToolchainError ;
25
25
use rustwide:: { AlternativeRegistry , Build , Crate , Toolchain , Workspace , WorkspaceBuilder } ;
26
- use std:: collections:: { HashMap , HashSet } ;
27
- use std:: path:: Path ;
28
- use std:: sync:: Arc ;
26
+ use std:: {
27
+ collections:: { HashMap , HashSet } ,
28
+ path:: Path ,
29
+ sync:: Arc ,
30
+ time:: Instant ,
31
+ } ;
29
32
use tracing:: { debug, info, warn} ;
30
33
31
34
const USER_AGENT : & str = "docs.rs builder (https://github.com/rust-lang/docs.rs)" ;
@@ -244,9 +247,19 @@ impl RustwideBuilder {
244
247
. run ( |build| {
245
248
( || -> Result < ( ) > {
246
249
let metadata = Metadata :: from_crate_root ( build. host_source_dir ( ) ) ?;
250
+ let deadline = Instant :: now ( )
251
+ . checked_add ( limits. timeout ( ) )
252
+ . context ( "deadline is not representable" ) ?;
247
253
248
- let res =
249
- self . execute_build ( HOST_TARGET , true , build, & limits, & metadata, true ) ?;
254
+ let res = self . execute_build (
255
+ HOST_TARGET ,
256
+ true ,
257
+ build,
258
+ & limits,
259
+ & metadata,
260
+ true ,
261
+ deadline,
262
+ ) ?;
250
263
if !res. result . successful {
251
264
bail ! ( "failed to build dummy crate for {}" , self . rustc_version) ;
252
265
}
@@ -333,15 +346,15 @@ impl RustwideBuilder {
333
346
return Ok ( false ) ;
334
347
}
335
348
336
- self . update_toolchain ( ) ?;
337
-
338
- info ! ( "building package {} {}" , name, version) ;
339
-
340
349
if is_blacklisted ( & mut conn, name) ? {
341
350
info ! ( "skipping build of {}, crate has been blacklisted" , name) ;
342
351
return Ok ( false ) ;
343
352
}
344
353
354
+ self . update_toolchain ( ) ?;
355
+
356
+ info ! ( "building package {} {}" , name, version) ;
357
+
345
358
let limits = Limits :: for_crate ( & mut conn, name) ?;
346
359
#[ cfg( target_os = "linux" ) ]
347
360
if !self . config . disable_memory_limit {
@@ -388,18 +401,56 @@ impl RustwideBuilder {
388
401
default_target,
389
402
other_targets,
390
403
} = metadata. targets ( self . config . include_default_targets ) ;
391
- let mut targets = vec ! [ default_target] ;
392
- targets. extend ( & other_targets) ;
404
+
405
+ let cargo_args = metadata. cargo_args ( & [ ] , & [ ] ) ;
406
+ let has_build_std = cargo_args. iter ( ) . any ( |arg| arg. starts_with ( "-Zbuild-std" ) )
407
+ || cargo_args
408
+ . windows ( 2 )
409
+ . any ( |args| args[ 0 ] == "-Z" && args[ 1 ] . starts_with ( "build-std" ) ) ;
410
+
411
+ let other_targets: Vec < _ > = other_targets
412
+ . into_iter ( )
413
+ . filter ( |target| {
414
+ // If the explicit target is not a tier one target, we need to install it.
415
+ if !docsrs_metadata:: DEFAULT_TARGETS . contains ( target) && !has_build_std {
416
+ // This is a no-op if the target is already installed.
417
+ if let Err ( e) = self . toolchain . add_target ( & self . workspace , target) {
418
+ info ! ( "Skipping target {target} since it failed to install: {e}" ) ;
419
+ return false ;
420
+ }
421
+ }
422
+ true
423
+ } )
424
+ // Limit the number of targets so that no one can try to build all 200000 possible targets
425
+ . take ( limits. targets ( ) )
426
+ . collect ( ) ;
427
+
428
+ let targets: Vec < _ > = [ & default_target]
429
+ . into_iter ( )
430
+ . chain ( & other_targets)
431
+ . copied ( )
432
+ . collect ( ) ;
393
433
// Fetch this before we enter the sandbox, so networking isn't blocked.
394
434
build. fetch_build_std_dependencies ( & targets) ?;
395
435
396
436
( || -> Result < bool > {
437
+ let deadline = Instant :: now ( )
438
+ . checked_add ( limits. timeout ( ) )
439
+ . context ( "deadline is not representable" ) ?;
440
+
397
441
let mut has_docs = false ;
398
442
let mut successful_targets = Vec :: new ( ) ;
399
443
400
444
// Perform an initial build
401
- let mut res =
402
- self . execute_build ( default_target, true , build, & limits, & metadata, false ) ?;
445
+ let mut res = self . execute_build (
446
+ default_target,
447
+ true ,
448
+ build,
449
+ & limits,
450
+ & metadata,
451
+ false ,
452
+ deadline,
453
+ ) ?;
403
454
404
455
// If the build fails with the lockfile given, try using only the dependencies listed in Cargo.toml.
405
456
let cargo_lock = build. host_source_dir ( ) . join ( "Cargo.lock" ) ;
@@ -421,6 +472,7 @@ impl RustwideBuilder {
421
472
& limits,
422
473
& metadata,
423
474
false ,
475
+ deadline,
424
476
) ?;
425
477
}
426
478
@@ -448,8 +500,7 @@ impl RustwideBuilder {
448
500
successful_targets. push ( res. target . clone ( ) ) ;
449
501
450
502
// Then build the documentation for all the targets
451
- // Limit the number of targets so that no one can try to build all 200000 possible targets
452
- for target in other_targets. into_iter ( ) . take ( limits. targets ( ) ) {
503
+ for target in other_targets {
453
504
debug ! ( "building package {} {} for {}" , name, version, target) ;
454
505
self . build_target (
455
506
target,
@@ -458,6 +509,7 @@ impl RustwideBuilder {
458
509
local_storage. path ( ) ,
459
510
& mut successful_targets,
460
511
& metadata,
512
+ deadline,
461
513
) ?;
462
514
}
463
515
let ( _, new_alg) = add_path_into_remote_archive (
@@ -572,6 +624,7 @@ impl RustwideBuilder {
572
624
Ok ( successful)
573
625
}
574
626
627
+ #[ allow( clippy:: too_many_arguments) ]
575
628
fn build_target (
576
629
& self ,
577
630
target : & str ,
@@ -580,8 +633,10 @@ impl RustwideBuilder {
580
633
local_storage : & Path ,
581
634
successful_targets : & mut Vec < String > ,
582
635
metadata : & Metadata ,
636
+ deadline : Instant ,
583
637
) -> Result < ( ) > {
584
- let target_res = self . execute_build ( target, false , build, limits, metadata, false ) ?;
638
+ let target_res =
639
+ self . execute_build ( target, false , build, limits, metadata, false , deadline) ?;
585
640
if target_res. result . successful {
586
641
// Cargo is not giving any error and not generating documentation of some crates
587
642
// when we use a target compile options. Check documentation exists before
@@ -600,7 +655,7 @@ impl RustwideBuilder {
600
655
target : & str ,
601
656
build : & Build ,
602
657
metadata : & Metadata ,
603
- limits : & Limits ,
658
+ deadline : Instant ,
604
659
) -> Result < Option < DocCoverage > > {
605
660
let rustdoc_flags = vec ! [
606
661
"--output-format" . to_string( ) ,
@@ -623,7 +678,7 @@ impl RustwideBuilder {
623
678
items_with_examples : 0 ,
624
679
} ;
625
680
626
- self . prepare_command ( build, target, metadata, limits , rustdoc_flags ) ?
681
+ self . prepare_command ( build, target, metadata, rustdoc_flags , deadline ) ?
627
682
. process_lines ( & mut |line, _| {
628
683
if line. starts_with ( '{' ) && line. ends_with ( '}' ) {
629
684
let parsed = match serde_json:: from_str :: < HashMap < String , FileCoverage > > ( line) {
@@ -650,6 +705,9 @@ impl RustwideBuilder {
650
705
)
651
706
}
652
707
708
+ // TODO(Nemo157): Look at pulling out a sub-builder for each crate-build
709
+ // that holds a lot of this state
710
+ #[ allow( clippy:: too_many_arguments) ]
653
711
fn execute_build (
654
712
& self ,
655
713
target : & str ,
@@ -658,6 +716,7 @@ impl RustwideBuilder {
658
716
limits : & Limits ,
659
717
metadata : & Metadata ,
660
718
create_essential_files : bool ,
719
+ deadline : Instant ,
661
720
) -> Result < FullBuildResult > {
662
721
let cargo_metadata = CargoMetadata :: load_from_rustwide (
663
722
& self . workspace ,
@@ -682,7 +741,7 @@ impl RustwideBuilder {
682
741
// we have to run coverage before the doc-build because currently it
683
742
// deletes the doc-target folder.
684
743
// https://github.com/rust-lang/cargo/issues/9447
685
- let doc_coverage = match self . get_coverage ( target, build, metadata, limits ) {
744
+ let doc_coverage = match self . get_coverage ( target, build, metadata, deadline ) {
686
745
Ok ( cov) => cov,
687
746
Err ( err) => {
688
747
info ! ( "error when trying to get coverage: {}" , err) ;
@@ -692,10 +751,11 @@ impl RustwideBuilder {
692
751
} ;
693
752
694
753
let successful = logging:: capture ( & storage, || {
695
- self . prepare_command ( build, target, metadata, limits , rustdoc_flags )
754
+ self . prepare_command ( build, target, metadata, rustdoc_flags , deadline )
696
755
. and_then ( |command| command. run ( ) . map_err ( Error :: from) )
697
- . is_ok ( )
698
- } ) ;
756
+ } )
757
+ . map_err ( |e| info ! ( "failed build: {e:?}" ) )
758
+ . is_ok ( ) ;
699
759
700
760
// For proc-macros, cargo will put the output in `target/doc`.
701
761
// Move it to the target-specific directory for consistency with other builds.
@@ -732,9 +792,13 @@ impl RustwideBuilder {
732
792
build : & ' ws Build ,
733
793
target : & str ,
734
794
metadata : & Metadata ,
735
- limits : & Limits ,
736
795
mut rustdoc_flags_extras : Vec < String > ,
796
+ deadline : Instant ,
737
797
) -> Result < Command < ' ws , ' pl > > {
798
+ let timeout = deadline
799
+ . checked_duration_since ( Instant :: now ( ) )
800
+ . context ( "exceeded deadline" ) ?;
801
+
738
802
// Add docs.rs specific arguments
739
803
let mut cargo_args = vec ! [
740
804
"--offline" . into( ) ,
@@ -783,22 +847,7 @@ impl RustwideBuilder {
783
847
rustdoc_flags_extras. extend ( UNCONDITIONAL_ARGS . iter ( ) . map ( |& s| s. to_owned ( ) ) ) ;
784
848
let cargo_args = metadata. cargo_args ( & cargo_args, & rustdoc_flags_extras) ;
785
849
786
- // If the explicit target is not a tier one target, we need to install it.
787
- let has_build_std = cargo_args. windows ( 2 ) . any ( |args| {
788
- args[ 0 ] . starts_with ( "-Zbuild-std" )
789
- || ( args[ 0 ] == "-Z" && args[ 1 ] . starts_with ( "build-std" ) )
790
- } ) || cargo_args. last ( ) . unwrap ( ) . starts_with ( "-Zbuild-std" ) ;
791
- if !docsrs_metadata:: DEFAULT_TARGETS . contains ( & target) && !has_build_std {
792
- // This is a no-op if the target is already installed.
793
- self . toolchain
794
- . add_target ( & self . workspace , target)
795
- . map_err ( FailureError :: compat) ?;
796
- }
797
-
798
- let mut command = build
799
- . cargo ( )
800
- . timeout ( Some ( limits. timeout ( ) ) )
801
- . no_output_timeout ( None ) ;
850
+ let mut command = build. cargo ( ) . timeout ( Some ( timeout) ) . no_output_timeout ( None ) ;
802
851
803
852
for ( key, val) in metadata. environment_variables ( ) {
804
853
command = command. env ( key, val) ;
@@ -885,7 +934,10 @@ pub(crate) struct BuildResult {
885
934
#[ cfg( test) ]
886
935
mod tests {
887
936
use super :: * ;
888
- use crate :: test:: { assert_redirect, assert_success, wrapper, TestEnvironment } ;
937
+ use crate :: {
938
+ db:: Overrides ,
939
+ test:: { assert_redirect, assert_success, wrapper, TestEnvironment } ,
940
+ } ;
889
941
use serde_json:: Value ;
890
942
891
943
fn remove_cache_files ( env : & TestEnvironment , crate_ : & str , version : & str ) -> Result < ( ) > {
@@ -1218,6 +1270,49 @@ mod tests {
1218
1270
} ) ;
1219
1271
}
1220
1272
1273
+ #[ test]
1274
+ #[ ignore]
1275
+ fn test_timeout_skips_some_targets ( ) {
1276
+ wrapper ( |env| {
1277
+ let crate_ = "bs58" ;
1278
+ let version = "0.5.0" ;
1279
+ let mut builder = RustwideBuilder :: init ( env) . unwrap ( ) ;
1280
+ let get_targets = || -> i32 {
1281
+ env. db ( )
1282
+ . conn ( )
1283
+ . query_one (
1284
+ "SELECT json_array_length(releases.doc_targets) FROM releases;" ,
1285
+ & [ ] ,
1286
+ )
1287
+ . unwrap ( )
1288
+ . get ( 0 )
1289
+ } ;
1290
+
1291
+ // Build once to time it and count how many targets are built
1292
+ let start = Instant :: now ( ) ;
1293
+ assert ! ( builder. build_package( crate_, version, PackageKind :: CratesIo ) ?) ;
1294
+ let timeout = start. elapsed ( ) / 2 ;
1295
+ let original_targets = get_targets ( ) ;
1296
+
1297
+ // Build again with half the time and count how many targets are built
1298
+ Overrides :: save (
1299
+ & mut env. db ( ) . conn ( ) ,
1300
+ crate_,
1301
+ Overrides {
1302
+ memory : None ,
1303
+ targets : Some ( original_targets as usize ) ,
1304
+ timeout : Some ( timeout) ,
1305
+ } ,
1306
+ ) ?;
1307
+ assert ! ( builder. build_package( crate_, version, PackageKind :: CratesIo ) ?) ;
1308
+ let new_targets = get_targets ( ) ;
1309
+
1310
+ assert ! ( new_targets < original_targets) ;
1311
+
1312
+ Ok ( ( ) )
1313
+ } ) ;
1314
+ }
1315
+
1221
1316
#[ test]
1222
1317
#[ ignore]
1223
1318
fn test_implicit_features_for_optional_dependencies ( ) {
0 commit comments