@@ -120,74 +120,85 @@ private static BuildScript DownloadDotNet(IBuildActions actions, ILogger logger,
120120 {
121121 if ( ! string . IsNullOrEmpty ( version ) )
122122 // Specific version requested
123- return DownloadDotNetVersion ( actions , logger , tempWorkingDirectory , shouldCleanUp , installDir , version ) ;
123+ return DownloadDotNetVersion ( actions , logger , tempWorkingDirectory , shouldCleanUp , installDir , [ version ] ) ;
124124
125125 // Download versions mentioned in `global.json` files
126126 // See https://docs.microsoft.com/en-us/dotnet/core/tools/global-json
127- var installScript = BuildScript . Success ;
128- var validGlobalJson = false ;
127+ var versions = new List < string > ( ) ;
129128
130129 foreach ( var path in files . Where ( p => p . EndsWith ( "global.json" , StringComparison . Ordinal ) ) )
131130 {
132131 try
133132 {
134133 var o = JObject . Parse ( File . ReadAllText ( path ) ) ;
135- version = ( string ) ( o ? [ "sdk" ] ? [ "version" ] ! ) ;
134+ versions . Add ( ( string ) o ? [ "sdk" ] ? [ "version" ] ! ) ;
136135 }
137136 catch
138137 {
139- // not a valid global.json file
138+ // not a valid ` global.json` file
140139 continue ;
141140 }
142-
143- installScript &= DownloadDotNetVersion ( actions , logger , tempWorkingDirectory , shouldCleanUp , installDir , version ) ;
144- validGlobalJson = true ;
145141 }
146142
147- if ( validGlobalJson )
143+ if ( versions . Count > 0 )
148144 {
149- return installScript ;
145+ return DownloadDotNetVersion ( actions , logger , tempWorkingDirectory , shouldCleanUp , installDir , versions ) ;
150146 }
151147
152148 if ( ensureDotNetAvailable )
153149 {
154- return DownloadDotNetVersion ( actions , logger , tempWorkingDirectory , shouldCleanUp , installDir , LatestDotNetSdkVersion , needExactVersion : false ) ;
150+ return DownloadDotNetVersion ( actions , logger , tempWorkingDirectory , shouldCleanUp , installDir , [ LatestDotNetSdkVersion ] , needExactVersion : false ) ;
155151 }
156152
157153 return BuildScript . Failure ;
158154 }
159155
160156 /// <summary>
161- /// Returns a script for downloading a specific .NET SDK version , if the
162- /// version is not already installed.
157+ /// Returns a script for downloading specific .NET SDK versions , if the
158+ /// versions are not already installed.
163159 ///
164160 /// See https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-install-script.
165161 /// </summary>
166- private static BuildScript DownloadDotNetVersion ( IBuildActions actions , ILogger logger , string tempWorkingDirectory , bool shouldCleanUp , string path , string version , bool needExactVersion = true )
162+ private static BuildScript DownloadDotNetVersion ( IBuildActions actions , ILogger logger , string tempWorkingDirectory , bool shouldCleanUp , string path , IEnumerable < string > versions , bool needExactVersion = true )
167163 {
164+ if ( ! versions . Any ( ) )
165+ {
166+ logger . LogInfo ( "No .NET SDK versions requested." ) ;
167+ return BuildScript . Failure ;
168+ }
169+
168170 return BuildScript . Bind ( GetInstalledSdksScript ( actions ) , ( sdks , sdksRet ) =>
171+ {
172+ if (
173+ needExactVersion &&
174+ sdksRet == 0 &&
175+ // quadratic; should be OK, given that both `version` and `sdks` are expected to be small
176+ versions . All ( version => sdks . Any ( sdk => sdk . StartsWith ( version + " " , StringComparison . Ordinal ) ) ) )
169177 {
170- if ( needExactVersion && sdksRet == 0 && sdks . Count == 1 && sdks [ 0 ] . StartsWith ( version + " " , StringComparison . Ordinal ) )
171- {
172- // The requested SDK is already installed (and no other SDKs are installed), so
173- // no need to reinstall
174- return BuildScript . Failure ;
175- }
176- else if ( ! needExactVersion && sdksRet == 0 && sdks . Count > 0 )
177- {
178- // there's at least one SDK installed, so no need to reinstall
179- return BuildScript . Failure ;
180- }
181- else if ( ! needExactVersion && sdksRet != 0 )
182- {
183- logger . LogInfo ( "No .NET SDK found." ) ;
184- }
178+ // The requested SDKs are already installed, so no need to reinstall
179+ return BuildScript . Failure ;
180+ }
181+ else if ( ! needExactVersion && sdksRet == 0 && sdks . Count > 0 )
182+ {
183+ // there's at least one SDK installed, so no need to reinstall
184+ return BuildScript . Failure ;
185+ }
186+ else if ( ! needExactVersion && sdksRet != 0 )
187+ {
188+ logger . LogInfo ( "No .NET SDK found." ) ;
189+ }
185190
186- logger . LogInfo ( $ "Attempting to download .NET { version } ") ;
191+ BuildScript prelude ;
192+ BuildScript postlude ;
193+ Func < string , BuildScript > getInstall ;
187194
188- if ( actions . IsWindows ( ) )
189- {
195+ if ( actions . IsWindows ( ) )
196+ {
197+ prelude = BuildScript . Success ;
198+ postlude = BuildScript . Success ;
190199
200+ getInstall = version =>
201+ {
191202 var psCommand = $ "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; &([scriptblock]::Create((Invoke-WebRequest -UseBasicParsing 'https://dot.net/v1/dotnet-install.ps1'))) -Version { version } -InstallDir { path } ";
192203
193204 BuildScript GetInstall ( string pwsh ) =>
@@ -201,40 +212,55 @@ BuildScript GetInstall(string pwsh) =>
201212 Script ;
202213
203214 return GetInstall ( "pwsh" ) | GetInstall ( "powershell" ) ;
204- }
205- else
215+ } ;
216+ }
217+ else
218+ {
219+ var dotnetInstallPath = actions . PathCombine ( tempWorkingDirectory , ".dotnet" , "dotnet-install.sh" ) ;
220+
221+ var downloadDotNetInstallSh = BuildScript . DownloadFile (
222+ "https://dot.net/v1/dotnet-install.sh" ,
223+ dotnetInstallPath ,
224+ e => logger . LogWarning ( $ "Failed to download 'dotnet-install.sh': { e . Message } ") ) ;
225+
226+ var chmod = new CommandBuilder ( actions ) .
227+ RunCommand ( "chmod" ) .
228+ Argument ( "u+x" ) .
229+ Argument ( dotnetInstallPath ) ;
230+
231+ prelude = downloadDotNetInstallSh & chmod . Script ;
232+ postlude = shouldCleanUp ? BuildScript . DeleteFile ( dotnetInstallPath ) : BuildScript . Success ;
233+
234+ getInstall = version => new CommandBuilder ( actions ) .
235+ RunCommand ( dotnetInstallPath ) .
236+ Argument ( "--channel" ) .
237+ Argument ( "release" ) .
238+ Argument ( "--version" ) .
239+ Argument ( version ) .
240+ Argument ( "--install-dir" ) .
241+ Argument ( path ) . Script ;
242+ }
243+
244+ var installScript = prelude & BuildScript . Failure ;
245+
246+ var attempted = new HashSet < string > ( ) ;
247+ foreach ( var version in versions )
248+ {
249+ if ( ! attempted . Add ( version ) )
250+ continue ;
251+
252+ installScript = BuildScript . Bind ( installScript , combinedExit =>
206253 {
207- var dotnetInstallPath = actions . PathCombine ( tempWorkingDirectory , ".dotnet" , "dotnet-install.sh" ) ;
208-
209- var downloadDotNetInstallSh = BuildScript . DownloadFile (
210- "https://dot.net/v1/dotnet-install.sh" ,
211- dotnetInstallPath ,
212- e => logger . LogWarning ( $ "Failed to download 'dotnet-install.sh': { e . Message } ") ) ;
213-
214- var chmod = new CommandBuilder ( actions ) .
215- RunCommand ( "chmod" ) .
216- Argument ( "u+x" ) .
217- Argument ( dotnetInstallPath ) ;
218-
219- var install = new CommandBuilder ( actions ) .
220- RunCommand ( dotnetInstallPath ) .
221- Argument ( "--channel" ) .
222- Argument ( "release" ) .
223- Argument ( "--version" ) .
224- Argument ( version ) .
225- Argument ( "--install-dir" ) .
226- Argument ( path ) ;
227-
228- var buildScript = downloadDotNetInstallSh & chmod . Script & install . Script ;
229-
230- if ( shouldCleanUp )
231- {
232- buildScript &= BuildScript . DeleteFile ( dotnetInstallPath ) ;
233- }
234-
235- return buildScript ;
236- }
237- } ) ;
254+ logger . LogInfo ( $ "Attempting to download .NET { version } ") ;
255+
256+ // When there are multiple versions requested, we want to try to fetch them all, reporting
257+ // a successful exit code when at least one of them succeeds
258+ return combinedExit != 0 ? getInstall ( version ) : BuildScript . Bind ( getInstall ( version ) , _ => BuildScript . Success ) ;
259+ } ) ;
260+ }
261+
262+ return installScript & postlude ;
263+ } ) ;
238264 }
239265
240266 private static BuildScript GetInstalledSdksScript ( IBuildActions actions )
0 commit comments