1
1
use std:: borrow:: Cow ;
2
+ use std:: collections:: HashMap ;
2
3
use std:: fmt:: { self , Display } ;
3
4
use std:: io;
4
5
use std:: path:: { Path , PathBuf } ;
@@ -38,6 +39,8 @@ enum OverrideFileConfigError {
38
39
#[ derive( Debug , Default , Deserialize , PartialEq , Eq ) ]
39
40
struct OverrideFile {
40
41
toolchain : ToolchainSection ,
42
+ #[ serde( default ) ]
43
+ env : HashMap < String , EnvOverride > ,
41
44
}
42
45
43
46
impl OverrideFile {
@@ -64,6 +67,19 @@ impl ToolchainSection {
64
67
}
65
68
}
66
69
70
+ #[ derive( Debug , Deserialize , PartialEq , Eq ) ]
71
+ #[ serde( untagged) ]
72
+ enum EnvOverride {
73
+ Literal ( String ) ,
74
+ Table {
75
+ value : String ,
76
+ #[ serde( default ) ]
77
+ force : bool ,
78
+ #[ serde( default ) ]
79
+ relative : bool ,
80
+ } ,
81
+ }
82
+
67
83
impl < T : Into < String > > From < T > for OverrideFile {
68
84
fn from ( channel : T ) -> Self {
69
85
let override_ = channel. into ( ) ;
@@ -73,13 +89,15 @@ impl<T: Into<String>> From<T> for OverrideFile {
73
89
path : Some ( PathBuf :: from ( override_) ) ,
74
90
..Default :: default ( )
75
91
} ,
92
+ env : Default :: default ( ) ,
76
93
}
77
94
} else {
78
95
Self {
79
96
toolchain : ToolchainSection {
80
97
channel : Some ( override_) ,
81
98
..Default :: default ( )
82
99
} ,
100
+ env : Default :: default ( ) ,
83
101
}
84
102
}
85
103
}
@@ -110,6 +128,7 @@ struct OverrideCfg<'a> {
110
128
components : Vec < String > ,
111
129
targets : Vec < String > ,
112
130
profile : Option < dist:: Profile > ,
131
+ env : HashMap < String , String > ,
113
132
}
114
133
115
134
impl < ' a > OverrideCfg < ' a > {
@@ -150,6 +169,30 @@ impl<'a> OverrideCfg<'a> {
150
169
. as_deref ( )
151
170
. map ( dist:: Profile :: from_str)
152
171
. transpose ( ) ?,
172
+ env : file
173
+ . env
174
+ . into_iter ( )
175
+ . filter_map ( |( k, v) | {
176
+ let ( v, force, relative) = match v {
177
+ EnvOverride :: Literal ( v) => ( v, false , false ) ,
178
+ EnvOverride :: Table {
179
+ value,
180
+ force,
181
+ relative,
182
+ } => ( value, force, relative) ,
183
+ } ;
184
+
185
+ if relative {
186
+ return Some ( Err ( anyhow ! (
187
+ "Rustup does not yet support config-relative values"
188
+ ) ) ) ;
189
+ }
190
+ if !force && process ( ) . var_os ( & k) . is_some ( ) {
191
+ return None ;
192
+ }
193
+ Some ( Ok ( ( k, v) ) )
194
+ } )
195
+ . collect :: < Result < _ , _ > > ( ) ?,
153
196
} )
154
197
}
155
198
}
@@ -492,7 +535,7 @@ impl Cfg {
492
535
}
493
536
494
537
pub fn which_binary ( & self , path : & Path , binary : & str ) -> Result < Option < PathBuf > > {
495
- let ( toolchain, _) = self . find_or_install_override_toolchain_or_default ( path) ?;
538
+ let ( toolchain, _, _ ) = self . find_or_install_override_toolchain_or_default ( path) ?;
496
539
Ok ( Some ( toolchain. binary_file ( binary) ) )
497
540
}
498
541
@@ -729,7 +772,11 @@ impl Cfg {
729
772
pub fn find_or_install_override_toolchain_or_default (
730
773
& self ,
731
774
path : & Path ,
732
- ) -> Result < ( Toolchain < ' _ > , Option < OverrideReason > ) > {
775
+ ) -> Result < (
776
+ Toolchain < ' _ > ,
777
+ HashMap < String , String > ,
778
+ Option < OverrideReason > ,
779
+ ) > {
733
780
fn components_exist (
734
781
distributable : & DistributableToolchain < ' _ > ,
735
782
components : & [ & str ] ,
@@ -773,14 +820,15 @@ impl Cfg {
773
820
}
774
821
}
775
822
776
- if let Some ( ( toolchain, components, targets, reason, profile) ) =
823
+ if let Some ( ( toolchain, components, targets, reason, profile, env ) ) =
777
824
match self . find_override_config ( path) ? {
778
825
Some ( (
779
826
OverrideCfg {
780
827
toolchain,
781
828
components,
782
829
targets,
783
830
profile,
831
+ env,
784
832
} ,
785
833
reason,
786
834
) ) => {
@@ -790,13 +838,13 @@ impl Cfg {
790
838
None
791
839
} ;
792
840
793
- toolchain
794
- . or ( default )
795
- . map ( |toolchain| ( toolchain , components , targets , Some ( reason ) , profile ) )
841
+ toolchain. or ( default ) . map ( |toolchain| {
842
+ ( toolchain , components , targets , Some ( reason ) , profile , env )
843
+ } )
796
844
}
797
845
None => self
798
846
. find_default ( ) ?
799
- . map ( |toolchain| ( toolchain, vec ! [ ] , vec ! [ ] , None , None ) ) ,
847
+ . map ( |toolchain| ( toolchain, vec ! [ ] , vec ! [ ] , None , None , Default :: default ( ) ) ) ,
800
848
}
801
849
{
802
850
if toolchain. is_custom ( ) {
@@ -816,7 +864,7 @@ impl Cfg {
816
864
}
817
865
}
818
866
819
- Ok ( ( toolchain, reason) )
867
+ Ok ( ( toolchain, env , reason) )
820
868
} else {
821
869
// No override and no default set
822
870
Err ( RustupError :: ToolchainNotSelected . into ( ) )
@@ -905,21 +953,29 @@ impl Cfg {
905
953
pub fn toolchain_for_dir (
906
954
& self ,
907
955
path : & Path ,
908
- ) -> Result < ( Toolchain < ' _ > , Option < OverrideReason > ) > {
956
+ ) -> Result < (
957
+ Toolchain < ' _ > ,
958
+ HashMap < String , String > ,
959
+ Option < OverrideReason > ,
960
+ ) > {
909
961
self . find_or_install_override_toolchain_or_default ( path)
910
962
}
911
963
912
964
pub fn create_command_for_dir ( & self , path : & Path , binary : & str ) -> Result < Command > {
913
- let ( ref toolchain, _) = self . toolchain_for_dir ( path) ?;
965
+ let ( ref toolchain, ref env , _) = self . toolchain_for_dir ( path) ?;
914
966
915
- if let Some ( cmd) = self . maybe_do_cargo_fallback ( toolchain, binary) ? {
967
+ let mut cmd = if let Some ( cmd) = self . maybe_do_cargo_fallback ( toolchain, binary) ? {
916
968
Ok ( cmd)
917
969
} else {
918
970
// NB this can only fail in race conditions since we used toolchain
919
971
// for dir.
920
972
let installed = toolchain. as_installed_common ( ) ?;
921
973
installed. create_command ( binary)
922
- }
974
+ } ?;
975
+
976
+ cmd. envs ( env) ;
977
+
978
+ Ok ( cmd)
923
979
}
924
980
925
981
pub fn create_command_for_toolchain (
@@ -1043,7 +1099,8 @@ mod tests {
1043
1099
components: None ,
1044
1100
targets: None ,
1045
1101
profile: None ,
1046
- }
1102
+ } ,
1103
+ env: Default :: default ( ) ,
1047
1104
}
1048
1105
) ;
1049
1106
}
@@ -1070,7 +1127,8 @@ profile = "default"
1070
1127
"thumbv2-none-eabi" . into( )
1071
1128
] ) ,
1072
1129
profile: Some ( "default" . into( ) ) ,
1073
- }
1130
+ } ,
1131
+ env: Default :: default ( ) ,
1074
1132
}
1075
1133
) ;
1076
1134
}
@@ -1091,7 +1149,8 @@ channel = "nightly-2020-07-10"
1091
1149
components: None ,
1092
1150
targets: None ,
1093
1151
profile: None ,
1094
- }
1152
+ } ,
1153
+ env: Default :: default ( ) ,
1095
1154
}
1096
1155
) ;
1097
1156
}
@@ -1112,7 +1171,8 @@ path = "foobar"
1112
1171
components: None ,
1113
1172
targets: None ,
1114
1173
profile: None ,
1115
- }
1174
+ } ,
1175
+ env: Default :: default ( ) ,
1116
1176
}
1117
1177
) ;
1118
1178
}
@@ -1134,7 +1194,8 @@ components = []
1134
1194
components: Some ( vec![ ] ) ,
1135
1195
targets: None ,
1136
1196
profile: None ,
1137
- }
1197
+ } ,
1198
+ env: Default :: default ( ) ,
1138
1199
}
1139
1200
) ;
1140
1201
}
@@ -1156,7 +1217,8 @@ targets = []
1156
1217
components: None ,
1157
1218
targets: Some ( vec![ ] ) ,
1158
1219
profile: None ,
1159
- }
1220
+ } ,
1221
+ env: Default :: default ( ) ,
1160
1222
}
1161
1223
) ;
1162
1224
}
@@ -1177,7 +1239,8 @@ components = [ "rustfmt" ]
1177
1239
components: Some ( vec![ "rustfmt" . into( ) ] ) ,
1178
1240
targets: None ,
1179
1241
profile: None ,
1180
- }
1242
+ } ,
1243
+ env: Default :: default ( ) ,
1181
1244
}
1182
1245
) ;
1183
1246
}
@@ -1195,6 +1258,103 @@ components = [ "rustfmt" ]
1195
1258
) ) ;
1196
1259
}
1197
1260
1261
+ #[ test]
1262
+ fn parse_toml_toolchain_file_env_literal ( ) {
1263
+ // XXX: It'd be nice if it was possible to specify [env] but _not_ [toolchain],
1264
+ // but that seems to currently cause an "empty config" error.
1265
+ let contents = r#"
1266
+ [toolchain]
1267
+ channel = "nightly-2020-07-10"
1268
+ [env]
1269
+ OPENSSL_DIR = "/opt/openssl"
1270
+ "# ;
1271
+
1272
+ let result = Cfg :: parse_override_file ( contents, ParseMode :: Both ) ;
1273
+ assert_eq ! (
1274
+ result. unwrap( ) ,
1275
+ OverrideFile {
1276
+ toolchain: ToolchainSection {
1277
+ channel: Some ( "nightly-2020-07-10" . into( ) ) ,
1278
+ path: None ,
1279
+ components: None ,
1280
+ targets: None ,
1281
+ profile: None ,
1282
+ } ,
1283
+ env: HashMap :: from( [ (
1284
+ String :: from( "OPENSSL_DIR" ) ,
1285
+ EnvOverride :: Literal ( String :: from( "/opt/openssl" ) )
1286
+ ) ] ) ,
1287
+ }
1288
+ ) ;
1289
+ }
1290
+
1291
+ #[ test]
1292
+ fn parse_toml_toolchain_file_env_table ( ) {
1293
+ let contents = r#"
1294
+ [toolchain]
1295
+ channel = "nightly-2020-07-10"
1296
+ [env]
1297
+ TMPDIR = { value = "/home/tmp", force = true }
1298
+ OPENSSL_DIR = { value = "vendor/openssl", relative = true }
1299
+ "# ;
1300
+
1301
+ let result = Cfg :: parse_override_file ( contents, ParseMode :: Both ) ;
1302
+ assert_eq ! (
1303
+ result. unwrap( ) ,
1304
+ OverrideFile {
1305
+ toolchain: ToolchainSection {
1306
+ channel: Some ( "nightly-2020-07-10" . into( ) ) ,
1307
+ path: None ,
1308
+ components: None ,
1309
+ targets: None ,
1310
+ profile: None ,
1311
+ } ,
1312
+ env: HashMap :: from( [
1313
+ (
1314
+ String :: from( "TMPDIR" ) ,
1315
+ EnvOverride :: Table {
1316
+ value: String :: from( "/home/tmp" ) ,
1317
+ force: true ,
1318
+ relative: false
1319
+ }
1320
+ ) ,
1321
+ (
1322
+ String :: from( "OPENSSL_DIR" ) ,
1323
+ EnvOverride :: Table {
1324
+ value: String :: from( "vendor/openssl" ) ,
1325
+ force: false ,
1326
+ relative: true
1327
+ }
1328
+ )
1329
+ ] ) ,
1330
+ }
1331
+ ) ;
1332
+ }
1333
+
1334
+ #[ test]
1335
+ fn parse_empty_toml_toolchain_file_env ( ) {
1336
+ let contents = r#"
1337
+ [toolchain]
1338
+ channel = "nightly-2020-07-10"
1339
+ [env]
1340
+ "# ;
1341
+
1342
+ let result = Cfg :: parse_override_file ( contents, ParseMode :: Both ) ;
1343
+ assert_eq ! (
1344
+ result. unwrap( ) ,
1345
+ OverrideFile {
1346
+ toolchain: ToolchainSection {
1347
+ channel: Some ( "nightly-2020-07-10" . into( ) ) ,
1348
+ path: None ,
1349
+ components: None ,
1350
+ targets: None ,
1351
+ profile: None ,
1352
+ } ,
1353
+ env: Default :: default ( ) ,
1354
+ }
1355
+ ) ;
1356
+ }
1357
+
1198
1358
#[ test]
1199
1359
fn parse_empty_toolchain_file ( ) {
1200
1360
let contents = "" ;
0 commit comments