@@ -22,9 +22,6 @@ protected override async Task<bool> Execute (Context context)
22
22
dotnetPath = dotnetPath . TrimEnd ( new char [ ] { Path . DirectorySeparatorChar } ) ;
23
23
var dotnetTool = Path . Combine ( dotnetPath , "dotnet" ) ;
24
24
25
- // Always delete the bin/$(Configuration)/dotnet/ directory
26
- Utilities . DeleteDirectory ( dotnetPath ) ;
27
-
28
25
if ( ! await InstallDotNetAsync ( context , dotnetPath , BuildToolVersion ) ) {
29
26
Log . ErrorLine ( $ "Installation of dotnet SDK { BuildToolVersion } failed.") ;
30
27
return false ;
@@ -65,68 +62,189 @@ protected override async Task<bool> Execute (Context context)
65
62
return true ;
66
63
}
67
64
68
- async Task < bool > InstallDotNetAsync ( Context context , string dotnetPath , string version , bool runtimeOnly = false )
65
+ async Task < bool > DownloadDotNetInstallScript ( Context context , string dotnetScriptPath , Uri dotnetScriptUrl )
69
66
{
70
- if ( Directory . Exists ( Path . Combine ( dotnetPath , "sdk" , version ) ) && ! runtimeOnly ) {
71
- Log . Status ( $ "dotnet SDK version ") ;
72
- Log . Status ( version , ConsoleColor . Yellow ) ;
73
- Log . StatusLine ( " already installed in: " , Path . Combine ( dotnetPath , "sdk" , version ) , tailColor : ConsoleColor . Cyan ) ;
74
- return true ;
67
+ string tempDotnetScriptPath = dotnetScriptPath + "-tmp" ;
68
+ Utilities . DeleteFile ( tempDotnetScriptPath ) ;
69
+
70
+ Log . StatusLine ( "Downloading dotnet-install..." ) ;
71
+
72
+ ( bool success , ulong size , HttpStatusCode status ) = await Utilities . GetDownloadSizeWithStatus ( dotnetScriptUrl ) ;
73
+ if ( ! success ) {
74
+ string message ;
75
+ if ( status == HttpStatusCode . NotFound ) {
76
+ message = "dotnet-install URL not found" ;
77
+ } else {
78
+ message = $ "Failed to obtain dotnet-install size. HTTP status code: { status } ({ ( int ) status } )";
79
+ }
80
+
81
+ return ReportAndCheckCached ( message , quietOnError : true ) ;
75
82
}
76
83
77
- if ( Directory . Exists ( Path . Combine ( dotnetPath , "shared" , "Microsoft.NETCore.App" , version ) ) && runtimeOnly ) {
78
- Log . Status ( $ "dotnet runtime version ") ;
79
- Log . Status ( version , ConsoleColor . Yellow ) ;
80
- Log . StatusLine ( " already installed in: " , Path . Combine ( dotnetPath , "shared" , "Microsoft.NETCore.App" , version ) , tailColor : ConsoleColor . Cyan ) ;
81
- return true ;
84
+ DownloadStatus downloadStatus = Utilities . SetupDownloadStatus ( context , size , context . InteractiveSession ) ;
85
+ Log . StatusLine ( $ " { context . Characters . Link } { dotnetScriptUrl } ", ConsoleColor . White ) ;
86
+ await Download ( context , dotnetScriptUrl , tempDotnetScriptPath , "dotnet-install" , Path . GetFileName ( dotnetScriptUrl . LocalPath ) , downloadStatus ) ;
87
+
88
+ if ( ! File . Exists ( tempDotnetScriptPath ) ) {
89
+ return ReportAndCheckCached ( $ "Download of dotnet-install from { dotnetScriptUrl } failed") ;
82
90
}
83
91
84
- Uri dotnetScriptUrl = Configurables . Urls . DotNetInstallScript ;
85
- string dotnetScriptPath = Path . Combine ( dotnetPath , Path . GetFileName ( dotnetScriptUrl . LocalPath ) ) ;
86
- if ( File . Exists ( dotnetScriptPath ) )
87
- Utilities . DeleteFile ( dotnetScriptPath ) ;
92
+ Utilities . CopyFile ( tempDotnetScriptPath , dotnetScriptPath ) ;
93
+ Utilities . DeleteFile ( tempDotnetScriptPath ) ;
94
+ return true ;
88
95
89
- Log . StatusLine ( "Downloading dotnet-install..." ) ;
96
+ bool ReportAndCheckCached ( string message , bool quietOnError = false )
97
+ {
98
+ if ( File . Exists ( dotnetScriptPath ) ) {
99
+ Log . WarningLine ( message ) ;
100
+ Log . WarningLine ( $ "Using cached installation script found in { dotnetScriptPath } ") ;
101
+ return true ;
102
+ }
90
103
91
- ( bool success , ulong size , HttpStatusCode status ) = await Utilities . GetDownloadSizeWithStatus ( dotnetScriptUrl ) ;
104
+ if ( ! quietOnError ) {
105
+ Log . ErrorLine ( message ) ;
106
+ Log . ErrorLine ( $ "Cached installation script not found in { dotnetScriptPath } ") ;
107
+ }
108
+ return false ;
109
+ }
110
+ }
111
+
112
+ async Task < bool > DownloadDotNetArchive ( Context context , string archiveDestinationPath , Uri archiveUrl )
113
+ {
114
+ Log . StatusLine ( "Downloading dotnet archive..." ) ;
115
+
116
+ ( bool success , ulong size , HttpStatusCode status ) = await Utilities . GetDownloadSizeWithStatus ( archiveUrl ) ;
92
117
if ( ! success ) {
93
- if ( status == HttpStatusCode . NotFound )
94
- Log . ErrorLine ( "dotnet-install URL not found" ) ;
95
- else
96
- Log . ErrorLine ( "Failed to obtain dotnet-install size. HTTP status code: {status} ({(int)status})" ) ;
118
+ if ( status == HttpStatusCode . NotFound ) {
119
+ Log . ErrorLine ( $ "dotnet archive URL { archiveUrl } not found") ;
120
+ return false ;
121
+ } else {
122
+ Log . WarningLine ( $ "Failed to obtain dotnet archive size. HTTP status code: { status } ({ ( int ) status } )") ;
123
+ }
124
+
97
125
return false ;
98
126
}
99
127
128
+ string tempArchiveDestinationPath = archiveDestinationPath + "-tmp" ;
129
+ Utilities . DeleteFile ( tempArchiveDestinationPath ) ;
130
+
100
131
DownloadStatus downloadStatus = Utilities . SetupDownloadStatus ( context , size , context . InteractiveSession ) ;
101
- Log . StatusLine ( $ " { context . Characters . Link } { dotnetScriptUrl } ", ConsoleColor . White ) ;
102
- await Download ( context , dotnetScriptUrl , dotnetScriptPath , "dotnet-install " , Path . GetFileName ( dotnetScriptUrl . LocalPath ) , downloadStatus ) ;
132
+ Log . StatusLine ( $ " { context . Characters . Link } { archiveUrl } ", ConsoleColor . White ) ;
133
+ await Download ( context , archiveUrl , tempArchiveDestinationPath , "dotnet archive " , Path . GetFileName ( archiveUrl . LocalPath ) , downloadStatus ) ;
103
134
104
- if ( ! File . Exists ( dotnetScriptPath ) ) {
105
- Log . ErrorLine ( $ "Download of dotnet-install from { dotnetScriptUrl } failed") ;
135
+ if ( ! File . Exists ( tempArchiveDestinationPath ) ) {
106
136
return false ;
107
137
}
108
138
109
- var type = runtimeOnly ? "runtime" : "SDK" ;
110
- Log . StatusLine ( $ "Installing dotnet { type } '{ version } '...") ;
139
+ Utilities . CopyFile ( tempArchiveDestinationPath , archiveDestinationPath ) ;
140
+ Utilities . DeleteFile ( tempArchiveDestinationPath ) ;
141
+
142
+ return true ;
143
+ }
111
144
145
+ string [ ] GetInstallationScriptArgs ( string version , string dotnetPath , string dotnetScriptPath , bool onlyGetUrls , bool runtimeOnly )
146
+ {
147
+ List < string > args ;
112
148
if ( Context . IsWindows ) {
113
- var args = new List < string > {
149
+ args = new List < string > {
114
150
"-NoProfile" , "-ExecutionPolicy" , "unrestricted" , "-file" , dotnetScriptPath ,
115
151
"-Version" , version , "-InstallDir" , dotnetPath , "-Verbose"
116
152
} ;
117
- if ( runtimeOnly )
153
+ if ( runtimeOnly ) {
118
154
args . AddRange ( new string [ ] { "-Runtime" , "dotnet" } ) ;
155
+ }
156
+ if ( onlyGetUrls ) {
157
+ args . Add ( "-DryRun" ) ;
158
+ }
119
159
120
- return Utilities . RunCommand ( "powershell.exe" , args . ToArray ( ) ) ;
121
- } else {
122
- var args = new List < string > {
123
- dotnetScriptPath , "--version" , version , "--install-dir" , dotnetPath , "--verbose"
124
- } ;
125
- if ( runtimeOnly )
126
- args . AddRange ( new string [ ] { "-Runtime" , "dotnet" } ) ;
160
+ return args . ToArray ( ) ;
161
+ }
162
+
163
+ args = new List < string > {
164
+ dotnetScriptPath , "--version" , version , "--install-dir" , dotnetPath , "--verbose"
165
+ } ;
166
+
167
+ if ( runtimeOnly ) {
168
+ args . AddRange ( new string [ ] { "-Runtime" , "dotnet" } ) ;
169
+ }
170
+ if ( onlyGetUrls ) {
171
+ args . Add ( "--dry-run" ) ;
172
+ }
173
+
174
+ return args . ToArray ( ) ;
175
+ }
176
+
177
+ async Task < bool > InstallDotNetAsync ( Context context , string dotnetPath , string version , bool runtimeOnly = false )
178
+ {
179
+ string cacheDir = context . Properties . GetRequiredValue ( KnownProperties . AndroidToolchainCacheDirectory ) ;
127
180
128
- return Utilities . RunCommand ( "bash" , args . ToArray ( ) ) ;
181
+ // Always delete the bin/$(Configuration)/dotnet/ directory
182
+ Utilities . DeleteDirectory ( dotnetPath ) ;
183
+
184
+ Uri dotnetScriptUrl = Configurables . Urls . DotNetInstallScript ;
185
+ string scriptFileName = Path . GetFileName ( dotnetScriptUrl . LocalPath ) ;
186
+ string cachedDotnetScriptPath = Path . Combine ( cacheDir , scriptFileName ) ;
187
+ if ( ! await DownloadDotNetInstallScript ( context , cachedDotnetScriptPath , dotnetScriptUrl ) ) {
188
+ return false ;
129
189
}
190
+
191
+ string dotnetScriptPath = Path . Combine ( dotnetPath , scriptFileName ) ;
192
+ Utilities . CopyFile ( cachedDotnetScriptPath , dotnetScriptPath ) ;
193
+
194
+ var type = runtimeOnly ? "runtime" : "SDK" ;
195
+
196
+ Log . StatusLine ( $ "Discovering download URLs for dotnet { type } '{ version } '...") ;
197
+ string scriptCommand = Context . IsWindows ? "powershell.exe" : "bash" ;
198
+ string [ ] scriptArgs = GetInstallationScriptArgs ( version , dotnetPath , dotnetScriptPath , onlyGetUrls : true , runtimeOnly : runtimeOnly ) ;
199
+ string scriptReply = Utilities . GetStringFromStdout ( scriptCommand , scriptArgs ) ;
200
+ var archiveUrls = new List < string > ( ) ;
201
+
202
+ char [ ] fieldSplitChars = new char [ ] { ':' } ;
203
+ foreach ( string l in scriptReply . Split ( new char [ ] { '\n ' } ) ) {
204
+ string line = l . Trim ( ) ;
205
+
206
+ if ( ! line . StartsWith ( "dotnet-install: URL #" , StringComparison . OrdinalIgnoreCase ) ) {
207
+ continue ;
208
+ }
209
+
210
+ string [ ] parts = line . Split ( fieldSplitChars , 3 ) ;
211
+ if ( parts . Length < 3 ) {
212
+ Log . WarningLine ( $ "dotnet-install URL line has unexpected number of parts. Expected 3, got { parts . Length } ") ;
213
+ Log . WarningLine ( $ "Line: { line } ") ;
214
+ continue ;
215
+ }
216
+
217
+ archiveUrls . Add ( parts [ 2 ] . Trim ( ) ) ;
218
+ }
219
+
220
+ if ( archiveUrls . Count == 0 ) {
221
+ Log . WarningLine ( "No dotnet archive URLs discovered, attempting to run the installation script" ) ;
222
+ scriptArgs = GetInstallationScriptArgs ( version , dotnetPath , dotnetScriptPath , onlyGetUrls : false , runtimeOnly : runtimeOnly ) ;
223
+ return Utilities . RunCommand ( scriptCommand , scriptArgs ) ;
224
+ }
225
+
226
+ string ? archivePath = null ;
227
+ foreach ( string url in archiveUrls ) {
228
+ var archiveUrl = new Uri ( url ) ;
229
+ string archiveDestinationPath = Path . Combine ( cacheDir , Path . GetFileName ( archiveUrl . LocalPath ) ) ;
230
+
231
+ if ( File . Exists ( archiveDestinationPath ) ) {
232
+ archivePath = archiveDestinationPath ;
233
+ break ;
234
+ }
235
+
236
+ if ( await DownloadDotNetArchive ( context , archiveDestinationPath , archiveUrl ) ) {
237
+ archivePath = archiveDestinationPath ;
238
+ break ;
239
+ }
240
+ }
241
+
242
+ if ( String . IsNullOrEmpty ( archivePath ) ) {
243
+ return false ;
244
+ }
245
+
246
+ Log . StatusLine ( $ "Installing dotnet { type } '{ version } '...") ;
247
+ return await Utilities . Unpack ( archivePath , dotnetPath ) ;
130
248
}
131
249
132
250
bool TestDotNetSdk ( string dotnetTool )
0 commit comments