@@ -23,9 +23,12 @@ use rustwide::cmd::{Command, CommandError, SandboxBuilder, SandboxImage};
2323use  rustwide:: logging:: { self ,  LogStorage } ; 
2424use  rustwide:: toolchain:: ToolchainError ; 
2525use  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+ } ; 
2932use  tracing:: { debug,  info,  warn} ; 
3033
3134const  USER_AGENT :  & str  = "docs.rs builder (https://github.com/rust-lang/docs.rs)" ; 
@@ -244,9 +247,19 @@ impl RustwideBuilder {
244247            . run ( |build| { 
245248                ( || -> Result < ( ) >  { 
246249                    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" ) ?; 
247253
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+                     ) ?; 
250263                    if  !res. result . successful  { 
251264                        bail ! ( "failed to build dummy crate for {}" ,  self . rustc_version) ; 
252265                    } 
@@ -333,15 +346,15 @@ impl RustwideBuilder {
333346            return  Ok ( false ) ; 
334347        } 
335348
336-         self . update_toolchain ( ) ?; 
337- 
338-         info ! ( "building package {} {}" ,  name,  version) ; 
339- 
340349        if  is_blacklisted ( & mut  conn,  name) ? { 
341350            info ! ( "skipping build of {}, crate has been blacklisted" ,  name) ; 
342351            return  Ok ( false ) ; 
343352        } 
344353
354+         self . update_toolchain ( ) ?; 
355+ 
356+         info ! ( "building package {} {}" ,  name,  version) ; 
357+ 
345358        let  limits = Limits :: for_crate ( & mut  conn,  name) ?; 
346359        #[ cfg( target_os = "linux" ) ]  
347360        if  !self . config . disable_memory_limit  { 
@@ -388,18 +401,55 @@ impl RustwideBuilder {
388401                    default_target, 
389402                    other_targets, 
390403                }  = 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+ 
393428                // Fetch this before we enter the sandbox, so networking isn't blocked. 
394-                 build. fetch_build_std_dependencies ( & targets) ?; 
429+                 build. fetch_build_std_dependencies ( & { 
430+                     let  mut  targets = other_targets. clone ( ) ; 
431+                     targets. push ( default_target) ; 
432+                     targets
433+                 } ) ?; 
395434
396435                ( || -> Result < bool >  { 
436+                     let  deadline = Instant :: now ( ) 
437+                         . checked_add ( limits. timeout ( ) ) 
438+                         . context ( "deadline is not representable" ) ?; 
439+ 
397440                    let  mut  has_docs = false ; 
398441                    let  mut  successful_targets = Vec :: new ( ) ; 
399442
400443                    // Perform an initial build 
401-                     let  mut  res =
402-                         self . execute_build ( default_target,  true ,  build,  & limits,  & metadata,  false ) ?; 
444+                     let  mut  res = self . execute_build ( 
445+                         default_target, 
446+                         true , 
447+                         build, 
448+                         & limits, 
449+                         & metadata, 
450+                         false , 
451+                         deadline, 
452+                     ) ?; 
403453
404454                    // If the build fails with the lockfile given, try using only the dependencies listed in Cargo.toml. 
405455                    let  cargo_lock = build. host_source_dir ( ) . join ( "Cargo.lock" ) ; 
@@ -421,6 +471,7 @@ impl RustwideBuilder {
421471                            & limits, 
422472                            & metadata, 
423473                            false , 
474+                             deadline, 
424475                        ) ?; 
425476                    } 
426477
@@ -448,8 +499,7 @@ impl RustwideBuilder {
448499                        successful_targets. push ( res. target . clone ( ) ) ; 
449500
450501                        // 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 ( ) )  { 
502+                         for  target in  other_targets { 
453503                            debug ! ( "building package {} {} for {}" ,  name,  version,  target) ; 
454504                            self . build_target ( 
455505                                target, 
@@ -458,6 +508,7 @@ impl RustwideBuilder {
458508                                local_storage. path ( ) , 
459509                                & mut  successful_targets, 
460510                                & metadata, 
511+                                 deadline, 
461512                            ) ?; 
462513                        } 
463514                        let  ( _,  new_alg)  = add_path_into_remote_archive ( 
@@ -572,6 +623,7 @@ impl RustwideBuilder {
572623        Ok ( successful) 
573624    } 
574625
626+     #[ allow( clippy:: too_many_arguments) ]  
575627    fn  build_target ( 
576628        & self , 
577629        target :  & str , 
@@ -580,8 +632,10 @@ impl RustwideBuilder {
580632        local_storage :  & Path , 
581633        successful_targets :  & mut  Vec < String > , 
582634        metadata :  & Metadata , 
635+         deadline :  Instant , 
583636    )  -> Result < ( ) >  { 
584-         let  target_res = self . execute_build ( target,  false ,  build,  limits,  metadata,  false ) ?; 
637+         let  target_res =
638+             self . execute_build ( target,  false ,  build,  limits,  metadata,  false ,  deadline) ?; 
585639        if  target_res. result . successful  { 
586640            // Cargo is not giving any error and not generating documentation of some crates 
587641            // when we use a target compile options. Check documentation exists before 
@@ -600,7 +654,7 @@ impl RustwideBuilder {
600654        target :  & str , 
601655        build :  & Build , 
602656        metadata :  & Metadata , 
603-         limits :   & Limits , 
657+         deadline :   Instant , 
604658    )  -> Result < Option < DocCoverage > >  { 
605659        let  rustdoc_flags = vec ! [ 
606660            "--output-format" . to_string( ) , 
@@ -623,7 +677,7 @@ impl RustwideBuilder {
623677            items_with_examples :  0 , 
624678        } ; 
625679
626-         self . prepare_command ( build,  target,  metadata,  limits ,  rustdoc_flags ) ?
680+         self . prepare_command ( build,  target,  metadata,  rustdoc_flags ,  deadline ) ?
627681            . process_lines ( & mut  |line,  _| { 
628682                if  line. starts_with ( '{' )  && line. ends_with ( '}' )  { 
629683                    let  parsed = match  serde_json:: from_str :: < HashMap < String ,  FileCoverage > > ( line)  { 
@@ -650,6 +704,9 @@ impl RustwideBuilder {
650704        ) 
651705    } 
652706
707+     // TODO(Nemo157): Look at pulling out a sub-builder for each crate-build 
708+     // that holds a lot of this state 
709+     #[ allow( clippy:: too_many_arguments) ]  
653710    fn  execute_build ( 
654711        & self , 
655712        target :  & str , 
@@ -658,6 +715,7 @@ impl RustwideBuilder {
658715        limits :  & Limits , 
659716        metadata :  & Metadata , 
660717        create_essential_files :  bool , 
718+         deadline :  Instant , 
661719    )  -> Result < FullBuildResult >  { 
662720        let  cargo_metadata = CargoMetadata :: load_from_rustwide ( 
663721            & self . workspace , 
@@ -682,7 +740,7 @@ impl RustwideBuilder {
682740        // we have to run coverage before the doc-build because currently it 
683741        // deletes the doc-target folder. 
684742        // https://github.com/rust-lang/cargo/issues/9447 
685-         let  doc_coverage = match  self . get_coverage ( target,  build,  metadata,  limits )  { 
743+         let  doc_coverage = match  self . get_coverage ( target,  build,  metadata,  deadline )  { 
686744            Ok ( cov)  => cov, 
687745            Err ( err)  => { 
688746                info ! ( "error when trying to get coverage: {}" ,  err) ; 
@@ -692,10 +750,11 @@ impl RustwideBuilder {
692750        } ; 
693751
694752        let  successful = logging:: capture ( & storage,  || { 
695-             self . prepare_command ( build,  target,  metadata,  limits ,  rustdoc_flags ) 
753+             self . prepare_command ( build,  target,  metadata,  rustdoc_flags ,  deadline ) 
696754                . and_then ( |command| command. run ( ) . map_err ( Error :: from) ) 
697-                 . is_ok ( ) 
698-         } ) ; 
755+         } ) 
756+         . map_err ( |e| info ! ( "failed build: {e:?}" ) ) 
757+         . is_ok ( ) ; 
699758
700759        // For proc-macros, cargo will put the output in `target/doc`. 
701760        // Move it to the target-specific directory for consistency with other builds. 
@@ -732,9 +791,13 @@ impl RustwideBuilder {
732791        build :  & ' ws  Build , 
733792        target :  & str , 
734793        metadata :  & Metadata , 
735-         limits :  & Limits , 
736794        mut  rustdoc_flags_extras :  Vec < String > , 
795+         deadline :  Instant , 
737796    )  -> Result < Command < ' ws ,  ' pl > >  { 
797+         let  timeout = deadline
798+             . checked_duration_since ( Instant :: now ( ) ) 
799+             . context ( "exceeded deadline" ) ?; 
800+ 
738801        // Add docs.rs specific arguments 
739802        let  mut  cargo_args = vec ! [ 
740803            "--offline" . into( ) , 
@@ -783,22 +846,7 @@ impl RustwideBuilder {
783846        rustdoc_flags_extras. extend ( UNCONDITIONAL_ARGS . iter ( ) . map ( |& s| s. to_owned ( ) ) ) ; 
784847        let  cargo_args = metadata. cargo_args ( & cargo_args,  & rustdoc_flags_extras) ; 
785848
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 ) ; 
849+         let  mut  command = build. cargo ( ) . timeout ( Some ( timeout) ) . no_output_timeout ( None ) ; 
802850
803851        for  ( key,  val)  in  metadata. environment_variables ( )  { 
804852            command = command. env ( key,  val) ; 
@@ -885,7 +933,10 @@ pub(crate) struct BuildResult {
885933#[ cfg( test) ]  
886934mod  tests { 
887935    use  super :: * ; 
888-     use  crate :: test:: { assert_redirect,  assert_success,  wrapper,  TestEnvironment } ; 
936+     use  crate :: { 
937+         db:: Overrides , 
938+         test:: { assert_redirect,  assert_success,  wrapper,  TestEnvironment } , 
939+     } ; 
889940    use  serde_json:: Value ; 
890941
891942    fn  remove_cache_files ( env :  & TestEnvironment ,  crate_ :  & str ,  version :  & str )  -> Result < ( ) >  { 
@@ -1218,6 +1269,49 @@ mod tests {
12181269        } ) ; 
12191270    } 
12201271
1272+     #[ test]  
1273+     #[ ignore]  
1274+     fn  test_timeout_skips_some_targets ( )  { 
1275+         wrapper ( |env| { 
1276+             let  crate_ = "bs58" ; 
1277+             let  version = "0.5.0" ; 
1278+             let  mut  builder = RustwideBuilder :: init ( env) . unwrap ( ) ; 
1279+             let  get_targets = || -> i32  { 
1280+                 env. db ( ) 
1281+                     . conn ( ) 
1282+                     . query_one ( 
1283+                         "SELECT json_array_length(releases.doc_targets) FROM releases;" , 
1284+                         & [ ] , 
1285+                     ) 
1286+                     . unwrap ( ) 
1287+                     . get ( 0 ) 
1288+             } ; 
1289+ 
1290+             // Build once to time it and count how many targets are built 
1291+             let  start = Instant :: now ( ) ; 
1292+             assert ! ( builder. build_package( crate_,  version,  PackageKind :: CratesIo ) ?) ; 
1293+             let  timeout = start. elapsed ( )  / 2 ; 
1294+             let  original_targets = get_targets ( ) ; 
1295+ 
1296+             // Build again with half the time and count how many targets are built 
1297+             Overrides :: save ( 
1298+                 & mut  env. db ( ) . conn ( ) , 
1299+                 crate_, 
1300+                 Overrides  { 
1301+                     memory :  None , 
1302+                     targets :  Some ( original_targets as  usize ) , 
1303+                     timeout :  Some ( timeout) , 
1304+                 } , 
1305+             ) ?; 
1306+             assert ! ( builder. build_package( crate_,  version,  PackageKind :: CratesIo ) ?) ; 
1307+             let  new_targets = get_targets ( ) ; 
1308+ 
1309+             assert ! ( new_targets < original_targets) ; 
1310+ 
1311+             Ok ( ( ) ) 
1312+         } ) ; 
1313+     } 
1314+ 
12211315    #[ test]  
12221316    #[ ignore]  
12231317    fn  test_implicit_features_for_optional_dependencies ( )  { 
0 commit comments