@@ -53,12 +53,18 @@ impl TryFrom<&str> for OutputFormat {
5353 }
5454}
5555
56+ /// Either an input crate, markdown file, or nothing (--merge=finalize).
57+ pub ( crate ) enum InputMode {
58+ /// The `--merge=finalize` step does not need an input crate to rustdoc.
59+ NoInputMergeFinalize ,
60+ /// A crate or markdown file.
61+ HasFile ( Input ) ,
62+ }
63+
5664/// Configuration options for rustdoc.
5765#[ derive( Clone ) ]
5866pub ( crate ) struct Options {
5967 // Basic options / Options passed directly to rustc
60- /// The crate root or Markdown file to load.
61- pub ( crate ) input : Input ,
6268 /// The name of the crate being documented.
6369 pub ( crate ) crate_name : Option < String > ,
6470 /// Whether or not this is a bin crate
@@ -179,7 +185,6 @@ impl fmt::Debug for Options {
179185 }
180186
181187 f. debug_struct ( "Options" )
182- . field ( "input" , & self . input . source_name ( ) )
183188 . field ( "crate_name" , & self . crate_name )
184189 . field ( "bin_crate" , & self . bin_crate )
185190 . field ( "proc_macro_crate" , & self . proc_macro_crate )
@@ -289,6 +294,12 @@ pub(crate) struct RenderOptions {
289294 /// This field is only used for the JSON output. If it's set to true, no file will be created
290295 /// and content will be displayed in stdout directly.
291296 pub ( crate ) output_to_stdout : bool ,
297+ /// Whether we should read or write rendered cross-crate info in the doc root.
298+ pub ( crate ) should_merge : ShouldMerge ,
299+ /// Path to crate-info for external crates.
300+ pub ( crate ) include_parts_dir : Vec < PathToParts > ,
301+ /// Where to write crate-info
302+ pub ( crate ) parts_out_dir : Option < PathToParts > ,
292303}
293304
294305#[ derive( Copy , Clone , Debug , PartialEq , Eq ) ]
@@ -348,7 +359,7 @@ impl Options {
348359 early_dcx : & mut EarlyDiagCtxt ,
349360 matches : & getopts:: Matches ,
350361 args : Vec < String > ,
351- ) -> Option < ( Options , RenderOptions ) > {
362+ ) -> Option < ( InputMode , Options , RenderOptions ) > {
352363 // Check for unstable options.
353364 nightly_options:: check_nightly_options ( early_dcx, matches, & opts ( ) ) ;
354365
@@ -478,22 +489,34 @@ impl Options {
478489 let ( lint_opts, describe_lints, lint_cap) = get_cmd_lint_options ( early_dcx, matches) ;
479490
480491 let input = if describe_lints {
481- "" // dummy, this won't be used
492+ InputMode :: HasFile ( make_input ( early_dcx , "" ) )
482493 } else {
483494 match matches. free . as_slice ( ) {
495+ [ ] if matches. opt_str ( "merge" ) . as_deref ( ) == Some ( "finalize" ) => {
496+ InputMode :: NoInputMergeFinalize
497+ }
484498 [ ] => dcx. fatal ( "missing file operand" ) ,
485- [ input] => input,
499+ [ input] => InputMode :: HasFile ( make_input ( early_dcx , input) ) ,
486500 _ => dcx. fatal ( "too many file operands" ) ,
487501 }
488502 } ;
489- let input = make_input ( early_dcx, input) ;
490503
491504 let externs = parse_externs ( early_dcx, matches, & unstable_opts) ;
492505 let extern_html_root_urls = match parse_extern_html_roots ( matches) {
493506 Ok ( ex) => ex,
494507 Err ( err) => dcx. fatal ( err) ,
495508 } ;
496509
510+ let parts_out_dir =
511+ match matches. opt_str ( "parts-out-dir" ) . map ( |p| PathToParts :: from_flag ( p) ) . transpose ( ) {
512+ Ok ( parts_out_dir) => parts_out_dir,
513+ Err ( e) => dcx. fatal ( e) ,
514+ } ;
515+ let include_parts_dir = match parse_include_parts_dir ( matches) {
516+ Ok ( include_parts_dir) => include_parts_dir,
517+ Err ( e) => dcx. fatal ( e) ,
518+ } ;
519+
497520 let default_settings: Vec < Vec < ( String , String ) > > = vec ! [
498521 matches
499522 . opt_str( "default-theme" )
@@ -735,6 +758,10 @@ impl Options {
735758 let extern_html_root_takes_precedence =
736759 matches. opt_present ( "extern-html-root-takes-precedence" ) ;
737760 let html_no_source = matches. opt_present ( "html-no-source" ) ;
761+ let should_merge = match parse_merge ( matches) {
762+ Ok ( result) => result,
763+ Err ( e) => dcx. fatal ( format ! ( "--merge option error: {e}" ) ) ,
764+ } ;
738765
739766 if generate_link_to_definition && ( show_coverage || output_format != OutputFormat :: Html ) {
740767 dcx. struct_warn (
@@ -751,7 +778,6 @@ impl Options {
751778 let unstable_features =
752779 rustc_feature:: UnstableFeatures :: from_environment ( crate_name. as_deref ( ) ) ;
753780 let options = Options {
754- input,
755781 bin_crate,
756782 proc_macro_crate,
757783 error_format,
@@ -823,16 +849,17 @@ impl Options {
823849 no_emit_shared : false ,
824850 html_no_source,
825851 output_to_stdout,
852+ should_merge,
853+ include_parts_dir,
854+ parts_out_dir,
826855 } ;
827- Some ( ( options, render_options) )
856+ Some ( ( input , options, render_options) )
828857 }
858+ }
829859
830- /// Returns `true` if the file given as `self.input` is a Markdown file.
831- pub ( crate ) fn markdown_input ( & self ) -> Option < & Path > {
832- self . input
833- . opt_path ( )
834- . filter ( |p| matches ! ( p. extension( ) , Some ( e) if e == "md" || e == "markdown" ) )
835- }
860+ /// Returns `true` if the file given as `self.input` is a Markdown file.
861+ pub ( crate ) fn markdown_input ( input : & Input ) -> Option < & Path > {
862+ input. opt_path ( ) . filter ( |p| matches ! ( p. extension( ) , Some ( e) if e == "md" || e == "markdown" ) )
836863}
837864
838865fn parse_remap_path_prefix (
@@ -900,3 +927,71 @@ fn parse_extern_html_roots(
900927 }
901928 Ok ( externs)
902929}
930+
931+ /// Path directly to crate-info file.
932+ ///
933+ /// For example, `/home/user/project/target/doc.parts/<crate>/crate-info`.
934+ #[ derive( Clone , Debug ) ]
935+ pub ( crate ) struct PathToParts ( pub ( crate ) PathBuf ) ;
936+
937+ impl PathToParts {
938+ fn from_flag ( path : String ) -> Result < PathToParts , String > {
939+ let mut path = PathBuf :: from ( path) ;
940+ // check here is for diagnostics
941+ if path. exists ( ) && !path. is_dir ( ) {
942+ Err ( format ! (
943+ "--parts-out-dir and --include-parts-dir expect directories, found: {}" ,
944+ path. display( ) ,
945+ ) )
946+ } else {
947+ // if it doesn't exist, we'll create it. worry about that in write_shared
948+ path. push ( "crate-info" ) ;
949+ Ok ( PathToParts ( path) )
950+ }
951+ }
952+ }
953+
954+ /// Reports error if --include-parts-dir / crate-info is not a file
955+ fn parse_include_parts_dir ( m : & getopts:: Matches ) -> Result < Vec < PathToParts > , String > {
956+ let mut ret = Vec :: new ( ) ;
957+ for p in m. opt_strs ( "include-parts-dir" ) {
958+ let p = PathToParts :: from_flag ( p) ?;
959+ // this is just for diagnostic
960+ if !p. 0 . is_file ( ) {
961+ return Err ( format ! ( "--include-parts-dir expected {} to be a file" , p. 0 . display( ) ) ) ;
962+ }
963+ ret. push ( p) ;
964+ }
965+ Ok ( ret)
966+ }
967+
968+ /// Controls merging of cross-crate information
969+ #[ derive( Debug , Clone ) ]
970+ pub ( crate ) struct ShouldMerge {
971+ /// Should we append to existing cci in the doc root
972+ pub ( crate ) read_rendered_cci : bool ,
973+ /// Should we write cci to the doc root
974+ pub ( crate ) write_rendered_cci : bool ,
975+ }
976+
977+ /// Extracts read_rendered_cci and write_rendered_cci from command line arguments, or
978+ /// reports an error if an invalid option was provided
979+ fn parse_merge ( m : & getopts:: Matches ) -> Result < ShouldMerge , & ' static str > {
980+ match m. opt_str ( "merge" ) . as_deref ( ) {
981+ // default = read-write
982+ None => Ok ( ShouldMerge { read_rendered_cci : true , write_rendered_cci : true } ) ,
983+ Some ( "none" ) if m. opt_present ( "include-parts-dir" ) => {
984+ Err ( "--include-parts-dir not allowed if --merge=none" )
985+ }
986+ Some ( "none" ) => Ok ( ShouldMerge { read_rendered_cci : false , write_rendered_cci : false } ) ,
987+ Some ( "shared" ) if m. opt_present ( "parts-out-dir" ) || m. opt_present ( "include-parts-dir" ) => {
988+ Err ( "--parts-out-dir and --include-parts-dir not allowed if --merge=shared" )
989+ }
990+ Some ( "shared" ) => Ok ( ShouldMerge { read_rendered_cci : true , write_rendered_cci : true } ) ,
991+ Some ( "finalize" ) if m. opt_present ( "parts-out-dir" ) => {
992+ Err ( "--parts-out-dir not allowed if --merge=finalize" )
993+ }
994+ Some ( "finalize" ) => Ok ( ShouldMerge { read_rendered_cci : false , write_rendered_cci : true } ) ,
995+ Some ( _) => Err ( "argument to --merge must be `none`, `shared`, or `finalize`" ) ,
996+ }
997+ }
0 commit comments