@@ -748,6 +748,7 @@ enum ReplaceOpt {
748
748
trait Merge {
749
749
fn merge (
750
750
& mut self ,
751
+ parent_config_path : Option < PathBuf > ,
751
752
included_extensions : & mut HashSet < PathBuf > ,
752
753
other : Self ,
753
754
replace : ReplaceOpt ,
@@ -757,26 +758,35 @@ trait Merge {
757
758
impl Merge for TomlConfig {
758
759
fn merge (
759
760
& mut self ,
761
+ parent_config_path : Option < PathBuf > ,
760
762
included_extensions : & mut HashSet < PathBuf > ,
761
763
TomlConfig { build, install, llvm, gcc, rust, dist, target, profile, change_id, include } : Self ,
762
764
replace : ReplaceOpt ,
763
765
) {
764
766
fn do_merge < T : Merge > ( x : & mut Option < T > , y : Option < T > , replace : ReplaceOpt ) {
765
767
if let Some ( new) = y {
766
768
if let Some ( original) = x {
767
- original. merge ( & mut Default :: default ( ) , new, replace) ;
769
+ original. merge ( None , & mut Default :: default ( ) , new, replace) ;
768
770
} else {
769
771
* x = Some ( new) ;
770
772
}
771
773
}
772
774
}
773
775
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) ;
783
+ let include_path = include_path. canonicalize ( ) . unwrap_or_else ( |e| {
784
+ eprintln ! ( "ERROR: Failed to canonicalize '{}' path: {e}" , include_path. display( ) ) ;
785
+ exit ! ( 2 ) ;
786
+ } ) ;
787
+
775
788
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
- ) ;
789
+ eprintln ! ( "ERROR: Failed to parse '{}': {e}" , include_path. display( ) ) ;
780
790
exit ! ( 2 ) ;
781
791
} ) ;
782
792
@@ -786,13 +796,20 @@ impl Merge for TomlConfig {
786
796
include_path. display( )
787
797
) ;
788
798
789
- self . merge ( included_extensions, included_toml, ReplaceOpt :: Override ) ;
799
+ self . merge (
800
+ Some ( include_path. clone ( ) ) ,
801
+ included_extensions,
802
+ included_toml,
803
+ // Ensures that parent configuration always takes precedence
804
+ // over child configurations.
805
+ ReplaceOpt :: IgnoreDuplicate ,
806
+ ) ;
790
807
791
808
included_extensions. remove ( & include_path) ;
792
809
}
793
810
794
- self . change_id . inner . merge ( & mut Default :: default ( ) , change_id. inner , replace) ;
795
- self . profile . merge ( & mut Default :: default ( ) , profile, replace) ;
811
+ self . change_id . inner . merge ( None , & mut Default :: default ( ) , change_id. inner , replace) ;
812
+ self . profile . merge ( None , & mut Default :: default ( ) , profile, replace) ;
796
813
797
814
do_merge ( & mut self . build , build, replace) ;
798
815
do_merge ( & mut self . install , install, replace) ;
@@ -807,7 +824,7 @@ impl Merge for TomlConfig {
807
824
( Some ( original_target) , Some ( new_target) ) => {
808
825
for ( triple, new) in new_target {
809
826
if let Some ( original) = original_target. get_mut ( & triple) {
810
- original. merge ( & mut Default :: default ( ) , new, replace) ;
827
+ original. merge ( None , & mut Default :: default ( ) , new, replace) ;
811
828
} else {
812
829
original_target. insert ( triple, new) ;
813
830
}
@@ -828,7 +845,13 @@ macro_rules! define_config {
828
845
}
829
846
830
847
impl Merge for $name {
831
- fn merge( & mut self , _included_extensions: & mut HashSet <PathBuf >, other: Self , replace: ReplaceOpt ) {
848
+ fn merge(
849
+ & mut self ,
850
+ _parent_config_path: Option <PathBuf >,
851
+ _included_extensions: & mut HashSet <PathBuf >,
852
+ other: Self ,
853
+ replace: ReplaceOpt
854
+ ) {
832
855
$(
833
856
match replace {
834
857
ReplaceOpt :: IgnoreDuplicate => {
@@ -930,6 +953,7 @@ macro_rules! define_config {
930
953
impl < T > Merge for Option < T > {
931
954
fn merge (
932
955
& mut self ,
956
+ _parent_config_path : Option < PathBuf > ,
933
957
_included_extensions : & mut HashSet < PathBuf > ,
934
958
other : Self ,
935
959
replace : ReplaceOpt ,
@@ -1608,6 +1632,24 @@ impl Config {
1608
1632
toml. profile = Some ( "dist" . into ( ) ) ;
1609
1633
}
1610
1634
1635
+ // Reverse the list to ensure the last added config extension remains the most dominant.
1636
+ // For example, given ["a.toml", "b.toml"], "b.toml" should take precedence over "a.toml".
1637
+ //
1638
+ // This must be handled before applying the `profile` since `include`s should always take
1639
+ // precedence over `profile`s.
1640
+ for include_path in toml. include . clone ( ) . unwrap_or_default ( ) . iter ( ) . rev ( ) {
1641
+ let included_toml = get_toml ( include_path) . unwrap_or_else ( |e| {
1642
+ eprintln ! ( "ERROR: Failed to parse '{}': {e}" , include_path. display( ) ) ;
1643
+ exit ! ( 2 ) ;
1644
+ } ) ;
1645
+ toml. merge (
1646
+ Some ( config. src . join ( include_path) ) ,
1647
+ & mut Default :: default ( ) ,
1648
+ included_toml,
1649
+ ReplaceOpt :: IgnoreDuplicate ,
1650
+ ) ;
1651
+ }
1652
+
1611
1653
if let Some ( include) = & toml. profile {
1612
1654
// Allows creating alias for profile names, allowing
1613
1655
// profiles to be renamed while maintaining back compatibility
@@ -1629,18 +1671,12 @@ impl Config {
1629
1671
) ;
1630
1672
exit ! ( 2 ) ;
1631
1673
} ) ;
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 ) ;
1674
+ toml. merge (
1675
+ Some ( include_path) ,
1676
+ & mut Default :: default ( ) ,
1677
+ included_toml,
1678
+ ReplaceOpt :: IgnoreDuplicate ,
1679
+ ) ;
1644
1680
}
1645
1681
1646
1682
let mut override_toml = TomlConfig :: default ( ) ;
@@ -1651,7 +1687,12 @@ impl Config {
1651
1687
1652
1688
let mut err = match get_table ( option) {
1653
1689
Ok ( v) => {
1654
- override_toml. merge ( & mut Default :: default ( ) , v, ReplaceOpt :: ErrorOnDuplicate ) ;
1690
+ override_toml. merge (
1691
+ None ,
1692
+ & mut Default :: default ( ) ,
1693
+ v,
1694
+ ReplaceOpt :: ErrorOnDuplicate ,
1695
+ ) ;
1655
1696
continue ;
1656
1697
}
1657
1698
Err ( e) => e,
@@ -1663,6 +1704,7 @@ impl Config {
1663
1704
match get_table ( & format ! ( r#"{key}="{value}""# ) ) {
1664
1705
Ok ( v) => {
1665
1706
override_toml. merge (
1707
+ None ,
1666
1708
& mut Default :: default ( ) ,
1667
1709
v,
1668
1710
ReplaceOpt :: ErrorOnDuplicate ,
@@ -1676,7 +1718,7 @@ impl Config {
1676
1718
eprintln ! ( "failed to parse override `{option}`: `{err}" ) ;
1677
1719
exit ! ( 2 )
1678
1720
}
1679
- toml. merge ( & mut Default :: default ( ) , override_toml, ReplaceOpt :: Override ) ;
1721
+ toml. merge ( None , & mut Default :: default ( ) , override_toml, ReplaceOpt :: Override ) ;
1680
1722
1681
1723
config. change_id = toml. change_id . inner ;
1682
1724
0 commit comments