Skip to content

Commit acbeaac

Browse files
Requested changes from review + build refactoring
~~ xa-prep-tasks ~~ The `<DownloadUri/>` MSBuild task now has an optional `HashHeader` property: - If set, a http HEAD request is made to the URL, and the destination file path gets a suffix added for the value. - To make this work properly, `DestinationFiles` is also an `[Output]` property, since these paths could change as a result. ~~ android-toolchain ~~ Chromium's `depot_tools` are downloaded the same as all of our other dependencies. I also updated all `<DownloadUri/>` calls to specify `HashHeader` and `DestinationFiles` as an `<Output/>` While implementing this, I became quite frustrated waiting on all of the other zip files to get deleted and recreated... So I went ahead and split up these operations into four targets: - `_UnzipAndroidSdkItems` with `@(_AndroidSdkItemDownloads)` as inputs - `_UnzipAndroidNdkItems` with `@(_AndroidNdkItemDownloads)` as inputs - `_UnzipAntItems` with `@(_AntItemDownloads)` as inputs - `_UnzipChromeItems` with `@(_ChromeItemDownloads)` as inputs In the future, if these downloads change, *all* of the directories won't need to be nuked and extracted again. I did this in a way so that the existing stamp files will be preserved, so after this change is merged, locally the build on your machine will only need to download `depot_tools` and extract it if you have a reasonably recent `~/android-toolchain` directory. Other changes: - Fixed formatting - Removed usage of the obsolete `<CreateItem/>` MSBuild task - `_UnzipChromeItems` also "bootstraps" `depot_tools`, and has to set/unset `$PATH` appropriately. I did this in a similar manner as the license acceptance for the Android SDK. ~~ r8.targets ~~ - Removed all of the `depot_tools` downloading code, and setup `android-toolchain` as a `@(ProjectReference)` - All targets that set `$PATH`, unset it when finished - Use `$(_GradleArgs)` to supply `--no-daemon` - `_CleanR8` should delete `*.jar` files in our build tree ~~ Xamarin.Android.Build.Tasks ~~ - Fixed any formatting that we noticed - Renamed `$(AndroidD8JarPath)` and `$(AndroidR8JarPath)` to be prefixed with `Android` - Refactored `$(IntermediateOutputPath)_dex_stamp` stamp file in `_CompileToDalvikWithDx` and `_CompileToDalvikWithD8` to match the new convention of `$(_AndroidStampDirectory)_CompileToDalvik.stamp` - `*.dex` files weren't in `FileWrites`??? - `CompileToDalvik` had a `DexOutputs` output property that was completely unused, so I removed it. Also removed extra log messages.
1 parent f1b7cad commit acbeaac

File tree

10 files changed

+225
-195
lines changed

10 files changed

+225
-195
lines changed

Configuration.props

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
<CmakePath Condition=" '$(CmakePath)' == '' ">$(AndroidSdkCmakeDirectory)\bin\cmake</CmakePath>
6565
<NinjaPath Condition=" '$(NinjaPath)' == '' ">$(AndroidSdkCmakeDirectory)\bin\ninja</NinjaPath>
6666
<AntDirectory>$(AndroidToolchainDirectory)\ant</AntDirectory>
67+
<ChromeToolsDirectory>$(AndroidToolchainDirectory)\depot_tools</ChromeToolsDirectory>
6768
<AntToolPath>$(AntDirectory)\bin</AntToolPath>
6869
<AndroidSupportedHostJitAbis Condition=" '$(AndroidSupportedHostJitAbis)' == '' ">$(HostOS)</AndroidSupportedHostJitAbis>
6970
<AndroidSupportedTargetJitAbis Condition=" '$(AndroidSupportedTargetJitAbis)' == '' ">armeabi-v7a:x86</AndroidSupportedTargetJitAbis>

Documentation/guides/BuildProcess.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,11 @@ when packaing Release applications.
210210

211211
This property is `False` by default.
212212

213+
- **AndroidD8JarPath** &ndash; The path to `d8.jar` for use with the
214+
d8 dex-compiler. Defaults to a path in the Xamarin.Android
215+
installation. For further information see our documentation on [D8
216+
and R8][d8-r8].
217+
213218
- **AndroidDexTool** &ndash; An enum-style property with valid
214219
values of `dx` or `d8`. Indicates which Android [dex][dex]
215220
compiler is used during the Xamarin.Android build process.
@@ -406,6 +411,11 @@ when packaing Release applications.
406411
produce the actual `AndroidManifest.xml`.
407412
The `$(AndroidManifest)` must contain the package name in the `/manifest/@package` attribute.
408413

414+
- **AndroidR8JarPath** &ndash; The path to `r8.jar` for use with the
415+
r8 dex-compiler and shrinker. Defaults to a path in the
416+
Xamarin.Android installation. For further information see our
417+
documentation on [D8 and R8][d8-r8].
418+
409419
- **AndroidSdkBuildToolsVersion** &ndash; The Android SDK
410420
build-tools package provides the **aapt** and **zipalign** tools,
411421
among others. Multiple different versions of the build-tools package

build-tools/android-toolchain/android-toolchain.projitems

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
<PropertyGroup>
44
<AndroidUri Condition=" '$(AndroidUri)' == '' ">https://dl.google.com/android/repository</AndroidUri>
55
<AntUri Condition=" '$(AntUri)' == '' ">https://archive.apache.org/dist/ant/binaries</AntUri>
6+
<ChromeUri Condition=" '$(ChromeUri)' == '' ">https://storage.googleapis.com/chrome-infra</ChromeUri>
67
</PropertyGroup>
78
<ItemGroup>
89
<AndroidNdkItem Include="android-ndk-r14b-linux-x86_64">
@@ -162,4 +163,15 @@
162163
<HostOS></HostOS>
163164
</AntItem>
164165
</ItemGroup>
166+
<ItemGroup>
167+
<!--
168+
depot_tools, a set of git extensions from Chromium
169+
http://commondatastorage.googleapis.com/chrome-infra-docs/flat/depot_tools/docs/html/depot_tools_tutorial.html
170+
-->
171+
<ChromeItem Include="depot_tools">
172+
<HostOS></HostOS>
173+
<NoSubdirectory>True</NoSubdirectory>
174+
<HashHeader>x-goog-hash</HashHeader>
175+
</ChromeItem>
176+
</ItemGroup>
165177
</Project>

build-tools/android-toolchain/android-toolchain.targets

Lines changed: 97 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,95 +1,134 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3-
<UsingTask AssemblyFile="$(MSBuildThisFileDirectory)..\..\bin\Build$(Configuration)\xa-prep-tasks.dll" TaskName="Xamarin.Android.BuildTools.PrepTasks.AcceptAndroidSdkLicenses" />
4-
<UsingTask AssemblyFile="$(MSBuildThisFileDirectory)..\..\bin\Build$(Configuration)\xa-prep-tasks.dll" TaskName="Xamarin.Android.BuildTools.PrepTasks.GitCommitHash" />
3+
<UsingTask AssemblyFile="..\..\bin\Build$(Configuration)\xa-prep-tasks.dll" TaskName="Xamarin.Android.BuildTools.PrepTasks.AcceptAndroidSdkLicenses" />
4+
<UsingTask AssemblyFile="..\..\bin\Build$(Configuration)\xa-prep-tasks.dll" TaskName="Xamarin.Android.BuildTools.PrepTasks.GitCommitHash" />
5+
<UsingTask AssemblyFile="..\..\bin\Build$(Configuration)\xa-prep-tasks.dll" TaskName="Xamarin.Android.BuildTools.PrepTasks.SetEnvironmentVariable" />
56
<PropertyGroup>
67
<BuildDependsOn>
78
ResolveReferences;
89
_DownloadItems;
9-
_UnzipFiles;
10+
_UnzipAndroidSdkItems;
11+
_UnzipAndroidNdkItems;
12+
_UnzipAntItems;
13+
_UnzipChromeItems;
1014
_CreateMxeToolchains;
1115
</BuildDependsOn>
1216
</PropertyGroup>
1317
<UsingTask AssemblyFile="..\..\bin\Build$(Configuration)\Xamarin.Android.Tools.BootstrapTasks.dll" TaskName="Xamarin.Android.Tools.BootstrapTasks.UnzipDirectoryChildren" />
1418
<Target Name="_DetermineItems">
15-
<CreateItem
16-
Include="@(AndroidSdkItem)"
17-
Condition=" '%(HostOS)' == '$(HostOS)' Or '%(HostOS)' == '' ">
18-
<Output TaskParameter="Include" ItemName="_PlatformAndroidSdkItem"/>
19-
</CreateItem>
20-
<CreateItem
21-
Include="@(AndroidNdkItem)"
22-
Condition=" '%(HostOS)' == '$(HostOS)' Or '%(HostOS)' == '' ">
23-
<Output TaskParameter="Include" ItemName="_PlatformAndroidNdkItem"/>
24-
</CreateItem>
25-
<CreateItem
26-
Include="@(AntItem)"
27-
Condition=" '%(HostOS)' == '$(HostOS)' Or '%(HostOS)' == '' ">
28-
<Output TaskParameter="Include" ItemName="_PlatformAntItem"/>
29-
</CreateItem>
3019
<ItemGroup>
31-
<_SdkStampFiles Include="@(_PlatformAndroidSdkItem->'$(AndroidToolchainDirectory)\sdk\.stamp-%(Identity)')" />
32-
</ItemGroup>
33-
<ItemGroup>
34-
<_SdkStampFiles Include="@(_PlatformAntItem->'$(AntDirectory)\.stamp-%(Identity)')" />
35-
</ItemGroup>
36-
<ItemGroup>
37-
<_DownloadedItem Include="@(_PlatformAndroidSdkItem->'$(AndroidToolchainCacheDirectory)\%(Identity).zip')" />
38-
<_DownloadedItem Include="@(_PlatformAndroidNdkItem->'$(AndroidToolchainCacheDirectory)\%(Identity).zip')" />
39-
<_DownloadedItem Include="@(_PlatformAntItem->'$(AndroidToolchainCacheDirectory)\%(Identity).zip')" />
20+
<_PlatformAndroidSdkItem Include="@(AndroidSdkItem)" Condition=" '%(HostOS)' == '$(HostOS)' Or '%(HostOS)' == '' " />
21+
<_PlatformAndroidNdkItem Include="@(AndroidNdkItem)" Condition=" '%(HostOS)' == '$(HostOS)' Or '%(HostOS)' == '' " />
22+
<_PlatformAntItem Include="@(AntItem)" Condition=" '%(HostOS)' == '$(HostOS)' Or '%(HostOS)' == '' " />
23+
<_PlatformChromeItem Include="@(ChromeItem)" Condition=" '%(HostOS)' == '$(HostOS)' Or '%(HostOS)' == '' " />
4024
</ItemGroup>
4125
</Target>
4226
<Target Name="_DownloadItems"
43-
DependsOnTargets="_DetermineItems"
44-
Outputs="@(_DownloadedItem)">
27+
DependsOnTargets="_DetermineItems">
4528
<MakeDir Directories="$(AndroidToolchainCacheDirectory)" />
4629
<DownloadUri
47-
SourceUris="@(_PlatformAndroidSdkItem->'$(AndroidUri)/%(RelUrl)%(Identity).zip');@(_PlatformAndroidNdkItem->'$(AndroidUri)/%(RelUrl)%(Identity).zip')"
48-
DestinationFiles="@(_PlatformAndroidSdkItem->'$(AndroidToolchainCacheDirectory)\%(Identity).zip');@(_PlatformAndroidNdkItem->'$(AndroidToolchainCacheDirectory)\%(Identity).zip')"
49-
/>
30+
SourceUris="@(_PlatformAndroidSdkItem->'$(AndroidUri)/%(RelUrl)%(Identity).zip')"
31+
DestinationFiles="@(_PlatformAndroidSdkItem->'$(AndroidToolchainCacheDirectory)\%(Identity).zip')"
32+
HashHeader="%(_PlatformAndroidSdkItem.HashHeader)">
33+
<Output TaskParameter="DestinationFiles" ItemName="_AndroidSdkItemDownloads" />
34+
</DownloadUri>
35+
<DownloadUri
36+
SourceUris="@(_PlatformAndroidNdkItem->'$(AndroidUri)/%(RelUrl)%(Identity).zip')"
37+
DestinationFiles="@(_PlatformAndroidNdkItem->'$(AndroidToolchainCacheDirectory)\%(Identity).zip')"
38+
HashHeader="%(_PlatformAndroidNdkItem.HashHeader)">
39+
<Output TaskParameter="DestinationFiles" ItemName="_AndroidNdkItemDownloads" />
40+
</DownloadUri>
5041
<DownloadUri
5142
SourceUris="@(_PlatformAntItem->'$(AntUri)/%(RelUrl)%(Identity).zip')"
5243
DestinationFiles="@(_PlatformAntItem->'$(AndroidToolchainCacheDirectory)\%(Identity).zip')"
53-
/>
44+
HashHeader="%(_PlatformAntItem.HashHeader)">
45+
<Output TaskParameter="DestinationFiles" ItemName="_AntItemDownloads" />
46+
</DownloadUri>
47+
<DownloadUri
48+
SourceUris="@(_PlatformChromeItem->'$(ChromeUri)/%(RelUrl)%(Identity).zip')"
49+
DestinationFiles="@(_PlatformChromeItem->'$(AndroidToolchainCacheDirectory)\%(Identity).zip')"
50+
HashHeader="%(_PlatformChromeItem.HashHeader)">
51+
<Output TaskParameter="DestinationFiles" ItemName="_ChromeItemDownloads" />
52+
</DownloadUri>
5453
</Target>
55-
<Target Name="_UnzipFiles"
56-
DependsOnTargets="_DetermineItems"
57-
Inputs="@(_DownloadedItem)"
58-
Outputs="@(_SdkStampFiles);$(AndroidToolchainDirectory)\ndk\.stamp-ndk">
59-
<CreateItem
60-
Include="@(_PlatformAndroidSdkItem->'$(AndroidToolchainCacheDirectory)\%(_PlatformAndroidSdkItem.Identity).zip'">
61-
<Output TaskParameter="Include" ItemName="_AndroidSdkItems"/>
62-
</CreateItem>
63-
<CreateItem
64-
Include="@(_PlatformAndroidNdkItem->'$(AndroidToolchainCacheDirectory)\%(_PlatformAndroidNdkItem.Identity).zip'"
65-
Condition=" '%(HostOS)' == '$(HostOS)' Or '%(HostOS)' == '' ">
66-
<Output TaskParameter="Include" ItemName="_AndroidNdkItems"/>
67-
</CreateItem>
68-
69-
<RemoveDir Directories="$(AndroidSdkDirectory);$(AndroidNdkDirectory);$(AntDirectory)" />
70-
<MakeDir Directories="$(AndroidSdkDirectory);$(AndroidNdkDirectory);$(AntDirectory)" />
71-
54+
<Target Name="_UnzipAndroidSdkItems"
55+
DependsOnTargets="_DownloadItems"
56+
Inputs="@(_AndroidSdkItemDownloads)"
57+
Outputs="@(_AndroidSdkItemDownloads->'$(AndroidToolchainDirectory)\sdk\.stamp-%(FileName)')">
58+
<RemoveDir Directories="$(AndroidSdkDirectory)" />
59+
<MakeDir Directories="$(AndroidSdkDirectory)" />
7260
<UnzipDirectoryChildren
7361
HostOS="$(HostOS)"
74-
NoSubdirectory="%(_PlatformAndroidSdkItem.NoSubdirectory)"
75-
SourceFiles="@(_PlatformAndroidSdkItem->'$(AndroidToolchainCacheDirectory)\%(Identity).zip')"
62+
NoSubdirectory="%(_AndroidSdkItemDownloads.NoSubdirectory)"
63+
SourceFiles="@(_AndroidSdkItemDownloads)"
7664
DestinationFolder="$(AndroidToolchainDirectory)\sdk"
7765
/>
66+
<AcceptAndroidSdkLicenses AndroidSdkDirectory="$(AndroidSdkDirectory)" JavaSdkDirectory="$(JavaSdkDirectory)" />
67+
<Touch
68+
Files="@(_AndroidSdkItemDownloads->'$(AndroidToolchainDirectory)\sdk\.stamp-%(FileName)')"
69+
AlwaysCreate="True"
70+
/>
71+
</Target>
72+
<Target Name="_UnzipAndroidNdkItems"
73+
DependsOnTargets="_DownloadItems"
74+
Inputs="@(_AndroidNdkItemDownloads)"
75+
Outputs="$(AndroidToolchainDirectory)\ndk\.stamp-ndk">
76+
<RemoveDir Directories="$(AndroidNdkDirectory)" />
77+
<MakeDir Directories="$(AndroidNdkDirectory)" />
7878
<UnzipDirectoryChildren
7979
HostOS="$(HostOS)"
80-
NoSubdirectory="%(_PlatformAndroidSdkItem.NoSubdirectory)"
81-
SourceFiles="@(_PlatformAndroidNdkItem->'$(AndroidToolchainCacheDirectory)\%(Identity).zip')"
80+
NoSubdirectory="%(_AndroidNdkItemDownloads.NoSubdirectory)"
81+
SourceFiles="@(_AndroidNdkItemDownloads)"
8282
DestinationFolder="$(AndroidToolchainDirectory)\ndk"
8383
/>
84+
<Touch
85+
Files="$(AndroidToolchainDirectory)\ndk\.stamp-ndk"
86+
AlwaysCreate="True"
87+
/>
88+
</Target>
89+
<Target Name="_UnzipAntItems"
90+
DependsOnTargets="_DownloadItems"
91+
Inputs="@(_AntItemDownloads)"
92+
Outputs="@(_AntItemDownloads->'$(AntDirectory)\.stamp-%(FileName)')">
93+
<RemoveDir Directories="$(AntDirectory)" />
94+
<MakeDir Directories="$(AntDirectory)" />
8495
<UnzipDirectoryChildren
8596
HostOS="$(HostOS)"
86-
NoSubdirectory="%(_PlatformAndroidSdkItem.NoSubdirectory)"
87-
SourceFiles="@(_PlatformAntItem->'$(AndroidToolchainCacheDirectory)\%(Identity).zip')"
97+
NoSubdirectory="%(_AntItemDownloads.NoSubdirectory)"
98+
SourceFiles="@(_AntItemDownloads)"
8899
DestinationFolder="$(AntDirectory)"
89100
/>
90-
<AcceptAndroidSdkLicenses AndroidSdkDirectory="$(AndroidSdkDirectory)" JavaSdkDirectory="$(JavaSdkDirectory)" />
91101
<Touch
92-
Files="@(_SdkStampFiles);$(AndroidToolchainDirectory)\ndk\.stamp-ndk"
102+
Files="@(_AntItemDownloads->'$(AntDirectory)\.stamp-%(FileName)')"
103+
AlwaysCreate="True"
104+
/>
105+
</Target>
106+
<Target Name="_UnzipChromeItems"
107+
DependsOnTargets="_DownloadItems"
108+
Inputs="@(_ChromeItemDownloads)"
109+
Outputs="@(_ChromeItemDownloads->'$(ChromeToolsDirectory)\.stamp-%(FileName)')">
110+
<PropertyGroup>
111+
<_OriginalPath>$(PATH)</_OriginalPath>
112+
</PropertyGroup>
113+
<RemoveDir Directories="$(ChromeToolsDirectory)" />
114+
<MakeDir Directories="$(ChromeToolsDirectory)" />
115+
<UnzipDirectoryChildren
116+
HostOS="$(HostOS)"
117+
NoSubdirectory="%(_ChromeItemDownloads.NoSubdirectory)"
118+
SourceFiles="@(_ChromeItemDownloads)"
119+
DestinationFolder="$(ChromeToolsDirectory)"
120+
/>
121+
<Xamarin.Android.BuildTools.PrepTasks.SetEnvironmentVariable
122+
Name="PATH"
123+
Value="$([System.IO.Path]::GetFullPath('$(ChromeToolsDirectory)'))$(PathSeparator)$(_OriginalPath)"
124+
/>
125+
<Exec Command="gclient --version" />
126+
<Xamarin.Android.BuildTools.PrepTasks.SetEnvironmentVariable
127+
Name="PATH"
128+
Value="$(_OriginalPath)"
129+
/>
130+
<Touch
131+
Files="@(_ChromeItemDownloads->'$(ChromeToolsDirectory)\.stamp-%(FileName)')"
93132
AlwaysCreate="True"
94133
/>
95134
</Target>

build-tools/xa-prep-tasks/Xamarin.Android.BuildTools.PrepTasks/DownloadUri.cs

Lines changed: 45 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.IO;
3+
using System.Linq;
34
using System.Net.Http;
45
using System.Threading;
56
using System.Threading.Tasks;
@@ -21,9 +22,11 @@ public DownloadUri ()
2122
[Required]
2223
public string[] SourceUris { get; set; }
2324

24-
[Required]
25+
[Required, Output]
2526
public ITaskItem[] DestinationFiles { get; set; }
2627

28+
public string HashHeader { get; set; }
29+
2730
CancellationTokenSource cancellationTokenSource;
2831

2932
public void Cancel ()
@@ -33,42 +36,44 @@ public void Cancel ()
3336

3437
public override bool Execute ()
3538
{
36-
Log.LogMessage (MessageImportance.Low, "DownloadUri:");
37-
Log.LogMessage (MessageImportance.Low, " SourceUris:");
38-
foreach (var uri in SourceUris) {
39-
Log.LogMessage (MessageImportance.Low, " {0}", uri);
40-
}
41-
Log.LogMessage (MessageImportance.Low, " DestinationFiles:");
42-
foreach (var dest in DestinationFiles) {
43-
Log.LogMessage (MessageImportance.Low, " {0}", dest.ItemSpec);
44-
}
45-
4639
if (SourceUris.Length != DestinationFiles.Length) {
4740
Log.LogError ("SourceUris.Length must equal DestinationFiles.Length.");
4841
return false;
4942
}
5043

5144
var source = cancellationTokenSource = new CancellationTokenSource ();
52-
var tasks = new TTask [SourceUris.Length];
45+
var tasks = new Task<ITaskItem> [SourceUris.Length];
5346
using (var client = new HttpClient ()) {
5447
client.Timeout = TimeSpan.FromHours (3);
5548
for (int i = 0; i < SourceUris.Length; ++i) {
56-
tasks [i] = DownloadFile (client, source, SourceUris [i], DestinationFiles [i].ItemSpec);
49+
tasks [i] = DownloadFile (client, source, SourceUris [i], DestinationFiles [i]);
5750
}
5851
TTask.WaitAll (tasks, source.Token);
5952
}
6053

54+
DestinationFiles = tasks.Select (t => t.Result).ToArray ();
55+
6156
return !Log.HasLoggedErrors;
6257
}
6358

64-
async TTask DownloadFile (HttpClient client, CancellationTokenSource source, string uri, string destinationFile)
59+
async Task<ITaskItem> DownloadFile (HttpClient client, CancellationTokenSource source, string uri, ITaskItem destinationFile)
6560
{
66-
if (File.Exists (destinationFile)) {
61+
if (!string.IsNullOrEmpty (HashHeader)) {
62+
var hashSuffix = await CheckHashHeader (client, source, uri);
63+
if (!string.IsNullOrEmpty (hashSuffix)) {
64+
var directory = Path.GetDirectoryName (destinationFile.ItemSpec);
65+
var fileName = Path.GetFileNameWithoutExtension (destinationFile.ItemSpec);
66+
var extension = Path.GetExtension (destinationFile.ItemSpec);
67+
destinationFile.ItemSpec = Path.Combine (directory, fileName + "-" + hashSuffix + extension);
68+
Log.LogMessage (MessageImportance.Normal, $"Hash found using '{HashHeader}', destination file changing to '{destinationFile}'.");
69+
}
70+
}
71+
if (File.Exists (destinationFile.ItemSpec)) {
6772
Log.LogMessage (MessageImportance.Normal, $"Skipping uri '{uri}' as destination file already exists '{destinationFile}'.");
68-
return;
73+
return destinationFile;
6974
}
70-
var dp = Path.GetDirectoryName (destinationFile);
71-
var dn = Path.GetFileName (destinationFile);
75+
var dp = Path.GetDirectoryName (destinationFile.ItemSpec);
76+
var dn = Path.GetFileName (destinationFile.ItemSpec);
7277
var tempPath = Path.Combine (dp, "." + dn + ".download");
7378
Directory.CreateDirectory(dp);
7479

@@ -82,12 +87,33 @@ async TTask DownloadFile (HttpClient client, CancellationTokenSource source, str
8287
}
8388
}
8489
Log.LogMessage (MessageImportance.Low, $"mv '{tempPath}' '{destinationFile}'.");
85-
File.Move (tempPath, destinationFile);
90+
File.Move (tempPath, destinationFile.ItemSpec);
8691
}
8792
catch (Exception e) {
8893
Log.LogError ("Unable to download URL `{0}` to `{1}`: {2}", uri, destinationFile, e.Message);
8994
Log.LogErrorFromException (e);
9095
}
96+
return destinationFile;
97+
}
98+
99+
async Task<string> CheckHashHeader (HttpClient client, CancellationTokenSource source, string uri)
100+
{
101+
var request = new HttpRequestMessage (HttpMethod.Head, uri);
102+
using (var response = await client.SendAsync (request, source.Token)) {
103+
response.EnsureSuccessStatusCode ();
104+
if (response.Headers.TryGetValues (HashHeader, out var values)) {
105+
foreach (var value in values) {
106+
Log.LogMessage (MessageImportance.Low, $"{HashHeader}: {value}");
107+
108+
//Current format: `x-goog-hash: crc32c=8HATIw==`
109+
if (!string.IsNullOrWhiteSpace (value)) {
110+
return value.Trim ();
111+
}
112+
}
113+
}
114+
}
115+
116+
return null;
91117
}
92118
}
93119
}

0 commit comments

Comments
 (0)