77//! Everything here is basically just a shim around calling either `rustbook` or
88//! `rustdoc`.
99
10+ use std:: ffi:: OsStr ;
1011use std:: fs;
1112use std:: io;
1213use std:: path:: { Path , PathBuf } ;
@@ -431,49 +432,24 @@ impl Step for Std {
431432 fn run ( self , builder : & Builder < ' _ > ) {
432433 let stage = self . stage ;
433434 let target = self . target ;
434- builder. info ( & format ! ( "Documenting stage{} std ({})" , stage, target) ) ;
435- if builder. no_std ( target) == Some ( true ) {
436- panic ! (
437- "building std documentation for no_std target {target} is not supported\n \
438- Set `docs = false` in the config to disable documentation."
439- ) ;
440- }
441435 let out = builder. doc_out ( target) ;
442436 t ! ( fs:: create_dir_all( & out) ) ;
443- let compiler = builder. compiler ( stage, builder. config . build ) ;
444-
445- let out_dir = builder. stage_out ( compiler, Mode :: Std ) . join ( target. triple ) . join ( "doc" ) ;
446-
447437 t ! ( fs:: copy( builder. src. join( "src/doc/rust.css" ) , out. join( "rust.css" ) ) ) ;
448438
449- let run_cargo_rustdoc_for = |package : & str | {
450- let mut cargo =
451- builder. cargo ( compiler, Mode :: Std , SourceType :: InTree , target, "rustdoc" ) ;
452- compile:: std_cargo ( builder, target, compiler. stage , & mut cargo) ;
453-
454- cargo
455- . arg ( "-p" )
456- . arg ( package)
457- . arg ( "-Zskip-rustdoc-fingerprint" )
458- . arg ( "--" )
459- . arg ( "--markdown-css" )
460- . arg ( "rust.css" )
461- . arg ( "--markdown-no-toc" )
462- . arg ( "-Z" )
463- . arg ( "unstable-options" )
464- . arg ( "--resource-suffix" )
465- . arg ( & builder. version )
466- . arg ( "--index-page" )
467- . arg ( & builder. src . join ( "src/doc/index.md" ) ) ;
468-
469- if !builder. config . docs_minification {
470- cargo. arg ( "--disable-minification" ) ;
471- }
472-
473- builder. run ( & mut cargo. into ( ) ) ;
474- } ;
439+ let index_page = builder. src . join ( "src/doc/index.md" ) . into_os_string ( ) ;
440+ let mut extra_args = vec ! [
441+ OsStr :: new( "--markdown-css" ) ,
442+ OsStr :: new( "rust.css" ) ,
443+ OsStr :: new( "--markdown-no-toc" ) ,
444+ OsStr :: new( "--index-page" ) ,
445+ & index_page,
446+ ] ;
447+
448+ if !builder. config . docs_minification {
449+ extra_args. push ( OsStr :: new ( "--disable-minification" ) ) ;
450+ }
475451
476- let paths = builder
452+ let requested_crates = builder
477453 . paths
478454 . iter ( )
479455 . map ( components_simplified)
@@ -491,37 +467,155 @@ impl Step for Std {
491467 } )
492468 . collect :: < Vec < _ > > ( ) ;
493469
494- // Only build the following crates. While we could just iterate over the
495- // folder structure, that would also build internal crates that we do
496- // not want to show in documentation. These crates will later be visited
497- // by the rustc step, so internal documentation will show them.
498- //
499- // Note that the order here is important! The crates need to be
500- // processed starting from the leaves, otherwise rustdoc will not
501- // create correct links between crates because rustdoc depends on the
502- // existence of the output directories to know if it should be a local
503- // or remote link.
504- let krates = [ "core" , "alloc" , "std" , "proc_macro" , "test" ] ;
505- for krate in & krates {
506- run_cargo_rustdoc_for ( krate) ;
507- if paths. iter ( ) . any ( |p| p == krate) {
508- // No need to document more of the libraries if we have the one we want.
509- break ;
510- }
511- }
512- builder. cp_r ( & out_dir, & out) ;
470+ doc_std (
471+ builder,
472+ DocumentationFormat :: HTML ,
473+ stage,
474+ target,
475+ & out,
476+ & extra_args,
477+ & requested_crates,
478+ ) ;
513479
514480 // Look for library/std, library/core etc in the `x.py doc` arguments and
515481 // open the corresponding rendered docs.
516- for requested_crate in paths {
517- if krates . iter ( ) . any ( |k| * k == requested_crate. as_str ( ) ) {
482+ for requested_crate in requested_crates {
483+ if STD_PUBLIC_CRATES . iter ( ) . any ( |k| * k == requested_crate. as_str ( ) ) {
518484 let index = out. join ( requested_crate) . join ( "index.html" ) ;
519485 open ( builder, & index) ;
520486 }
521487 }
522488 }
523489}
524490
491+ #[ derive( Debug , Copy , Clone , Hash , PartialEq , Eq ) ]
492+ pub struct JsonStd {
493+ pub stage : u32 ,
494+ pub target : TargetSelection ,
495+ }
496+
497+ impl Step for JsonStd {
498+ type Output = ( ) ;
499+ const DEFAULT : bool = false ;
500+
501+ fn should_run ( run : ShouldRun < ' _ > ) -> ShouldRun < ' _ > {
502+ let default = run. builder . config . docs && run. builder . config . cmd . json ( ) ;
503+ run. all_krates ( "test" ) . path ( "library" ) . default_condition ( default)
504+ }
505+
506+ fn make_run ( run : RunConfig < ' _ > ) {
507+ run. builder . ensure ( Std { stage : run. builder . top_stage , target : run. target } ) ;
508+ }
509+
510+ /// Build JSON documentation for the standard library crates.
511+ ///
512+ /// This is largely just a wrapper around `cargo doc`.
513+ fn run ( self , builder : & Builder < ' _ > ) {
514+ let stage = self . stage ;
515+ let target = self . target ;
516+ let out = builder. json_doc_out ( target) ;
517+ t ! ( fs:: create_dir_all( & out) ) ;
518+ let extra_args = [ OsStr :: new ( "--output-format" ) , OsStr :: new ( "json" ) ] ;
519+ doc_std ( builder, DocumentationFormat :: JSON , stage, target, & out, & extra_args, & [ ] )
520+ }
521+ }
522+
523+ /// Name of the crates that are visible to consumers of the standard library.
524+ /// Documentation for internal crates is handled by the rustc step, so internal crates will show
525+ /// up there.
526+ ///
527+ /// Order here is important!
528+ /// Crates need to be processed starting from the leaves, otherwise rustdoc will not
529+ /// create correct links between crates because rustdoc depends on the
530+ /// existence of the output directories to know if it should be a local
531+ /// or remote link.
532+ const STD_PUBLIC_CRATES : [ & str ; 5 ] = [ "core" , "alloc" , "std" , "proc_macro" , "test" ] ;
533+
534+ #[ derive( Debug , Copy , Clone , Hash , PartialEq , Eq ) ]
535+ enum DocumentationFormat {
536+ HTML ,
537+ JSON ,
538+ }
539+
540+ impl DocumentationFormat {
541+ fn as_str ( & self ) -> & str {
542+ match self {
543+ DocumentationFormat :: HTML => "HTML" ,
544+ DocumentationFormat :: JSON => "JSON" ,
545+ }
546+ }
547+ }
548+
549+ /// Build the documentation for public standard library crates.
550+ ///
551+ /// `requested_crates` can be used to build only a subset of the crates. If empty, all crates will
552+ /// be built.
553+ fn doc_std (
554+ builder : & Builder < ' _ > ,
555+ format : DocumentationFormat ,
556+ stage : u32 ,
557+ target : TargetSelection ,
558+ out : & Path ,
559+ extra_args : & [ & OsStr ] ,
560+ requested_crates : & [ String ] ,
561+ ) {
562+ builder. info ( & format ! (
563+ "Documenting stage{} std ({}) in {} format" ,
564+ stage,
565+ target,
566+ format. as_str( )
567+ ) ) ;
568+ if builder. no_std ( target) == Some ( true ) {
569+ panic ! (
570+ "building std documentation for no_std target {target} is not supported\n \
571+ Set `docs = false` in the config to disable documentation."
572+ ) ;
573+ }
574+ let compiler = builder. compiler ( stage, builder. config . build ) ;
575+ // This is directory where the compiler will place the output of the command.
576+ // We will then copy the files from this directory into the final `out` directory, the specified
577+ // as a function parameter.
578+ let out_dir = builder. stage_out ( compiler, Mode :: Std ) . join ( target. triple ) . join ( "doc" ) ;
579+ // `cargo` uses the same directory for both JSON docs and HTML docs.
580+ // This could lead to cross-contamination when copying files into the specified `out` directory.
581+ // For example:
582+ // ```bash
583+ // x doc std
584+ // x doc std --json
585+ // ```
586+ // could lead to HTML docs being copied into the JSON docs output directory.
587+ // To avoid this issue, we clean the doc folder before invoking `cargo`.
588+ if out_dir. exists ( ) {
589+ builder. remove_dir ( & out_dir) ;
590+ }
591+
592+ let run_cargo_rustdoc_for = |package : & str | {
593+ let mut cargo = builder. cargo ( compiler, Mode :: Std , SourceType :: InTree , target, "rustdoc" ) ;
594+ compile:: std_cargo ( builder, target, compiler. stage , & mut cargo) ;
595+ cargo
596+ . arg ( "-p" )
597+ . arg ( package)
598+ . arg ( "-Zskip-rustdoc-fingerprint" )
599+ . arg ( "--" )
600+ . arg ( "-Z" )
601+ . arg ( "unstable-options" )
602+ . arg ( "--resource-suffix" )
603+ . arg ( & builder. version )
604+ . args ( extra_args) ;
605+ builder. run ( & mut cargo. into ( ) ) ;
606+ } ;
607+
608+ for krate in STD_PUBLIC_CRATES {
609+ run_cargo_rustdoc_for ( krate) ;
610+ if requested_crates. iter ( ) . any ( |p| p == krate) {
611+ // No need to document more of the libraries if we have the one we want.
612+ break ;
613+ }
614+ }
615+
616+ builder. cp_r ( & out_dir, & out) ;
617+ }
618+
525619#[ derive( Debug , Copy , Clone , Hash , PartialEq , Eq ) ]
526620pub struct Rustc {
527621 pub stage : u32 ,
0 commit comments