@@ -23,30 +23,31 @@ namespace Microsoft.Build.Cargo
23
23
/// </summary>
24
24
public class CargoTask : Task
25
25
{
26
- private static readonly string ? _tempPath = Environment . GetEnvironmentVariable ( "TEMP" ) ;
27
- private static readonly string _rustUpBinary = $ " { _tempPath } \\ cargohome\\ bin\\ rustup.exe";
28
- private static readonly string _cargoPath = $ " { _tempPath } \\ cargohome\\ bin\\ cargo.exe";
29
- private static readonly string _rustInstallPath = $ " { _tempPath } \\ rustinstall";
30
- private static readonly string _rustUpInitBinary = $ " { _rustInstallPath } \\ rustup-init.exe";
31
- private static readonly string _cargoHome = $ " { _tempPath } \\ cargohome";
32
- private static readonly string _rustUpHome = $ " { _tempPath } \\ rustuphome";
33
- private static readonly string _cargoHomeBin = $ " { _tempPath } \\ cargohome\\ bin\\ " ;
34
- private static readonly string _msRustUpBinary = $ " { _tempPath } \\ cargohome\\ bin\\ msrustup.exe";
26
+ private static readonly string ? _tempPath = Environment . GetEnvironmentVariable ( "TEMP" ) ?? throw new Exception ( "%TEMP% directory not defined" ) ;
27
+ private static readonly string _rustUpBinary = Path . Combine ( _tempPath , " cargohome" , " bin" , " rustup.exe") ;
28
+ private static readonly string _cargoPath = Path . Combine ( _tempPath , " cargohome" , " bin" , " cargo.exe") ;
29
+ private static readonly string _rustInstallPath = Path . Combine ( _tempPath , " rustinstall") ;
30
+ private static readonly string _rustUpInitBinary = Path . Combine ( _rustInstallPath , " rustup-init.exe") ;
31
+ private static readonly string _cargoHome = Path . Combine ( _tempPath , " cargohome") ;
32
+ private static readonly string _rustUpHome = Path . Combine ( _tempPath , " rustuphome") ;
33
+ private static readonly string _cargoHomeBin = Path . Combine ( _tempPath , " cargohome" , " bin" ) ;
34
+ private static readonly string _msRustUpBinary = Path . Combine ( _tempPath , " cargohome" , " bin" , " msrustup.exe") ;
35
35
private static readonly Dictionary < string , string > _envVars = new ( ) { { "CARGO_HOME" , _cargoHome } , { "RUSTUP_HOME" , _rustUpHome } } ;
36
36
private static readonly string _rustUpDownloadLink = "https://static.rust-lang.org/rustup/dist/x86_64-pc-windows-msvc/rustup-init.exe" ;
37
37
private static readonly string _checkSumVerifyUrl = "https://static.rust-lang.org/rustup/dist/x86_64-pc-windows-msvc/rustup-init.exe.sha256" ;
38
38
private static readonly string _rustToolChainFileName = "rust-toolchain.toml" ;
39
- private static readonly string _configFileName = " config.toml";
39
+ private static readonly string _cargoConfigFilePath = Path . Combine ( ".cargo" , " config.toml") ;
40
40
private static readonly string _cargoFileName = "cargo.toml" ;
41
41
private static readonly string _nugetConfigFileName = "nuget.config" ;
42
42
private static readonly string _clearCacheCommand = "clearcargocache" ;
43
43
private static readonly string _installCommand = "install" ;
44
44
private static readonly string _fetchCommand = "fetch" ;
45
45
private static readonly string _loginCommand = "login" ;
46
46
private string ? _rustUpFile = Environment . GetEnvironmentVariable ( "MSRUSTUP_FILE" ) ;
47
- private bool _shouldCleanRustPath ;
47
+ private bool _shouldCleanRustPath = false ;
48
48
private bool _isMsRustUp = false ;
49
49
private string ? _currentRustUpInitExeCheckSum ;
50
+ private List < string > _cargoRegistries = new ( ) ;
50
51
51
52
private enum ExitCode
52
53
{
@@ -114,6 +115,41 @@ private async Task<bool> ExecuteAsync()
114
115
}
115
116
else if ( Command . Equals ( _fetchCommand ) )
116
117
{
118
+ if ( _isMsRustUp )
119
+ {
120
+ if ( string . IsNullOrEmpty ( _rustUpFile ) || ! File . Exists ( _rustUpFile ) )
121
+ {
122
+ Log . LogMessage ( $ "MSRUSTUP_FILE environment variable is not set or the file does not exist. Assuming local build.") ;
123
+ }
124
+ else
125
+ {
126
+ try
127
+ {
128
+ var val = System . Text . Encoding . UTF8 . GetString ( Convert . FromBase64String ( File . ReadAllText ( _rustUpFile ) ) ) ;
129
+
130
+ if ( ! _envVars . ContainsKey ( "CARGO_REGISTRY_GLOBAL_CREDENTIAL_PROVIDERS" ) )
131
+ {
132
+ _envVars . Add ( "CARGO_REGISTRY_GLOBAL_CREDENTIAL_PROVIDERS" , "cargo:token" ) ;
133
+ }
134
+
135
+ foreach ( var registry in GetRegistries ( Path . Combine ( RepoRoot , _cargoConfigFilePath ) ) )
136
+ {
137
+ var registryName = registry . Trim ( ) . ToUpper ( ) ;
138
+ _cargoRegistries . Add ( registryName ) ;
139
+ var tokenName = $ "CARGO_REGISTRIES_{ registryName } _TOKEN";
140
+ if ( ! _envVars . ContainsKey ( tokenName ) )
141
+ {
142
+ _envVars . Add ( tokenName , $ "Bearer { val } ") ;
143
+ }
144
+ }
145
+ }
146
+ catch ( FormatException ex )
147
+ {
148
+ Log . LogError ( $ "Failed to decode MSRUSTUP_FILE content: { ex . Message } ") ;
149
+ }
150
+ }
151
+ }
152
+
117
153
return await FetchCratesAsync ( StartupProj ) ;
118
154
}
119
155
else if ( Command . Equals ( _clearCacheCommand , StringComparison . InvariantCultureIgnoreCase ) )
@@ -151,16 +187,16 @@ private async Task<ExitCode> CargoRunCommandAsync(string command, string args)
151
187
Log . LogMessage ( MessageImportance . Normal , $ "Executing cargo command: { command } { args } ") ;
152
188
if ( _isMsRustUp )
153
189
{
154
- var customCargoBin = GetCustomToolChainPath ( ) ;
190
+ var customCargoBin = GetCustomToolChainCargoPath ( ) ;
155
191
if ( ! string . IsNullOrEmpty ( customCargoBin ) )
156
192
{
157
- bool debugConfig = true ;
193
+ bool isDebugConfiguration = true ;
158
194
if ( ! Configuration . Equals ( "debug" , StringComparison . InvariantCultureIgnoreCase ) )
159
195
{
160
- debugConfig = false ;
196
+ isDebugConfiguration = false ;
161
197
}
162
198
163
- return await ExecuteProcessAsync ( customCargoBin ! , $ "{ command } { args } --offline { ( debugConfig ? string . Empty : "--" + Configuration . ToLowerInvariant ( ) ) } --config { Path . Combine ( RepoRoot , _configFileName ) } ", "." , _envVars ) ;
199
+ return await ExecuteProcessAsync ( customCargoBin ! , $ "{ command } { args } --offline { ( isDebugConfiguration ? string . Empty : "--" + Configuration . ToLowerInvariant ( ) ) } --config { Path . Combine ( RepoRoot , _cargoConfigFilePath ) } ", "." , _envVars ) ;
164
200
}
165
201
166
202
// if we don't have the toolchain, we need to install it.
@@ -172,15 +208,18 @@ private async Task<ExitCode> CargoRunCommandAsync(string command, string args)
172
208
173
209
private async Task < bool > DownloadAndInstallRust ( )
174
210
{
175
- if ( await DownloadRustUpAsync ( ) )
211
+ bool downloadSuccess = await DownloadRustUpAsync ( ) ;
212
+ bool installSuccess = false ;
213
+ if ( downloadSuccess )
176
214
{
177
- if ( await InstallRust ( ) )
215
+ installSuccess = await InstallRust ( ) ;
216
+ if ( installSuccess )
178
217
{
179
218
_shouldCleanRustPath = true ;
180
219
}
181
220
}
182
221
183
- return true ;
222
+ return downloadSuccess && installSuccess ;
184
223
}
185
224
186
225
private async Task < bool > FetchCratesAsync ( string project )
@@ -237,30 +276,6 @@ [new ProjectGraphEntryPoint(project)],
237
276
238
277
Log . LogMessage ( MessageImportance . Normal , $ "Cargo, Auth Enabled: { EnableAuth } ") ;
239
278
240
- if ( _isMsRustUp )
241
- {
242
- if ( string . IsNullOrEmpty ( _rustUpFile ) || ! File . Exists ( _rustUpFile ) )
243
- {
244
- Log . LogError ( $ "MSRUSTUP_FILE environment variable is not set or the file does not exist.") ;
245
- return false ;
246
- }
247
-
248
- var val = System . Text . Encoding . UTF8 . GetString ( Convert . FromBase64String ( File . ReadAllText ( _rustUpFile ) ) ) ;
249
-
250
- if ( _envVars . ContainsKey ( "CARGO_REGISTRY_GLOBAL_CREDENTIAL_PROVIDERS" ) )
251
- {
252
- _envVars . Remove ( "CARGO_REGISTRY_GLOBAL_CREDENTIAL_PROVIDERS" ) ;
253
- }
254
-
255
- if ( _envVars . ContainsKey ( "CARGO_REGISTRIES_RUST_PUBLICPACKAGES_TOKEN" ) )
256
- {
257
- _envVars . Remove ( "CARGO_REGISTRIES_RUST_PUBLICPACKAGES_TOKEN" ) ;
258
- }
259
-
260
- _envVars . Add ( "CARGO_REGISTRY_GLOBAL_CREDENTIAL_PROVIDERS" , "cargo:token" ) ;
261
- _envVars . Add ( "CARGO_REGISTRIES_RUST_PUBLICPACKAGES_TOKEN" , $ "Bearer { val } ") ;
262
- }
263
-
264
279
foreach ( var projects in rustProjects )
265
280
{
266
281
string path = projects ;
@@ -276,10 +291,6 @@ [new ProjectGraphEntryPoint(project)],
276
291
if ( success )
277
292
{
278
293
Log . LogMessage ( MessageImportance . Normal , $ "Cargo fetching completed successfully in { stopwatch . Elapsed . Seconds } seconds") ;
279
- if ( _isMsRustUp )
280
- {
281
- _envVars . Remove ( "CARGO_REGISTRIES_RUST_PUBLICPACKAGES_TOKEN" ) ;
282
- }
283
294
}
284
295
else
285
296
{
@@ -324,13 +335,13 @@ private async Task<ExitCode> RustFetchAsync(string workingDir, bool authorize =
324
335
if ( authResult == ExitCode . Succeeded )
325
336
{
326
337
string path = _cargoPath ;
327
- string args = $ "fetch { ( _isMsRustUp ? "--config " + Path . Combine ( RepoRoot , _configFileName ) : string . Empty ) } ";
338
+ string args = $ "fetch { ( _isMsRustUp ? "--config " + Path . Combine ( RepoRoot , _cargoConfigFilePath ) : string . Empty ) } ";
328
339
ExitCode exitCode = ExitCode . Failed ;
329
340
Log . LogMessage ( MessageImportance . Normal , $ "Fetching cargo crates for project in { workingDir } ") ;
330
341
331
342
if ( File . Exists ( Path . Combine ( RepoRoot , _rustToolChainFileName ) ) )
332
343
{
333
- var customCargoBin = GetCustomToolChainPath ( ) ;
344
+ var customCargoBin = GetCustomToolChainCargoPath ( ) ;
334
345
if ( ! string . IsNullOrEmpty ( customCargoBin ) )
335
346
{
336
347
exitCode = await ExecuteProcessAsync ( customCargoBin ! , args , workingDir , _envVars ) ;
@@ -449,36 +460,52 @@ private async Task<bool> InstallRust()
449
460
var rootToolchainPath = Path . Combine ( StartupProj , _rustToolChainFileName ) ;
450
461
var useMsRustUp = File . Exists ( rootToolchainPath ) && IsMSToolChain ( rootToolchainPath ) ;
451
462
var rustUpBinary = useMsRustUp ? _msRustUpBinary : _rustUpBinary ;
452
- if ( ( File . Exists ( _cargoPath ) && Directory . Exists ( _rustUpHome ) && File . Exists ( _rustUpBinary ) ) || ( File . Exists ( _cargoHome ) && useMsRustUp != true ) || File . Exists ( _msRustUpBinary ) )
463
+ bool msRustupToolChainExists = useMsRustUp && ! string . IsNullOrEmpty ( GetCustomToolChainCargoPath ( ) ) ;
464
+ bool cargoPathAndRustPathsExists = Directory . Exists ( _cargoHome ) && Directory . Exists ( _rustUpHome ) ;
465
+ bool cargoBinaryExists = File . Exists ( _cargoPath ) ;
466
+ if ( ( msRustupToolChainExists && cargoPathAndRustPathsExists && useMsRustUp ) || cargoPathAndRustPathsExists && cargoBinaryExists && ! useMsRustUp )
453
467
{
454
- return false ;
468
+ return true ;
455
469
}
456
470
457
471
ExitCode exitCode = ExitCode . Succeeded ;
458
472
ExitCode exitCodeLatest = ExitCode . Succeeded ;
459
- Log . LogMessage ( MessageImportance . Normal , "Installing Rust" ) ;
460
- exitCode = await ExecuteProcessAsync ( _rustUpInitBinary , "-y" , "." , _envVars ) ;
461
- if ( exitCode != ExitCode . Succeeded )
473
+
474
+ if ( useMsRustUp )
462
475
{
463
- Log . LogMessage ( MessageImportance . Normal , "Installed Rust successfully" ) ;
476
+ if ( ! _envVars . ContainsKey ( "MSRUSTUP_FEED_URL" ) )
477
+ {
478
+ _envVars . Add ( "MSRUSTUP_FEED_URL" , GetNugetFeedUrl ( ) ?? string . Empty ) ;
479
+ }
464
480
}
465
481
466
- if ( useMsRustUp )
482
+ if ( ( ! cargoBinaryExists && ! useMsRustUp ) || ! cargoPathAndRustPathsExists )
467
483
{
468
- string ? workingDirPart = new DirectoryInfo ( BuildEngine . ProjectFileOfTaskNode ) . Parent ? . Parent ? . FullName ;
469
- if ( Directory . Exists ( workingDirPart ) )
484
+ Log . LogMessage ( MessageImportance . Normal , "Installing Rust" ) ;
485
+ exitCode = await ExecuteProcessAsync ( _rustUpInitBinary , "-y" , "." , _envVars ) ;
486
+ if ( exitCode != ExitCode . Succeeded )
470
487
{
471
- Log . LogMessage ( MessageImportance . Normal , "Installing MSRustup " ) ;
472
- string distRootPath = Path . Combine ( workingDirPart ! , "content \\ dist" ) ;
473
- var installationExitCode = await ExecuteProcessAsync ( "powershell.exe" , $ ". \\ msrustup.ps1 ' { _cargoHomeBin } '" , distRootPath , _envVars ) ;
474
- if ( installationExitCode == ExitCode . Succeeded )
475
- {
476
- Log . LogMessage ( MessageImportance . Normal , "Installed MSRustup successfully" ) ;
477
- }
478
- else
488
+ Log . LogMessage ( MessageImportance . Normal , "Installed Rust successfully " ) ;
489
+ }
490
+
491
+ if ( useMsRustUp )
492
+ {
493
+ string ? workingDirPart = new DirectoryInfo ( BuildEngine . ProjectFileOfTaskNode ) . Parent ? . Parent ? . FullName ;
494
+
495
+ if ( Directory . Exists ( workingDirPart ) )
479
496
{
480
- Log . LogError ( "MSRustup failed to installed successfully" ) ;
481
- return false ;
497
+ Log . LogMessage ( MessageImportance . Normal , "Installing MSRustup" ) ;
498
+ string distRootPath = Path . Combine ( workingDirPart ! , "content\\ dist" ) ;
499
+ var installationExitCode = await ExecuteProcessAsync ( "powershell.exe" , $ ".\\ msrustup.ps1 '{ _cargoHomeBin } '", distRootPath , _envVars ) ;
500
+ if ( installationExitCode == ExitCode . Succeeded )
501
+ {
502
+ Log . LogMessage ( MessageImportance . Normal , "Installed MSRustup successfully" ) ;
503
+ }
504
+ else
505
+ {
506
+ Log . LogError ( "MSRustup failed to installed successfully" ) ;
507
+ return false ;
508
+ }
482
509
}
483
510
}
484
511
}
@@ -489,16 +516,24 @@ private async Task<bool> InstallRust()
489
516
490
517
if ( string . IsNullOrEmpty ( _rustUpFile ) || ! File . Exists ( _rustUpFile ) )
491
518
{
492
- Log . LogError ( $ "MSRUSTUP_FILE environment variable is not set or the file does not exist.") ;
493
- return false ;
519
+ Log . LogMessage ( $ "MSRUSTUP_FILE environment variable is not set or the file does not exist. Assuming local build.") ;
494
520
}
521
+ else
522
+ {
523
+ var val = System . Text . Encoding . UTF8 . GetString ( Convert . FromBase64String ( File . ReadAllText ( _rustUpFile ) ) ) ;
524
+ if ( ! _envVars . ContainsKey ( "MSRUSTUP_PAT" ) )
525
+ {
526
+ _envVars . Add ( "MSRUSTUP_PAT" , val ) ;
527
+ }
528
+ }
529
+
530
+ if ( ! _envVars . ContainsKey ( "MSRUSTUP_HOME" ) )
531
+ {
532
+ _envVars . Add ( "MSRUSTUP_HOME" , _cargoHome ) ;
533
+ }
534
+
535
+ exitCodeLatest = await ExecuteProcessAsync ( rustUpBinary , $ "toolchain install { GetToolChainVersion ( ) } ", StartupProj , _envVars ) ;
495
536
496
- var val = System . Text . Encoding . UTF8 . GetString ( Convert . FromBase64String ( File . ReadAllText ( _rustUpFile ) ) ) ;
497
- _envVars . Add ( "MSRUSTUP_PAT" , val ) ;
498
- _envVars . Add ( "MSRUSTUP_HOME" , _cargoHome ) ;
499
- _envVars . Add ( "MSRUSTUP_FEED_URL" , GetNugetFeedUrl ( ) ?? string . Empty ) ;
500
- exitCodeLatest = await ExecuteProcessAsync ( rustUpBinary , "toolchain install" , StartupProj , _envVars ) ;
501
- _envVars . Remove ( "MSRUSTUP_PAT" ) ;
502
537
if ( exitCodeLatest == ExitCode . Succeeded )
503
538
{
504
539
Log . LogMessage ( MessageImportance . Normal , "Installed custom toolchain successfully" ) ;
@@ -559,7 +594,7 @@ private string GetToolChainVersion()
559
594
return string . Empty ;
560
595
}
561
596
562
- private string ? GetCustomToolChainPath ( )
597
+ private string ? GetCustomToolChainCargoPath ( )
563
598
{
564
599
var toolchainVersion = GetToolChainVersion ( ) ;
565
600
if ( ! string . IsNullOrEmpty ( toolchainVersion ) )
@@ -568,7 +603,6 @@ private string GetToolChainVersion()
568
603
var toolchainPath = Path . Combine ( _cargoHome , "toolchains" , toolchainVersion ) ;
569
604
if ( ! Directory . Exists ( toolchainPath ) )
570
605
{
571
- Log . LogError ( $ "Toolchain { toolchainVersion } not found. Please run 'cargo install' to install the required toolchain.") ;
572
606
return null ;
573
607
}
574
608
@@ -595,5 +629,27 @@ private async Task<string> GetHashAsync()
595
629
596
630
return _currentRustUpInitExeCheckSum ;
597
631
}
632
+
633
+ private List < string > GetRegistries ( string configPath )
634
+ {
635
+ string config = File . ReadAllText ( configPath ) ;
636
+ Regex regex = new ( @"(?<=\[registries\]).*?(?=\[|#)" , RegexOptions . Singleline ) ;
637
+ var matches = regex . Matches ( config ) ;
638
+ List < string > registries = new ( ) ;
639
+ foreach ( Match match in matches )
640
+ {
641
+ if ( string . IsNullOrWhiteSpace ( match . Value ) || ! match . Value . Contains ( '=' ) )
642
+ {
643
+ continue ;
644
+ }
645
+
646
+ var registryNames = match . Value . Split ( new [ ] { '\r ' , '\n ' } , StringSplitOptions . RemoveEmptyEntries )
647
+ . Select ( line => line . Split ( '=' ) [ 0 ] . Trim ( ) )
648
+ . ToList ( ) ;
649
+ registries . AddRange ( registryNames ) ;
650
+ }
651
+
652
+ return registries ;
653
+ }
598
654
}
599
655
}
0 commit comments