@@ -751,6 +751,7 @@ enum ReplaceOpt {
751751trait Merge {
752752 fn merge (
753753 & mut self ,
754+ parent_config_path : Option < PathBuf > ,
754755 included_extensions : & mut HashSet < PathBuf > ,
755756 other : Self ,
756757 replace : ReplaceOpt ,
@@ -760,26 +761,35 @@ trait Merge {
760761impl Merge for TomlConfig {
761762 fn merge (
762763 & mut self ,
764+ parent_config_path : Option < PathBuf > ,
763765 included_extensions : & mut HashSet < PathBuf > ,
764766 TomlConfig { build, install, llvm, gcc, rust, dist, target, profile, change_id, include } : Self ,
765767 replace : ReplaceOpt ,
766768 ) {
767769 fn do_merge < T : Merge > ( x : & mut Option < T > , y : Option < T > , replace : ReplaceOpt ) {
768770 if let Some ( new) = y {
769771 if let Some ( original) = x {
770- original. merge ( & mut Default :: default ( ) , new, replace) ;
772+ original. merge ( None , & mut Default :: default ( ) , new, replace) ;
771773 } else {
772774 * x = Some ( new) ;
773775 }
774776 }
775777 }
776778
777- for include_path in include. clone ( ) . unwrap_or_default ( ) {
779+ let parent_dir = parent_config_path
780+ . as_ref ( )
781+ . and_then ( |p| p. parent ( ) . map ( ToOwned :: to_owned) )
782+ . unwrap_or_default ( ) ;
783+
784+ for include_path in include. clone ( ) . unwrap_or_default ( ) . iter ( ) . rev ( ) {
785+ let include_path = parent_dir. join ( include_path) ;
786+ let include_path = include_path. canonicalize ( ) . unwrap_or_else ( |e| {
787+ eprintln ! ( "ERROR: Failed to canonicalize '{}' path: {e}" , include_path. display( ) ) ;
788+ exit ! ( 2 ) ;
789+ } ) ;
790+
778791 let included_toml = Config :: get_toml ( & include_path) . unwrap_or_else ( |e| {
779- eprintln ! (
780- "ERROR: Failed to parse default config profile at '{}': {e}" ,
781- include_path. display( )
782- ) ;
792+ eprintln ! ( "ERROR: Failed to parse '{}': {e}" , include_path. display( ) ) ;
783793 exit ! ( 2 ) ;
784794 } ) ;
785795
@@ -789,13 +799,20 @@ impl Merge for TomlConfig {
789799 include_path. display( )
790800 ) ;
791801
792- self . merge ( included_extensions, included_toml, ReplaceOpt :: Override ) ;
802+ self . merge (
803+ Some ( include_path. clone ( ) ) ,
804+ included_extensions,
805+ included_toml,
806+ // Ensures that parent configuration always takes precedence
807+ // over child configurations.
808+ ReplaceOpt :: IgnoreDuplicate ,
809+ ) ;
793810
794811 included_extensions. remove ( & include_path) ;
795812 }
796813
797- self . change_id . inner . merge ( & mut Default :: default ( ) , change_id. inner , replace) ;
798- self . profile . merge ( & mut Default :: default ( ) , profile, replace) ;
814+ self . change_id . inner . merge ( None , & mut Default :: default ( ) , change_id. inner , replace) ;
815+ self . profile . merge ( None , & mut Default :: default ( ) , profile, replace) ;
799816
800817 do_merge ( & mut self . build , build, replace) ;
801818 do_merge ( & mut self . install , install, replace) ;
@@ -810,7 +827,7 @@ impl Merge for TomlConfig {
810827 ( Some ( original_target) , Some ( new_target) ) => {
811828 for ( triple, new) in new_target {
812829 if let Some ( original) = original_target. get_mut ( & triple) {
813- original. merge ( & mut Default :: default ( ) , new, replace) ;
830+ original. merge ( None , & mut Default :: default ( ) , new, replace) ;
814831 } else {
815832 original_target. insert ( triple, new) ;
816833 }
@@ -831,7 +848,13 @@ macro_rules! define_config {
831848 }
832849
833850 impl Merge for $name {
834- fn merge( & mut self , _included_extensions: & mut HashSet <PathBuf >, other: Self , replace: ReplaceOpt ) {
851+ fn merge(
852+ & mut self ,
853+ _parent_config_path: Option <PathBuf >,
854+ _included_extensions: & mut HashSet <PathBuf >,
855+ other: Self ,
856+ replace: ReplaceOpt
857+ ) {
835858 $(
836859 match replace {
837860 ReplaceOpt :: IgnoreDuplicate => {
@@ -933,6 +956,7 @@ macro_rules! define_config {
933956impl < T > Merge for Option < T > {
934957 fn merge (
935958 & mut self ,
959+ _parent_config_path : Option < PathBuf > ,
936960 _included_extensions : & mut HashSet < PathBuf > ,
937961 other : Self ,
938962 replace : ReplaceOpt ,
@@ -1581,7 +1605,8 @@ impl Config {
15811605 // but not if `bootstrap.toml` hasn't been created.
15821606 let mut toml = if !using_default_path || toml_path. exists ( ) {
15831607 config. config = Some ( if cfg ! ( not( test) ) {
1584- toml_path. canonicalize ( ) . unwrap ( )
1608+ toml_path = toml_path. canonicalize ( ) . unwrap ( ) ;
1609+ toml_path. clone ( )
15851610 } else {
15861611 toml_path. clone ( )
15871612 } ) ;
@@ -1609,6 +1634,24 @@ impl Config {
16091634 toml. profile = Some ( "dist" . into ( ) ) ;
16101635 }
16111636
1637+ // Reverse the list to ensure the last added config extension remains the most dominant.
1638+ // For example, given ["a.toml", "b.toml"], "b.toml" should take precedence over "a.toml".
1639+ //
1640+ // This must be handled before applying the `profile` since `include`s should always take
1641+ // precedence over `profile`s.
1642+ for include_path in toml. include . clone ( ) . unwrap_or_default ( ) . iter ( ) . rev ( ) {
1643+ let included_toml = get_toml ( include_path) . unwrap_or_else ( |e| {
1644+ eprintln ! ( "ERROR: Failed to parse '{}': {e}" , include_path. display( ) ) ;
1645+ exit ! ( 2 ) ;
1646+ } ) ;
1647+ toml. merge (
1648+ Some ( toml_path. join ( include_path) ) ,
1649+ & mut Default :: default ( ) ,
1650+ included_toml,
1651+ ReplaceOpt :: IgnoreDuplicate ,
1652+ ) ;
1653+ }
1654+
16121655 if let Some ( include) = & toml. profile {
16131656 // Allows creating alias for profile names, allowing
16141657 // profiles to be renamed while maintaining back compatibility
@@ -1630,18 +1673,12 @@ impl Config {
16301673 ) ;
16311674 exit ! ( 2 ) ;
16321675 } ) ;
1633- toml. merge ( & mut Default :: default ( ) , included_toml, ReplaceOpt :: IgnoreDuplicate ) ;
1634- }
1635-
1636- for include_path in toml. include . clone ( ) . unwrap_or_default ( ) {
1637- let included_toml = get_toml ( & include_path) . unwrap_or_else ( |e| {
1638- eprintln ! (
1639- "ERROR: Failed to parse default config profile at '{}': {e}" ,
1640- include_path. display( )
1641- ) ;
1642- exit ! ( 2 ) ;
1643- } ) ;
1644- toml. merge ( & mut Default :: default ( ) , included_toml, ReplaceOpt :: Override ) ;
1676+ toml. merge (
1677+ Some ( include_path) ,
1678+ & mut Default :: default ( ) ,
1679+ included_toml,
1680+ ReplaceOpt :: IgnoreDuplicate ,
1681+ ) ;
16451682 }
16461683
16471684 let mut override_toml = TomlConfig :: default ( ) ;
@@ -1652,7 +1689,12 @@ impl Config {
16521689
16531690 let mut err = match get_table ( option) {
16541691 Ok ( v) => {
1655- override_toml. merge ( & mut Default :: default ( ) , v, ReplaceOpt :: ErrorOnDuplicate ) ;
1692+ override_toml. merge (
1693+ None ,
1694+ & mut Default :: default ( ) ,
1695+ v,
1696+ ReplaceOpt :: ErrorOnDuplicate ,
1697+ ) ;
16561698 continue ;
16571699 }
16581700 Err ( e) => e,
@@ -1664,6 +1706,7 @@ impl Config {
16641706 match get_table ( & format ! ( r#"{key}="{value}""# ) ) {
16651707 Ok ( v) => {
16661708 override_toml. merge (
1709+ None ,
16671710 & mut Default :: default ( ) ,
16681711 v,
16691712 ReplaceOpt :: ErrorOnDuplicate ,
@@ -1677,7 +1720,7 @@ impl Config {
16771720 eprintln ! ( "failed to parse override `{option}`: `{err}" ) ;
16781721 exit ! ( 2 )
16791722 }
1680- toml. merge ( & mut Default :: default ( ) , override_toml, ReplaceOpt :: Override ) ;
1723+ toml. merge ( None , & mut Default :: default ( ) , override_toml, ReplaceOpt :: Override ) ;
16811724
16821725 config. change_id = toml. change_id . inner ;
16831726
0 commit comments