@@ -751,6 +751,7 @@ enum ReplaceOpt {
751
751
trait Merge {
752
752
fn merge (
753
753
& mut self ,
754
+ parent_config_path : Option < PathBuf > ,
754
755
included_extensions : & mut HashSet < PathBuf > ,
755
756
other : Self ,
756
757
replace : ReplaceOpt ,
@@ -760,26 +761,35 @@ trait Merge {
760
761
impl Merge for TomlConfig {
761
762
fn merge (
762
763
& mut self ,
764
+ parent_config_path : Option < PathBuf > ,
763
765
included_extensions : & mut HashSet < PathBuf > ,
764
766
TomlConfig { build, install, llvm, gcc, rust, dist, target, profile, change_id, include } : Self ,
765
767
replace : ReplaceOpt ,
766
768
) {
767
769
fn do_merge < T : Merge > ( x : & mut Option < T > , y : Option < T > , replace : ReplaceOpt ) {
768
770
if let Some ( new) = y {
769
771
if let Some ( original) = x {
770
- original. merge ( & mut Default :: default ( ) , new, replace) ;
772
+ original. merge ( None , & mut Default :: default ( ) , new, replace) ;
771
773
} else {
772
774
* x = Some ( new) ;
773
775
}
774
776
}
775
777
}
776
778
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
+
778
791
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( ) ) ;
783
793
exit ! ( 2 ) ;
784
794
} ) ;
785
795
@@ -789,13 +799,20 @@ impl Merge for TomlConfig {
789
799
include_path. display( )
790
800
) ;
791
801
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
+ ) ;
793
810
794
811
included_extensions. remove ( & include_path) ;
795
812
}
796
813
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) ;
799
816
800
817
do_merge ( & mut self . build , build, replace) ;
801
818
do_merge ( & mut self . install , install, replace) ;
@@ -810,7 +827,7 @@ impl Merge for TomlConfig {
810
827
( Some ( original_target) , Some ( new_target) ) => {
811
828
for ( triple, new) in new_target {
812
829
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) ;
814
831
} else {
815
832
original_target. insert ( triple, new) ;
816
833
}
@@ -831,7 +848,13 @@ macro_rules! define_config {
831
848
}
832
849
833
850
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
+ ) {
835
858
$(
836
859
match replace {
837
860
ReplaceOpt :: IgnoreDuplicate => {
@@ -933,6 +956,7 @@ macro_rules! define_config {
933
956
impl < T > Merge for Option < T > {
934
957
fn merge (
935
958
& mut self ,
959
+ _parent_config_path : Option < PathBuf > ,
936
960
_included_extensions : & mut HashSet < PathBuf > ,
937
961
other : Self ,
938
962
replace : ReplaceOpt ,
@@ -1581,7 +1605,8 @@ impl Config {
1581
1605
// but not if `bootstrap.toml` hasn't been created.
1582
1606
let mut toml = if !using_default_path || toml_path. exists ( ) {
1583
1607
config. config = Some ( if cfg ! ( not( test) ) {
1584
- toml_path. canonicalize ( ) . unwrap ( )
1608
+ toml_path = toml_path. canonicalize ( ) . unwrap ( ) ;
1609
+ toml_path. clone ( )
1585
1610
} else {
1586
1611
toml_path. clone ( )
1587
1612
} ) ;
@@ -1609,6 +1634,24 @@ impl Config {
1609
1634
toml. profile = Some ( "dist" . into ( ) ) ;
1610
1635
}
1611
1636
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
+
1612
1655
if let Some ( include) = & toml. profile {
1613
1656
// Allows creating alias for profile names, allowing
1614
1657
// profiles to be renamed while maintaining back compatibility
@@ -1630,18 +1673,12 @@ impl Config {
1630
1673
) ;
1631
1674
exit ! ( 2 ) ;
1632
1675
} ) ;
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
+ ) ;
1645
1682
}
1646
1683
1647
1684
let mut override_toml = TomlConfig :: default ( ) ;
@@ -1652,7 +1689,12 @@ impl Config {
1652
1689
1653
1690
let mut err = match get_table ( option) {
1654
1691
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
+ ) ;
1656
1698
continue ;
1657
1699
}
1658
1700
Err ( e) => e,
@@ -1664,6 +1706,7 @@ impl Config {
1664
1706
match get_table ( & format ! ( r#"{key}="{value}""# ) ) {
1665
1707
Ok ( v) => {
1666
1708
override_toml. merge (
1709
+ None ,
1667
1710
& mut Default :: default ( ) ,
1668
1711
v,
1669
1712
ReplaceOpt :: ErrorOnDuplicate ,
@@ -1677,7 +1720,7 @@ impl Config {
1677
1720
eprintln ! ( "failed to parse override `{option}`: `{err}" ) ;
1678
1721
exit ! ( 2 )
1679
1722
}
1680
- toml. merge ( & mut Default :: default ( ) , override_toml, ReplaceOpt :: Override ) ;
1723
+ toml. merge ( None , & mut Default :: default ( ) , override_toml, ReplaceOpt :: Override ) ;
1681
1724
1682
1725
config. change_id = toml. change_id . inner ;
1683
1726
0 commit comments