Skip to content

Commit b07a0c9

Browse files
authored
[Xamarin.Android.Build.Tasks] %(AndroidMavenLibrary.ArtifactFilename) (#9479)
Fixes: #9465 Context: 3659766 In all of the examples we used when developing and testing `@(AndroidMavenLibrary)` (3659766), the Maven artifact filename followed the pattern `{artifact.Id}-{artifact.Version}.[jar|aar]`, so this is the default filename we construct for the user. However, there exist some packages like React that do not follow this pattern and instead do e.g.: `react-android-0.76.0-release.aar`. Attempting to use `@(AndroidMavenLibrary)` for React produces the following error: error XA4236: Cannot download Maven artifact 'com.facebook.react:react-android'. error XA4236: - react-android-0.76.0.jar: Response status code does not indicate success: 404 (Not Found). error XA4236: - react-android-0.76.0.aar: Response status code does not indicate success: 404 (Not Found). To support these cases, add a new optional `%(ArtifactFilename)` item metadata that can be specified to override the Maven filename of the artifact: <AndroidMavenLibrary Include="com.facebook.react:react-android" Version="0.76.0" ArtifactFilename="react-android-0.76.0-release.aar" /> This allows our tasks to properly download the artifact and avoid the error.
1 parent 38e8857 commit b07a0c9

File tree

6 files changed

+74
-29
lines changed

6 files changed

+74
-29
lines changed

src/Xamarin.Android.Build.Tasks/Properties/Resources.Designer.cs

Lines changed: 1 addition & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Xamarin.Android.Build.Tasks/Properties/Resources.resx

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -977,15 +977,11 @@ To use a custom JDK path for a command line build, set the 'JavaSdkDirectory' MS
977977
</data>
978978
<data name="XA4236" xml:space="preserve">
979979
<value>Cannot download Maven artifact '{0}:{1}'.
980-
- {2}: {3}
981-
- {4}: {5}</value>
980+
{2}</value>
982981
<comment>The following are literal names and should not be translated: Maven
983982
{0} - Maven artifact group id
984983
{1} - Maven artifact id
985-
{2} - The .jar filename we tried to download
986-
{3} - The HttpClient reported download exception message
987-
{4} - The .aar filename we tried to download
988-
{5} - The HttpClient provided download exception message</comment>
984+
{2} - The filenames we tried to download and the HttpClient reported download exception messages</comment>
989985
</data>
990986
<data name="XA4237" xml:space="preserve">
991987
<value>Cannot download POM file for Maven artifact '{0}'.

src/Xamarin.Android.Build.Tasks/Tasks/MavenDownload.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,11 @@ public async override System.Threading.Tasks.Task RunTaskAsync ()
8787
if (repository is null)
8888
return null;
8989

90+
// Allow user to override the Maven filename of the artifact
91+
var maven_override_filename = item.GetMetadataOrDefault ("ArtifactFilename", null);
92+
9093
// Download artifact
91-
var artifact_file = await MavenExtensions.DownloadPayload (repository, artifact, MavenCacheDirectory, Log, CancellationToken);
94+
var artifact_file = await MavenExtensions.DownloadPayload (repository, artifact, MavenCacheDirectory, maven_override_filename, Log, CancellationToken);
9295

9396
if (artifact_file is null)
9497
return null;

src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/MavenDownloadTests.cs

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,14 +170,46 @@ public async Task MavenGoogleSuccess ()
170170
}
171171
}
172172

173-
ITaskItem CreateMavenTaskItem (string name, string? version, string? repository = null)
173+
[Test]
174+
public async Task ArtifactFilenameOverride ()
175+
{
176+
// Technically the artifact is 'react-android-0.76.1-release.aar' but we're going to override the filename to
177+
// 'react-android-0.76.1.module' and download it instead for this test because the real .aar is 120+ MB.
178+
var temp_cache_dir = Path.Combine (Path.GetTempPath (), Guid.NewGuid ().ToString ());
179+
180+
try {
181+
var engine = new MockBuildEngine (TestContext.Out, new List<BuildErrorEventArgs> ());
182+
var task = new MavenDownload {
183+
BuildEngine = engine,
184+
MavenCacheDirectory = temp_cache_dir,
185+
AndroidMavenLibraries = [CreateMavenTaskItem ("com.facebook.react:react-android", "0.76.1", artifactFilename: "react-android-0.76.1.module")],
186+
};
187+
188+
await task.RunTaskAsync ();
189+
190+
Assert.AreEqual (0, engine.Errors.Count);
191+
Assert.AreEqual (1, task.ResolvedAndroidMavenLibraries?.Length);
192+
193+
var output_item = task.ResolvedAndroidMavenLibraries! [0];
194+
195+
Assert.AreEqual ("com.facebook.react:react-android:0.76.1", output_item.GetMetadata ("JavaArtifact"));
196+
Assert.True (output_item.ItemSpec.EndsWith (Path.Combine ("0.76.1", "react-android-0.76.1.module"), StringComparison.OrdinalIgnoreCase));
197+
Assert.AreEqual (Path.Combine (temp_cache_dir, "central", "com.facebook.react", "react-android", "0.76.1", "react-android-0.76.1.pom"), output_item.GetMetadata ("Manifest"));
198+
} finally {
199+
DeleteTempDirectory (temp_cache_dir);
200+
}
201+
}
202+
203+
ITaskItem CreateMavenTaskItem (string name, string? version, string? repository = null, string? artifactFilename = null)
174204
{
175205
var item = new TaskItem (name);
176206

177207
if (version is not null)
178208
item.SetMetadata ("Version", version);
179209
if (repository is not null)
180210
item.SetMetadata ("Repository", repository);
211+
if (artifactFilename is not null)
212+
item.SetMetadata ("ArtifactFilename", artifactFilename);
181213

182214
return item;
183215
}

src/Xamarin.Android.Build.Tasks/Utilities/ITaskItemExtensions.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.Diagnostics.CodeAnalysis;
34
using System.Linq;
45
using System.Xml.Linq;
56
using Microsoft.Android.Build.Tasks;
@@ -23,7 +24,8 @@ public static IEnumerable<XElement> ToXElements (this ICollection<ITaskItem> ite
2324
}
2425
}
2526

26-
public static string GetMetadataOrDefault (this ITaskItem item, string name, string defaultValue)
27+
[return: NotNullIfNotNull (nameof (defaultValue))]
28+
public static string? GetMetadataOrDefault (this ITaskItem item, string name, string? defaultValue)
2729
{
2830
var value = item.GetMetadata (name);
2931

src/Xamarin.Android.Build.Tasks/Utilities/MavenExtensions.cs

Lines changed: 31 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.Diagnostics.CodeAnalysis;
66
using System.IO;
77
using System.Linq;
8+
using System.Text;
89
using System.Threading;
910
using System.Threading.Tasks;
1011
using Java.Interop.Tools.Maven.Models;
@@ -77,7 +78,7 @@ public static bool TryParseArtifacts (string id, TaskLoggingHelper log, out List
7778
return result;
7879
}
7980

80-
public static bool TryParseJavaArtifact (this ITaskItem task, string type, TaskLoggingHelper log, [NotNullWhen (true)]out Artifact? artifact, out bool attributesSpecified)
81+
public static bool TryParseJavaArtifact (this ITaskItem task, string type, TaskLoggingHelper log, [NotNullWhen (true)] out Artifact? artifact, out bool attributesSpecified)
8182
{
8283
var result = TryParseJavaArtifacts (task, type, log, out var artifacts, out attributesSpecified);
8384

@@ -130,45 +131,57 @@ public static bool TryParseJavaArtifacts (this ITaskItem task, string type, Task
130131
}
131132

132133
// Returns artifact output path
133-
public static async Task<string?> DownloadPayload (CachedMavenRepository repository, Artifact artifact, string cacheDir, TaskLoggingHelper log, CancellationToken cancellationToken)
134+
public static async Task<string?> DownloadPayload (CachedMavenRepository repository, Artifact artifact, string cacheDir, string? mavenOverrideFilename, TaskLoggingHelper log, CancellationToken cancellationToken)
134135
{
135136
var output_directory = Path.Combine (cacheDir, repository.Name, artifact.GroupId, artifact.Id, artifact.Version);
136137

137138
Directory.CreateDirectory (output_directory);
138139

139-
var filename = $"{artifact.Id}-{artifact.Version}";
140-
var jar_filename = Path.Combine (output_directory, Path.Combine ($"{filename}.jar"));
141-
var aar_filename = Path.Combine (output_directory, Path.Combine ($"{filename}.aar"));
140+
var files_to_check = new List<string> ();
141+
142+
if (mavenOverrideFilename.HasValue ()) {
143+
files_to_check.Add (Path.Combine (output_directory, mavenOverrideFilename));
144+
} else {
145+
files_to_check.Add (Path.Combine (output_directory, $"{artifact.Id}-{artifact.Version}.jar"));
146+
files_to_check.Add (Path.Combine (output_directory, $"{artifact.Id}-{artifact.Version}.aar"));
147+
}
142148

143149
// We don't need to redownload if we already have a cached copy
144-
if (File.Exists (jar_filename))
145-
return jar_filename;
150+
foreach (var file in files_to_check) {
151+
if (File.Exists (file))
152+
return file;
153+
}
146154

147-
if (File.Exists (aar_filename))
148-
return aar_filename;
155+
// Try to download the file from Maven
156+
var results = new List<(string file, string error)> ();
149157

150-
if (await TryDownloadPayload (repository, artifact, jar_filename, cancellationToken) is not string jar_error)
151-
return jar_filename;
158+
foreach (var file in files_to_check) {
159+
if (await TryDownloadPayload (repository, artifact, Path.GetFileName (file), cancellationToken) is not string error)
160+
return file;
152161

153-
if (await TryDownloadPayload (repository, artifact, aar_filename, cancellationToken) is not string aar_error)
154-
return aar_filename;
162+
results.Add ((file, error));
163+
}
155164

156-
log.LogCodedError ("XA4236", Properties.Resources.XA4236, artifact.GroupId, artifact.Id, Path.GetFileName (jar_filename), jar_error, Path.GetFileName (aar_filename), aar_error);
165+
// Couldn't download the artifact, construct an error message for the user
166+
var error_builder = new StringBuilder ();
167+
168+
foreach (var error in results)
169+
error_builder.AppendLine ($"- {Path.GetFileName (error.file)}: {error.error}");
170+
171+
log.LogCodedError ("XA4236", Properties.Resources.XA4236, artifact.GroupId, artifact.Id, error_builder.ToString ().TrimEnd ());
157172

158173
return null;
159174
}
160175

161176
// Return value is download error message, null represents success (async methods cannot have out parameters)
162177
static async Task<string?> TryDownloadPayload (CachedMavenRepository repository, Artifact artifact, string filename, CancellationToken cancellationToken)
163178
{
164-
var maven_filename = $"{artifact.Id}-{artifact.Version}{Path.GetExtension (filename)}";
165-
166179
try {
167-
if ((await repository.GetFilePathAsync (artifact, maven_filename, cancellationToken)) is string path) {
180+
if ((await repository.GetFilePathAsync (artifact, filename, cancellationToken)) is string path) {
168181
return null;
169182
} else {
170183
// This probably(?) cannot be hit, everything should come back as an exception
171-
return $"Could not download {maven_filename}";
184+
return $"Could not download {filename}";
172185
}
173186

174187
} catch (Exception ex) {

0 commit comments

Comments
 (0)