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 } ;
@@ -432,48 +433,24 @@ impl Step for Std {
432433 let stage = self . stage ;
433434 let target = self . target ;
434435 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- }
441436 let out = builder. doc_out ( target) ;
442437 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-
447438 t ! ( fs:: copy( builder. src. join( "src/doc/rust.css" ) , out. join( "rust.css" ) ) ) ;
448439
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- } ;
440+ let index_page = builder. src . join ( "src/doc/index.md" ) . into_os_string ( ) ;
441+ let mut extra_args = vec ! [
442+ OsStr :: new( "--markdown-css" ) ,
443+ OsStr :: new( "rust.css" ) ,
444+ OsStr :: new( "--markdown-no-toc" ) ,
445+ OsStr :: new( "--index-page" ) ,
446+ & index_page,
447+ ] ;
448+
449+ if !builder. config . docs_minification {
450+ extra_args. push ( OsStr :: new ( "--disable-minification" ) ) ;
451+ }
475452
476- let paths = builder
453+ let requested_crates = builder
477454 . paths
478455 . iter ( )
479456 . map ( components_simplified)
@@ -491,37 +468,153 @@ impl Step for Std {
491468 } )
492469 . collect :: < Vec < _ > > ( ) ;
493470
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) ;
471+ doc_std (
472+ builder,
473+ DocumentationFormat :: HTML ,
474+ stage,
475+ target,
476+ & out,
477+ & extra_args,
478+ & requested_crates,
479+ ) ;
513480
514481 // Look for library/std, library/core etc in the `x.py doc` arguments and
515482 // open the corresponding rendered docs.
516- for requested_crate in paths {
517- if krates . iter ( ) . any ( |k| * k == requested_crate. as_str ( ) ) {
483+ for requested_crate in requested_crates {
484+ if STD_PUBLIC_CRATES . iter ( ) . any ( |k| * k == requested_crate. as_str ( ) ) {
518485 let index = out. join ( requested_crate) . join ( "index.html" ) ;
519486 open ( builder, & index) ;
520487 }
521488 }
522489 }
523490}
524491
492+ #[ derive( Debug , Copy , Clone , Hash , PartialEq , Eq ) ]
493+ pub struct JsonStd {
494+ pub stage : u32 ,
495+ pub target : TargetSelection ,
496+ }
497+
498+ impl Step for JsonStd {
499+ type Output = ( ) ;
500+ const DEFAULT : bool = false ;
501+
502+ fn should_run ( run : ShouldRun < ' _ > ) -> ShouldRun < ' _ > {
503+ let default = run. builder . config . docs && run. builder . config . cmd . json ( ) ;
504+ run. all_krates ( "test" ) . path ( "library" ) . default_condition ( default)
505+ }
506+
507+ fn make_run ( run : RunConfig < ' _ > ) {
508+ run. builder . ensure ( Std { stage : run. builder . top_stage , target : run. target } ) ;
509+ }
510+
511+ /// Build JSON documentation for the standard library crates.
512+ ///
513+ /// This is largely just a wrapper around `cargo doc`.
514+ fn run ( self , builder : & Builder < ' _ > ) {
515+ let stage = self . stage ;
516+ let target = self . target ;
517+ let out = builder. json_doc_out ( target) ;
518+ t ! ( fs:: create_dir_all( & out) ) ;
519+ let extra_args = [ OsStr :: new ( "--output-format" ) , OsStr :: new ( "json" ) ] ;
520+ doc_std ( builder, DocumentationFormat :: JSON , stage, target, & out, & extra_args, & [ ] )
521+ }
522+ }
523+
524+ /// Name of the crates that are visible to consumers of the standard library.
525+ /// Documentation for internal crates is handled by the rustc step, so internal crates will show
526+ /// up there.
527+ ///
528+ /// Order here is important!
529+ /// Crates need to be processed starting from the leaves, otherwise rustdoc will not
530+ /// create correct links between crates because rustdoc depends on the
531+ /// existence of the output directories to know if it should be a local
532+ /// or remote link.
533+ const STD_PUBLIC_CRATES : [ & str ; 5 ] = [ "core" , "alloc" , "std" , "proc_macro" , "test" ] ;
534+
535+ #[ derive( Debug , Copy , Clone , Hash , PartialEq , Eq ) ]
536+ enum DocumentationFormat {
537+ HTML ,
538+ JSON ,
539+ }
540+
541+ impl DocumentationFormat {
542+ fn as_str ( & self ) -> & str {
543+ match self {
544+ DocumentationFormat :: HTML => "HTML" ,
545+ DocumentationFormat :: JSON => "JSON" ,
546+ }
547+ }
548+ }
549+
550+ /// Build the documentation for public standard library crates.
551+ ///
552+ /// `requested_crates` can be used to build only a subset of the crates. If empty, all crates will
553+ /// be built.
554+ fn doc_std (
555+ builder : & Builder < ' _ > ,
556+ format : DocumentationFormat ,
557+ stage : u32 ,
558+ target : TargetSelection ,
559+ out : & Path ,
560+ extra_args : & [ & OsStr ] ,
561+ requested_crates : & [ String ] ,
562+ ) {
563+ builder. info ( & format ! (
564+ "Documenting stage{} std ({}) in {} format" ,
565+ stage,
566+ target,
567+ format. as_str( )
568+ ) ) ;
569+ if builder. no_std ( target) == Some ( true ) {
570+ panic ! (
571+ "building std documentation for no_std target {target} is not supported\n \
572+ Set `docs = false` in the config to disable documentation."
573+ ) ;
574+ }
575+ let compiler = builder. compiler ( stage, builder. config . build ) ;
576+ // This is directory where the compiler will place the output of the command.
577+ // We will then copy the files from this directory into the final `out` directory, the specified
578+ // as a function parameter.
579+ let out_dir = builder. stage_out ( compiler, Mode :: Std ) . join ( target. triple ) . join ( "doc" ) ;
580+ // `cargo` uses the same directory for both JSON docs and HTML docs.
581+ // This could lead to cross-contamination when copying files into the specified `out` directory.
582+ // For example:
583+ // ```bash
584+ // x doc std
585+ // x doc std --json
586+ // ```
587+ // could lead to HTML docs being copied into the JSON docs output directory.
588+ // To avoid this issue, we clean the doc folder before invoking `cargo`.
589+ builder. remove_dir ( & out_dir) ;
590+
591+ let run_cargo_rustdoc_for = |package : & str | {
592+ let mut cargo = builder. cargo ( compiler, Mode :: Std , SourceType :: InTree , target, "rustdoc" ) ;
593+ compile:: std_cargo ( builder, target, compiler. stage , & mut cargo) ;
594+ cargo
595+ . arg ( "-p" )
596+ . arg ( package)
597+ . arg ( "-Zskip-rustdoc-fingerprint" )
598+ . arg ( "--" )
599+ . arg ( "-Z" )
600+ . arg ( "unstable-options" )
601+ . arg ( "--resource-suffix" )
602+ . arg ( & builder. version )
603+ . args ( extra_args) ;
604+ builder. run ( & mut cargo. into ( ) ) ;
605+ } ;
606+
607+ for krate in STD_PUBLIC_CRATES {
608+ run_cargo_rustdoc_for ( krate) ;
609+ if requested_crates. iter ( ) . any ( |p| p == krate) {
610+ // No need to document more of the libraries if we have the one we want.
611+ break ;
612+ }
613+ }
614+
615+ builder. cp_r ( & out_dir, & out) ;
616+ }
617+
525618#[ derive( Debug , Copy , Clone , Hash , PartialEq , Eq ) ]
526619pub struct Rustc {
527620 pub stage : u32 ,
0 commit comments