@@ -748,6 +748,7 @@ enum ReplaceOpt {
748748trait Merge {
749749 fn merge (
750750 & mut self ,
751+ parent_config_path : Option < PathBuf > ,
751752 included_extensions : & mut HashSet < PathBuf > ,
752753 other : Self ,
753754 replace : ReplaceOpt ,
@@ -757,26 +758,31 @@ trait Merge {
757758impl Merge for TomlConfig {
758759 fn merge (
759760 & mut self ,
761+ parent_config_path : Option < PathBuf > ,
760762 included_extensions : & mut HashSet < PathBuf > ,
761763 TomlConfig { build, install, llvm, gcc, rust, dist, target, profile, change_id, include } : Self ,
762764 replace : ReplaceOpt ,
763765 ) {
764766 fn do_merge < T : Merge > ( x : & mut Option < T > , y : Option < T > , replace : ReplaceOpt ) {
765767 if let Some ( new) = y {
766768 if let Some ( original) = x {
767- original. merge ( & mut Default :: default ( ) , new, replace) ;
769+ original. merge ( None , & mut Default :: default ( ) , new, replace) ;
768770 } else {
769771 * x = Some ( new) ;
770772 }
771773 }
772774 }
773775
774- for include_path in include. clone ( ) . unwrap_or_default ( ) {
776+ let parent_dir = parent_config_path
777+ . as_ref ( )
778+ . and_then ( |p| p. parent ( ) . map ( ToOwned :: to_owned) )
779+ . unwrap_or_default ( ) ;
780+
781+ for include_path in include. clone ( ) . unwrap_or_default ( ) . iter ( ) . rev ( ) {
782+ let include_path = parent_dir. join ( include_path) . canonicalize ( ) . unwrap ( ) ;
783+
775784 let included_toml = Config :: get_toml ( & include_path) . unwrap_or_else ( |e| {
776- eprintln ! (
777- "ERROR: Failed to parse default config profile at '{}': {e}" ,
778- include_path. display( )
779- ) ;
785+ eprintln ! ( "ERROR: Failed to parse '{}': {e}" , include_path. display( ) ) ;
780786 exit ! ( 2 ) ;
781787 } ) ;
782788
@@ -786,13 +792,20 @@ impl Merge for TomlConfig {
786792 include_path. display( )
787793 ) ;
788794
789- self . merge ( included_extensions, included_toml, ReplaceOpt :: Override ) ;
795+ self . merge (
796+ Some ( include_path. clone ( ) ) ,
797+ included_extensions,
798+ included_toml,
799+ // Ensures that parent configuration always takes precedence
800+ // over child configurations.
801+ ReplaceOpt :: IgnoreDuplicate ,
802+ ) ;
790803
791804 included_extensions. remove ( & include_path) ;
792805 }
793806
794- self . change_id . inner . merge ( & mut Default :: default ( ) , change_id. inner , replace) ;
795- self . profile . merge ( & mut Default :: default ( ) , profile, replace) ;
807+ self . change_id . inner . merge ( None , & mut Default :: default ( ) , change_id. inner , replace) ;
808+ self . profile . merge ( None , & mut Default :: default ( ) , profile, replace) ;
796809
797810 do_merge ( & mut self . build , build, replace) ;
798811 do_merge ( & mut self . install , install, replace) ;
@@ -807,7 +820,7 @@ impl Merge for TomlConfig {
807820 ( Some ( original_target) , Some ( new_target) ) => {
808821 for ( triple, new) in new_target {
809822 if let Some ( original) = original_target. get_mut ( & triple) {
810- original. merge ( & mut Default :: default ( ) , new, replace) ;
823+ original. merge ( None , & mut Default :: default ( ) , new, replace) ;
811824 } else {
812825 original_target. insert ( triple, new) ;
813826 }
@@ -828,7 +841,13 @@ macro_rules! define_config {
828841 }
829842
830843 impl Merge for $name {
831- fn merge( & mut self , _included_extensions: & mut HashSet <PathBuf >, other: Self , replace: ReplaceOpt ) {
844+ fn merge(
845+ & mut self ,
846+ _parent_config_path: Option <PathBuf >,
847+ _included_extensions: & mut HashSet <PathBuf >,
848+ other: Self ,
849+ replace: ReplaceOpt
850+ ) {
832851 $(
833852 match replace {
834853 ReplaceOpt :: IgnoreDuplicate => {
@@ -930,6 +949,7 @@ macro_rules! define_config {
930949impl < T > Merge for Option < T > {
931950 fn merge (
932951 & mut self ,
952+ _parent_config_path : Option < PathBuf > ,
933953 _included_extensions : & mut HashSet < PathBuf > ,
934954 other : Self ,
935955 replace : ReplaceOpt ,
@@ -1608,6 +1628,24 @@ impl Config {
16081628 toml. profile = Some ( "dist" . into ( ) ) ;
16091629 }
16101630
1631+ // Reverse the list to ensure the last added config extension remains the most dominant.
1632+ // For example, given ["a.toml", "b.toml"], "b.toml" should take precedence over "a.toml".
1633+ //
1634+ // This must be handled before applying the `profile` since `include`s should always take
1635+ // precedence over `profile`s.
1636+ for include_path in toml. include . clone ( ) . unwrap_or_default ( ) . iter ( ) . rev ( ) {
1637+ let included_toml = get_toml ( include_path) . unwrap_or_else ( |e| {
1638+ eprintln ! ( "ERROR: Failed to parse '{}': {e}" , include_path. display( ) ) ;
1639+ exit ! ( 2 ) ;
1640+ } ) ;
1641+ toml. merge (
1642+ Some ( t ! ( include_path. canonicalize( ) ) ) ,
1643+ & mut Default :: default ( ) ,
1644+ included_toml,
1645+ ReplaceOpt :: IgnoreDuplicate ,
1646+ ) ;
1647+ }
1648+
16111649 if let Some ( include) = & toml. profile {
16121650 // Allows creating alias for profile names, allowing
16131651 // profiles to be renamed while maintaining back compatibility
@@ -1629,18 +1667,12 @@ impl Config {
16291667 ) ;
16301668 exit ! ( 2 ) ;
16311669 } ) ;
1632- toml. merge ( & mut Default :: default ( ) , included_toml, ReplaceOpt :: IgnoreDuplicate ) ;
1633- }
1634-
1635- for include_path in toml. include . clone ( ) . unwrap_or_default ( ) {
1636- let included_toml = get_toml ( & include_path) . unwrap_or_else ( |e| {
1637- eprintln ! (
1638- "ERROR: Failed to parse default config profile at '{}': {e}" ,
1639- include_path. display( )
1640- ) ;
1641- exit ! ( 2 ) ;
1642- } ) ;
1643- toml. merge ( & mut Default :: default ( ) , included_toml, ReplaceOpt :: Override ) ;
1670+ toml. merge (
1671+ Some ( t ! ( include_path. canonicalize( ) ) ) ,
1672+ & mut Default :: default ( ) ,
1673+ included_toml,
1674+ ReplaceOpt :: IgnoreDuplicate ,
1675+ ) ;
16441676 }
16451677
16461678 let mut override_toml = TomlConfig :: default ( ) ;
@@ -1651,7 +1683,12 @@ impl Config {
16511683
16521684 let mut err = match get_table ( option) {
16531685 Ok ( v) => {
1654- override_toml. merge ( & mut Default :: default ( ) , v, ReplaceOpt :: ErrorOnDuplicate ) ;
1686+ override_toml. merge (
1687+ None ,
1688+ & mut Default :: default ( ) ,
1689+ v,
1690+ ReplaceOpt :: ErrorOnDuplicate ,
1691+ ) ;
16551692 continue ;
16561693 }
16571694 Err ( e) => e,
@@ -1663,6 +1700,7 @@ impl Config {
16631700 match get_table ( & format ! ( r#"{key}="{value}""# ) ) {
16641701 Ok ( v) => {
16651702 override_toml. merge (
1703+ None ,
16661704 & mut Default :: default ( ) ,
16671705 v,
16681706 ReplaceOpt :: ErrorOnDuplicate ,
@@ -1676,7 +1714,7 @@ impl Config {
16761714 eprintln ! ( "failed to parse override `{option}`: `{err}" ) ;
16771715 exit ! ( 2 )
16781716 }
1679- toml. merge ( & mut Default :: default ( ) , override_toml, ReplaceOpt :: Override ) ;
1717+ toml. merge ( None , & mut Default :: default ( ) , override_toml, ReplaceOpt :: Override ) ;
16801718
16811719 config. change_id = toml. change_id . inner ;
16821720
0 commit comments