@@ -350,7 +350,7 @@ impl SplitDebuginfo {
350350}
351351
352352/// LTO mode used for compiling rustc itself.
353- #[ derive( Default , Clone , PartialEq ) ]
353+ #[ derive( Default , Clone , PartialEq , Debug ) ]
354354pub enum RustcLto {
355355 Off ,
356356 #[ default]
@@ -508,29 +508,42 @@ struct TomlConfig {
508508 profile : Option < String > ,
509509}
510510
511+ /// Describes how to handle conflicts in merging two [`TomlConfig`]
512+ #[ derive( Copy , Clone , Debug ) ]
513+ enum ReplaceOpt {
514+ /// Silently ignore a duplicated value
515+ IgnoreDuplicate ,
516+ /// Override the current value, even if it's `Some`
517+ Override ,
518+ /// Exit with an error on duplicate values
519+ ErrorOnDuplicate ,
520+ }
521+
511522trait Merge {
512- fn merge ( & mut self , other : Self ) ;
523+ fn merge ( & mut self , other : Self , replace : ReplaceOpt ) ;
513524}
514525
515526impl Merge for TomlConfig {
516527 fn merge (
517528 & mut self ,
518- TomlConfig { build, install, llvm, rust, dist, target, profile : _, changelog_seen : _ } : Self ,
529+ TomlConfig { build, install, llvm, rust, dist, target, profile : _, changelog_seen } : Self ,
530+ replace : ReplaceOpt ,
519531 ) {
520- fn do_merge < T : Merge > ( x : & mut Option < T > , y : Option < T > ) {
532+ fn do_merge < T : Merge > ( x : & mut Option < T > , y : Option < T > , replace : ReplaceOpt ) {
521533 if let Some ( new) = y {
522534 if let Some ( original) = x {
523- original. merge ( new) ;
535+ original. merge ( new, replace ) ;
524536 } else {
525537 * x = Some ( new) ;
526538 }
527539 }
528540 }
529- do_merge ( & mut self . build , build) ;
530- do_merge ( & mut self . install , install) ;
531- do_merge ( & mut self . llvm , llvm) ;
532- do_merge ( & mut self . rust , rust) ;
533- do_merge ( & mut self . dist , dist) ;
541+ self . changelog_seen . merge ( changelog_seen, replace) ;
542+ do_merge ( & mut self . build , build, replace) ;
543+ do_merge ( & mut self . install , install, replace) ;
544+ do_merge ( & mut self . llvm , llvm, replace) ;
545+ do_merge ( & mut self . rust , rust, replace) ;
546+ do_merge ( & mut self . dist , dist, replace) ;
534547 assert ! ( target. is_none( ) , "merging target-specific config is not currently supported" ) ;
535548 }
536549}
@@ -547,10 +560,33 @@ macro_rules! define_config {
547560 }
548561
549562 impl Merge for $name {
550- fn merge( & mut self , other: Self ) {
563+ fn merge( & mut self , other: Self , replace : ReplaceOpt ) {
551564 $(
552- if !self . $field. is_some( ) {
553- self . $field = other. $field;
565+ match replace {
566+ ReplaceOpt :: IgnoreDuplicate => {
567+ if self . $field. is_none( ) {
568+ self . $field = other. $field;
569+ }
570+ } ,
571+ ReplaceOpt :: Override => {
572+ if other. $field. is_some( ) {
573+ self . $field = other. $field;
574+ }
575+ }
576+ ReplaceOpt :: ErrorOnDuplicate => {
577+ if other. $field. is_some( ) {
578+ if self . $field. is_some( ) {
579+ if cfg!( test) {
580+ panic!( "overriding existing option" )
581+ } else {
582+ eprintln!( "overriding existing option: `{}`" , stringify!( $field) ) ;
583+ crate :: detail_exit( 2 ) ;
584+ }
585+ } else {
586+ self . $field = other. $field;
587+ }
588+ }
589+ }
554590 }
555591 ) *
556592 }
@@ -623,6 +659,37 @@ macro_rules! define_config {
623659 }
624660}
625661
662+ impl < T > Merge for Option < T > {
663+ fn merge ( & mut self , other : Self , replace : ReplaceOpt ) {
664+ match replace {
665+ ReplaceOpt :: IgnoreDuplicate => {
666+ if self . is_none ( ) {
667+ * self = other;
668+ }
669+ }
670+ ReplaceOpt :: Override => {
671+ if other. is_some ( ) {
672+ * self = other;
673+ }
674+ }
675+ ReplaceOpt :: ErrorOnDuplicate => {
676+ if other. is_some ( ) {
677+ if self . is_some ( ) {
678+ if cfg ! ( test) {
679+ panic ! ( "overriding existing option" )
680+ } else {
681+ eprintln ! ( "overriding existing option" ) ;
682+ crate :: detail_exit ( 2 ) ;
683+ }
684+ } else {
685+ * self = other;
686+ }
687+ }
688+ }
689+ }
690+ }
691+ }
692+
626693define_config ! {
627694 /// TOML representation of various global build decisions.
628695 #[ derive( Default ) ]
@@ -864,28 +931,27 @@ impl Config {
864931
865932 pub fn parse ( args : & [ String ] ) -> Config {
866933 #[ cfg( test) ]
867- let get_toml = |_: & _ | TomlConfig :: default ( ) ;
934+ fn get_toml ( _: & Path ) -> TomlConfig {
935+ TomlConfig :: default ( )
936+ }
937+
868938 #[ cfg( not( test) ) ]
869- let get_toml = | file : & Path | {
939+ fn get_toml ( file : & Path ) -> TomlConfig {
870940 let contents =
871941 t ! ( fs:: read_to_string( file) , format!( "config file {} not found" , file. display( ) ) ) ;
872942 // Deserialize to Value and then TomlConfig to prevent the Deserialize impl of
873943 // TomlConfig and sub types to be monomorphized 5x by toml.
874- match toml:: from_str ( & contents)
944+ toml:: from_str ( & contents)
875945 . and_then ( |table : toml:: Value | TomlConfig :: deserialize ( table) )
876- {
877- Ok ( table) => table,
878- Err ( err) => {
879- eprintln ! ( "failed to parse TOML configuration '{}': {}" , file. display( ) , err) ;
946+ . unwrap_or_else ( |err| {
947+ eprintln ! ( "failed to parse TOML configuration '{}': {err}" , file. display( ) ) ;
880948 crate :: detail_exit ( 2 ) ;
881- }
882- }
883- } ;
884-
949+ } )
950+ }
885951 Self :: parse_inner ( args, get_toml)
886952 }
887953
888- fn parse_inner < ' a > ( args : & [ String ] , get_toml : impl ' a + Fn ( & Path ) -> TomlConfig ) -> Config {
954+ fn parse_inner ( args : & [ String ] , get_toml : impl Fn ( & Path ) -> TomlConfig ) -> Config {
889955 let mut flags = Flags :: parse ( & args) ;
890956 let mut config = Config :: default_opts ( ) ;
891957
@@ -998,8 +1064,40 @@ impl Config {
9981064 include_path. push ( "defaults" ) ;
9991065 include_path. push ( format ! ( "config.{}.toml" , include) ) ;
10001066 let included_toml = get_toml ( & include_path) ;
1001- toml. merge ( included_toml) ;
1067+ toml. merge ( included_toml, ReplaceOpt :: IgnoreDuplicate ) ;
1068+ }
1069+
1070+ let mut override_toml = TomlConfig :: default ( ) ;
1071+ for option in flags. set . iter ( ) {
1072+ fn get_table ( option : & str ) -> Result < TomlConfig , toml:: de:: Error > {
1073+ toml:: from_str ( & option)
1074+ . and_then ( |table : toml:: Value | TomlConfig :: deserialize ( table) )
1075+ }
1076+
1077+ let mut err = match get_table ( option) {
1078+ Ok ( v) => {
1079+ override_toml. merge ( v, ReplaceOpt :: ErrorOnDuplicate ) ;
1080+ continue ;
1081+ }
1082+ Err ( e) => e,
1083+ } ;
1084+ // We want to be able to set string values without quotes,
1085+ // like in `configure.py`. Try adding quotes around the right hand side
1086+ if let Some ( ( key, value) ) = option. split_once ( "=" ) {
1087+ if !value. contains ( '"' ) {
1088+ match get_table ( & format ! ( r#"{key}="{value}""# ) ) {
1089+ Ok ( v) => {
1090+ override_toml. merge ( v, ReplaceOpt :: ErrorOnDuplicate ) ;
1091+ continue ;
1092+ }
1093+ Err ( e) => err = e,
1094+ }
1095+ }
1096+ }
1097+ eprintln ! ( "failed to parse override `{option}`: `{err}" ) ;
1098+ crate :: detail_exit ( 2 )
10021099 }
1100+ toml. merge ( override_toml, ReplaceOpt :: Override ) ;
10031101
10041102 config. changelog_seen = toml. changelog_seen ;
10051103
0 commit comments