@@ -349,7 +349,7 @@ impl SplitDebuginfo {
349349}
350350
351351/// LTO mode used for compiling rustc itself.
352- #[ derive( Default , Clone , PartialEq ) ]
352+ #[ derive( Default , Clone , PartialEq , Debug ) ]
353353pub enum RustcLto {
354354 Off ,
355355 #[ default]
@@ -507,29 +507,42 @@ struct TomlConfig {
507507 profile : Option < String > ,
508508}
509509
510+ /// Describes how to handle conflicts in merging two [`TomlConfig`]
511+ #[ derive( Copy , Clone , Debug ) ]
512+ enum ReplaceOpt {
513+ /// Silently ignore a duplicated value
514+ IgnoreDuplicate ,
515+ /// Override the current value, even if it's `Some`
516+ Override ,
517+ /// Exit with an error on duplicate values
518+ ErrorOnDuplicate ,
519+ }
520+
510521trait Merge {
511- fn merge ( & mut self , other : Self ) ;
522+ fn merge ( & mut self , other : Self , replace : ReplaceOpt ) ;
512523}
513524
514525impl Merge for TomlConfig {
515526 fn merge (
516527 & mut self ,
517- TomlConfig { build, install, llvm, rust, dist, target, profile : _, changelog_seen : _ } : Self ,
528+ TomlConfig { build, install, llvm, rust, dist, target, profile : _, changelog_seen } : Self ,
529+ replace : ReplaceOpt ,
518530 ) {
519- fn do_merge < T : Merge > ( x : & mut Option < T > , y : Option < T > ) {
531+ fn do_merge < T : Merge > ( x : & mut Option < T > , y : Option < T > , replace : ReplaceOpt ) {
520532 if let Some ( new) = y {
521533 if let Some ( original) = x {
522- original. merge ( new) ;
534+ original. merge ( new, replace ) ;
523535 } else {
524536 * x = Some ( new) ;
525537 }
526538 }
527539 }
528- do_merge ( & mut self . build , build) ;
529- do_merge ( & mut self . install , install) ;
530- do_merge ( & mut self . llvm , llvm) ;
531- do_merge ( & mut self . rust , rust) ;
532- do_merge ( & mut self . dist , dist) ;
540+ self . changelog_seen . merge ( changelog_seen, replace) ;
541+ do_merge ( & mut self . build , build, replace) ;
542+ do_merge ( & mut self . install , install, replace) ;
543+ do_merge ( & mut self . llvm , llvm, replace) ;
544+ do_merge ( & mut self . rust , rust, replace) ;
545+ do_merge ( & mut self . dist , dist, replace) ;
533546 assert ! ( target. is_none( ) , "merging target-specific config is not currently supported" ) ;
534547 }
535548}
@@ -546,10 +559,33 @@ macro_rules! define_config {
546559 }
547560
548561 impl Merge for $name {
549- fn merge( & mut self , other: Self ) {
562+ fn merge( & mut self , other: Self , replace : ReplaceOpt ) {
550563 $(
551- if !self . $field. is_some( ) {
552- self . $field = other. $field;
564+ match replace {
565+ ReplaceOpt :: IgnoreDuplicate => {
566+ if self . $field. is_none( ) {
567+ self . $field = other. $field;
568+ }
569+ } ,
570+ ReplaceOpt :: Override => {
571+ if other. $field. is_some( ) {
572+ self . $field = other. $field;
573+ }
574+ }
575+ ReplaceOpt :: ErrorOnDuplicate => {
576+ if other. $field. is_some( ) {
577+ if self . $field. is_some( ) {
578+ if cfg!( test) {
579+ panic!( "overriding existing option" )
580+ } else {
581+ eprintln!( "overriding existing option: `{}`" , stringify!( $field) ) ;
582+ crate :: detail_exit( 2 ) ;
583+ }
584+ } else {
585+ self . $field = other. $field;
586+ }
587+ }
588+ }
553589 }
554590 ) *
555591 }
@@ -622,6 +658,37 @@ macro_rules! define_config {
622658 }
623659}
624660
661+ impl < T > Merge for Option < T > {
662+ fn merge ( & mut self , other : Self , replace : ReplaceOpt ) {
663+ match replace {
664+ ReplaceOpt :: IgnoreDuplicate => {
665+ if self . is_none ( ) {
666+ * self = other;
667+ }
668+ }
669+ ReplaceOpt :: Override => {
670+ if other. is_some ( ) {
671+ * self = other;
672+ }
673+ }
674+ ReplaceOpt :: ErrorOnDuplicate => {
675+ if other. is_some ( ) {
676+ if self . is_some ( ) {
677+ if cfg ! ( test) {
678+ panic ! ( "overriding existing option" )
679+ } else {
680+ eprintln ! ( "overriding existing option" ) ;
681+ crate :: detail_exit ( 2 ) ;
682+ }
683+ } else {
684+ * self = other;
685+ }
686+ }
687+ }
688+ }
689+ }
690+ }
691+
625692define_config ! {
626693 /// TOML representation of various global build decisions.
627694 #[ derive( Default ) ]
@@ -863,28 +930,27 @@ impl Config {
863930
864931 pub fn parse ( args : & [ String ] ) -> Config {
865932 #[ cfg( test) ]
866- let get_toml = |_: & _ | TomlConfig :: default ( ) ;
933+ fn get_toml ( _: & Path ) -> TomlConfig {
934+ TomlConfig :: default ( )
935+ }
936+
867937 #[ cfg( not( test) ) ]
868- let get_toml = | file : & Path | {
938+ fn get_toml ( file : & Path ) -> TomlConfig {
869939 let contents =
870940 t ! ( fs:: read_to_string( file) , format!( "config file {} not found" , file. display( ) ) ) ;
871941 // Deserialize to Value and then TomlConfig to prevent the Deserialize impl of
872942 // TomlConfig and sub types to be monomorphized 5x by toml.
873- match toml:: from_str ( & contents)
943+ toml:: from_str ( & contents)
874944 . and_then ( |table : toml:: Value | TomlConfig :: deserialize ( table) )
875- {
876- Ok ( table) => table,
877- Err ( err) => {
878- eprintln ! ( "failed to parse TOML configuration '{}': {}" , file. display( ) , err) ;
945+ . unwrap_or_else ( |err| {
946+ eprintln ! ( "failed to parse TOML configuration '{}': {err}" , file. display( ) ) ;
879947 crate :: detail_exit ( 2 ) ;
880- }
881- }
882- } ;
883-
948+ } )
949+ }
884950 Self :: parse_inner ( args, get_toml)
885951 }
886952
887- fn parse_inner < ' a > ( args : & [ String ] , get_toml : impl ' a + Fn ( & Path ) -> TomlConfig ) -> Config {
953+ fn parse_inner ( args : & [ String ] , get_toml : impl Fn ( & Path ) -> TomlConfig ) -> Config {
888954 let mut flags = Flags :: parse ( & args) ;
889955 let mut config = Config :: default_opts ( ) ;
890956
@@ -997,8 +1063,40 @@ impl Config {
9971063 include_path. push ( "defaults" ) ;
9981064 include_path. push ( format ! ( "config.{}.toml" , include) ) ;
9991065 let included_toml = get_toml ( & include_path) ;
1000- toml. merge ( included_toml) ;
1066+ toml. merge ( included_toml, ReplaceOpt :: IgnoreDuplicate ) ;
1067+ }
1068+
1069+ let mut override_toml = TomlConfig :: default ( ) ;
1070+ for option in flags. set . iter ( ) {
1071+ fn get_table ( option : & str ) -> Result < TomlConfig , toml:: de:: Error > {
1072+ toml:: from_str ( & option)
1073+ . and_then ( |table : toml:: Value | TomlConfig :: deserialize ( table) )
1074+ }
1075+
1076+ let mut err = match get_table ( option) {
1077+ Ok ( v) => {
1078+ override_toml. merge ( v, ReplaceOpt :: ErrorOnDuplicate ) ;
1079+ continue ;
1080+ }
1081+ Err ( e) => e,
1082+ } ;
1083+ // We want to be able to set string values without quotes,
1084+ // like in `configure.py`. Try adding quotes around the right hand side
1085+ if let Some ( ( key, value) ) = option. split_once ( "=" ) {
1086+ if !value. contains ( '"' ) {
1087+ match get_table ( & format ! ( r#"{key}="{value}""# ) ) {
1088+ Ok ( v) => {
1089+ override_toml. merge ( v, ReplaceOpt :: ErrorOnDuplicate ) ;
1090+ continue ;
1091+ }
1092+ Err ( e) => err = e,
1093+ }
1094+ }
1095+ }
1096+ eprintln ! ( "failed to parse override `{option}`: `{err}" ) ;
1097+ crate :: detail_exit ( 2 )
10011098 }
1099+ toml. merge ( override_toml, ReplaceOpt :: Override ) ;
10021100
10031101 config. changelog_seen = toml. changelog_seen ;
10041102
0 commit comments