Skip to content

Commit 693877e

Browse files
committed
[Xamarin.Android.Build.Tasks] Add support for @(AndroidMavenLibrary).
1 parent e82abf3 commit 693877e

File tree

8 files changed

+507
-0
lines changed

8 files changed

+507
-0
lines changed

build-tools/installers/create-installers.targets

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@
165165
<_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)libZipSharp.dll" />
166166
<_MSBuildFiles Include="@(_LocalizationLanguages->'$(MicrosoftAndroidSdkOutDir)%(Identity)\libZipSharp.resources.dll')" />
167167
<_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)libZipSharp.pdb" />
168+
<_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)MavenNet.dll" />
168169
<_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)Mono.Unix.dll" />
169170
<_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)Mono.Unix.dll.config" ExcludeFromAndroidNETSdk="true" />
170171
<_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)Mono.Unix.pdb" />
@@ -213,6 +214,7 @@
213214
<_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)Xamarin.Android.Bindings.Core.targets" />
214215
<_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)Xamarin.Android.Bindings.Documentation.targets" ExcludeFromAndroidNETSdk="true" />
215216
<_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)Xamarin.Android.Bindings.JarToXml.targets" ExcludeFromAndroidNETSdk="true" />
217+
<_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)Xamarin.Android.Bindings.Maven.targets" />
216218
<_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)Xamarin.Android.Build.Tasks.dll" />
217219
<_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)Xamarin.Android.Build.Tasks.pdb" />
218220
<_MSBuildFiles Include="@(_LocalizationLanguages->'$(MicrosoftAndroidSdkOutDir)%(Identity)\Java.Interop.Localization.resources.dll')" ExcludeFromAndroidNETSdk="true" />
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<!--
2+
***********************************************************************************************
3+
Xamarin.Android.Bindings.Maven.targets
4+
5+
This file contains MSBuild targets used to enable @(AndroidMavenLibrary) support.
6+
7+
***********************************************************************************************
8+
-->
9+
10+
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
11+
12+
<UsingTask TaskName="Xamarin.Android.Tasks.MavenDownloadTask" AssemblyFile="Xamarin.Android.Build.Tasks.dll" />
13+
14+
<PropertyGroup>
15+
<!-- Maven cache directory, locations copied from XBD which should be sufficiently battle-tested -->
16+
<MavenCacheDirectory Condition="'$(OS)'=='Unix' and '$(MavenCacheDirectory)'==''">$(HOME)\Library\Caches\MavenCacheDirectory\</MavenCacheDirectory>
17+
<MavenCacheDirectory Condition="'$(OS)'!='Unix' and '$(MavenCacheDirectory)'==''">$(LocalAppData)\MavenCacheDirectory\</MavenCacheDirectory>
18+
<MavenCacheDirectory Condition="'$(MavenCacheDirectory)' != '' and !HasTrailingSlash('$(MavenCacheDirectory)')">$(MavenCacheDirectory)\</MavenCacheDirectory>
19+
</PropertyGroup>
20+
21+
<Target Name="MavenRestore" BeforeTargets="_CategorizeAndroidLibraries" DependsOnTargets="ResolvePackageAssets">
22+
23+
<!-- Download artifacts and POMs from Maven to a local cache. -->
24+
<MavenDownloadTask MavenCacheDirectory="$(MavenCacheDirectory)" AndroidMavenLibraries="@(AndroidMavenLibrary)">
25+
<Output TaskParameter="ResolvedAndroidMavenLibraries" ItemName="_ResolvedAndroidMavenLibraries" />
26+
</MavenDownloadTask>
27+
28+
<!-- Add @(AndroidMavenLibrary)'s to @(AndroidLibrary)'s. -->
29+
<ItemGroup>
30+
<AndroidLibrary Include="@(_ResolvedAndroidMavenLibraries)" />
31+
</ItemGroup>
32+
33+
</Target>
34+
35+
</Project>

src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.After.targets

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ This file is imported *after* the Microsoft.NET.Sdk/Sdk.targets.
1717
<Import Project="..\tools\Xamarin.Android.Common.targets" />
1818
<Import Project="..\tools\Xamarin.Android.Bindings.Core.targets" />
1919
<Import Project="..\tools\Xamarin.Android.Bindings.ClassParse.targets" />
20+
<Import Project="..\tools\Xamarin.Android.Bindings.Maven.targets" />
2021
<Import Project="Microsoft.Android.Sdk.AndroidLibraries.targets" />
2122
<Import Project="Microsoft.Android.Sdk.Aot.targets" Condition=" '$(AndroidApplication)' == 'true' " />
2223
<Import Project="Microsoft.Android.Sdk.Application.targets" Condition=" '$(AndroidApplication)' == 'true' " />
Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using System.Linq;
5+
using MavenNet;
6+
using MavenNet.Models;
7+
using Microsoft.Android.Build.Tasks;
8+
using Microsoft.Build.Framework;
9+
using Microsoft.Build.Utilities;
10+
using MonoDroid.Utils;
11+
12+
namespace Xamarin.Android.Tasks;
13+
14+
public class MavenDownloadTask : AndroidAsyncTask
15+
{
16+
public override string TaskPrefix => "MDT";
17+
18+
/// <summary>
19+
/// The cache directory to use for Maven artifacts.
20+
/// </summary>
21+
[Required]
22+
public string MavenCacheDirectory { get; set; } = null!; // NRT enforced by [Required]
23+
24+
/// <summary>
25+
/// The set of Maven libraries that we are being asked to acquire.
26+
/// </summary>
27+
public ITaskItem []? AndroidMavenLibraries { get; set; }
28+
29+
/// <summary>
30+
/// The set of requested Maven libraries that we were able to successfully download.
31+
/// </summary>
32+
[Output]
33+
public ITaskItem []? ResolvedAndroidMavenLibraries { get; set; }
34+
35+
public async override System.Threading.Tasks.Task RunTaskAsync ()
36+
{
37+
var resolved = new List<ITaskItem> ();
38+
39+
// Note each called function is responsible for raising any errors it encounters to the user
40+
foreach (var library in AndroidMavenLibraries.OrEmpty ()) {
41+
42+
// Validate artifact
43+
var id = library.ItemSpec;
44+
var version = library.GetRequiredMetadata ("Version", Log);
45+
46+
if (version is null)
47+
continue;
48+
49+
var artifact = MavenExtensions.ParseArtifact (id, version, Log);
50+
51+
if (artifact is null)
52+
continue;
53+
54+
// Check for local files
55+
if (GetLocalArtifactOrDefault (library, Log) is TaskItem cached_result) {
56+
library.CopyMetadataTo (cached_result);
57+
resolved.Add (cached_result);
58+
continue;
59+
}
60+
61+
// Check for repository files
62+
if (await GetRepositoryArtifactOrDefault (artifact, library, Log) is TaskItem result) {
63+
library.CopyMetadataTo (result);
64+
resolved.Add (result);
65+
continue;
66+
}
67+
}
68+
69+
ResolvedAndroidMavenLibraries = resolved.ToArray ();
70+
}
71+
72+
TaskItem? GetLocalArtifactOrDefault (ITaskItem item, TaskLoggingHelper log)
73+
{
74+
// Handles a Repository="file" entry, like:
75+
// <AndroidMavenLibrary
76+
// Include="my.company:package"
77+
// Version="1.0.0"
78+
// Repository="File"
79+
// PackageFile="C:\packages\mypackage-1.0.0.jar"
80+
// PomFile="C:\packages\mypackage-1.0.0.pom" />
81+
var type = item.GetMetadataOrDefault ("Repository", "Central");
82+
83+
if (type.Equals ("file", StringComparison.InvariantCultureIgnoreCase)) {
84+
var artifact_file = item.GetMetadataOrDefault ("PackageFile", string.Empty);
85+
var pom_file = item.GetMetadataOrDefault ("PomFile", string.Empty);
86+
87+
if (!artifact_file.HasValue () || !pom_file.HasValue ()) {
88+
log.LogError ("'PackageFile' and 'PomFile' must be specified when using a 'File' repository.");
89+
return null;
90+
}
91+
92+
if (!File.Exists (artifact_file)) {
93+
log.LogError ("Specified package file '{0}' does not exist.", artifact_file);
94+
return null;
95+
}
96+
97+
if (!File.Exists (pom_file)) {
98+
log.LogError ("Specified pom file '{0}' does not exist.", pom_file);
99+
return null;
100+
}
101+
102+
var result = new TaskItem (artifact_file);
103+
104+
result.SetMetadata ("ArtifactSpec", item.ItemSpec);
105+
result.SetMetadata ("ArtifactFile", artifact_file);
106+
result.SetMetadata ("ArtifactPom", pom_file);
107+
108+
return result;
109+
}
110+
111+
return null;
112+
}
113+
114+
async System.Threading.Tasks.Task<TaskItem?> GetRepositoryArtifactOrDefault (Artifact artifact, ITaskItem item, TaskLoggingHelper log)
115+
{
116+
// Handles a Repository="Central|Google|<url>" entry, like:
117+
// <AndroidMavenLibrary
118+
// Include="androidx.core:core"
119+
// Version="1.9.0"
120+
// Repository="Google" />
121+
// Note if Repository is not specifed, it is defaulted to "Central"
122+
123+
// Initialize repo
124+
var repository = GetRepository (item);
125+
126+
if (repository is null)
127+
return null;
128+
129+
artifact.SetRepository (repository);
130+
131+
// Download artifact
132+
var artifact_file = await MavenExtensions.DownloadPayload (artifact, MavenCacheDirectory, Log);
133+
134+
if (artifact_file is null)
135+
return null;
136+
137+
// Download POM
138+
var pom_file = await MavenExtensions.DownloadPom (artifact, MavenCacheDirectory, Log);
139+
140+
if (pom_file is null)
141+
return null;
142+
143+
var result = new TaskItem (artifact_file);
144+
145+
result.SetMetadata ("ArtifactSpec", item.ItemSpec);
146+
result.SetMetadata ("ArtifactFile", artifact_file);
147+
result.SetMetadata ("ArtifactPom", pom_file);
148+
149+
return result;
150+
}
151+
152+
async System.Threading.Tasks.Task<TaskItem?> TryGetParentPom (ITaskItem item, TaskLoggingHelper log)
153+
{
154+
var child_pom_file = item.GetRequiredMetadata ("ArtifactPom", Log);
155+
156+
// Shouldn't be possible because we just created this items
157+
if (child_pom_file is null)
158+
return null;
159+
160+
// No parent POM needed
161+
if (!(MavenExtensions.CheckForNeededParentPom (child_pom_file) is Artifact artifact))
162+
return null;
163+
164+
// Initialize repo (parent will be in same repository as child)
165+
var repository = GetRepository (item);
166+
167+
if (repository is null)
168+
return null;
169+
170+
artifact.SetRepository (repository);
171+
172+
// Download POM
173+
var pom_file = await MavenExtensions.DownloadPom (artifact, MavenCacheDirectory, Log);
174+
175+
if (pom_file is null)
176+
return null;
177+
178+
var result = new TaskItem ($"{artifact.GroupId}:{artifact.Id}");
179+
180+
result.SetMetadata ("Version", artifact.Versions.FirstOrDefault ());
181+
result.SetMetadata ("ArtifactPom", pom_file);
182+
183+
// Copy repository data
184+
item.CopyMetadataTo (result);
185+
186+
return result;
187+
}
188+
189+
MavenRepository? GetRepository (ITaskItem item)
190+
{
191+
var type = item.GetMetadataOrDefault ("Repository", "Central");
192+
193+
var repo = type.ToLowerInvariant () switch {
194+
"central" => MavenRepository.FromMavenCentral (),
195+
"google" => MavenRepository.FromGoogle (),
196+
_ => (MavenRepository?) null
197+
};
198+
199+
if (repo is null && type.StartsWith ("http", StringComparison.OrdinalIgnoreCase))
200+
repo = MavenRepository.FromUrl (type);
201+
202+
if (repo is null)
203+
Log.LogError ("Unknown Maven repository: '{0}'.", type);
204+
205+
return repo;
206+
}
207+
}

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

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.Linq;
34
using System.Xml.Linq;
45
using Microsoft.Build.Framework;
6+
using Microsoft.Build.Utilities;
57

68
namespace Xamarin.Android.Tasks
79
{
@@ -19,5 +21,30 @@ public static IEnumerable<XElement> ToXElements (this ICollection<ITaskItem> ite
1921
yield return e;
2022
}
2123
}
24+
25+
public static string GetMetadataOrDefault (this ITaskItem item, string name, string defaultValue)
26+
{
27+
var value = item.GetMetadata (name);
28+
29+
if (string.IsNullOrWhiteSpace (value))
30+
return defaultValue;
31+
32+
return value;
33+
}
34+
35+
public static string? GetRequiredMetadata (this ITaskItem item, string name, TaskLoggingHelper log)
36+
{
37+
var value = item.GetMetadata (name);
38+
39+
if (string.IsNullOrWhiteSpace (value)) {
40+
log.LogError ("Item is missing required metadata '{0}'", name);
41+
return null;
42+
}
43+
44+
return value;
45+
}
46+
47+
public static bool HasMetadata (this ITaskItem item, string name)
48+
=> item.MetadataNames.OfType<string> ().Contains (name, StringComparer.OrdinalIgnoreCase);
2249
}
2350
}

0 commit comments

Comments
 (0)