Skip to content

Commit

Permalink
[XABT] Allow deployment artifacts to specify mutiple JavaDependencyVe…
Browse files Browse the repository at this point in the history
…rification JavaArtifact libraries.
  • Loading branch information
jpobst committed Jul 16, 2024
1 parent 000759c commit 6b87b0c
Show file tree
Hide file tree
Showing 8 changed files with 137 additions and 100 deletions.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Xamarin.Android.Build.Tasks/Properties/Resources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -1059,4 +1059,9 @@ To use a custom JDK path for a command line build, set the 'JavaSdkDirectory' MS
{0} - NuGet package id
{1} - NuGet package version</comment>
</data>
<data name="XA4249" xml:space="preserve">
<value>Maven artifact specification '{0}' is invalid. The correct format is 'group_id:artifact_id:version'.</value>
<comment>The following are literal names and should not be translated: Maven, group_id, artifact_id
{0} - A Maven artifact specification</comment>
</data>
</root>
96 changes: 47 additions & 49 deletions src/Xamarin.Android.Build.Tasks/Tasks/JavaDependencyVerification.cs
Original file line number Diff line number Diff line change
Expand Up @@ -149,15 +149,12 @@ public void AddAndroidLibraries (ITaskItem []? tasks)
{
foreach (var task in tasks.OrEmpty ()) {
var id = task.GetMetadataOrDefault ("JavaArtifact", string.Empty);
var version = task.GetMetadataOrDefault ("JavaVersion", string.Empty);

// TODO: Should raise an error if JavaArtifact is specified but JavaVersion is not
if (!id.HasValue () || !version.HasValue ())
continue;

if (version != null && MavenExtensions.TryParseArtifactWithVersion (id, version, log, out var art)) {
log.LogMessage ("Found Java dependency '{0}:{1}' version '{2}' from AndroidLibrary '{3}'", art.GroupId, art.Id, art.Version, task.ItemSpec);
artifacts.Add (art.ArtifactString, art);
if (MavenExtensions.TryParseArtifacts (id, log, out var parsed)) {
foreach (var art in parsed) {
log.LogMessage ("Found Java dependency '{0}:{1}' version '{2}' from AndroidLibrary '{3}'", art.GroupId, art.Id, art.Version, task.ItemSpec);
artifacts.Add (art.ArtifactString, art);
}
}
}
}
Expand All @@ -166,20 +163,22 @@ public void AddPackageReferences (ITaskItem []? tasks)
{
foreach (var task in tasks.OrEmpty ()) {

// See if JavaArtifact/JavaVersion overrides were used
if (task.TryParseJavaArtifactAndJavaVersion ("PackageReference", log, out var explicit_artifact, out var attributes_specified)) {
artifacts.Add (explicit_artifact.ArtifactString, explicit_artifact);
// See if JavaArtifact override was used
if (task.TryParseJavaArtifacts ("PackageReference", log, out var explicit_artifacts, out var attributes_specified)) {
foreach (var explicit_artifact in explicit_artifacts)
artifacts.Add (explicit_artifact.ArtifactString, explicit_artifact);

continue;
}

// If user tried to specify JavaArtifact or JavaVersion, but did it incorrectly, we do not perform any fallback
// If user tried to specify JavaArtifact, but did it incorrectly, we do not perform any fallback
if (attributes_specified)
continue;

// Try parsing the NuGet metadata for Java version information instead
var artifact = finder?.GetJavaInformation (task.ItemSpec, task.GetMetadataOrDefault ("Version", string.Empty), log);
var metadata_artifacts = finder?.GetArtifactsInNugetPackage (task.ItemSpec, task.GetMetadataOrDefault ("Version", string.Empty), log);

if (artifact != null) {
foreach (var artifact in metadata_artifacts ?? []) {
log.LogMessage ("Found Java dependency '{0}:{1}' version '{2}' from PackageReference '{3}'", artifact.GroupId, artifact.Id, artifact.Version, task.ItemSpec);
artifacts.Add (artifact.ArtifactString, artifact);

Expand All @@ -193,13 +192,15 @@ public void AddPackageReferences (ITaskItem []? tasks)
public void AddProjectReferences (ITaskItem []? tasks)
{
foreach (var task in tasks.OrEmpty ()) {
// See if JavaArtifact/JavaVersion overrides were used
if (task.TryParseJavaArtifactAndJavaVersion ("ProjectReference", log, out var explicit_artifact, out var attributes_specified)) {
artifacts.Add (explicit_artifact.ArtifactString, explicit_artifact);
// See if JavaArtifact override was used
if (task.TryParseJavaArtifacts ("ProjectReference", log, out var explicit_artifacts, out var attributes_specified)) {
foreach (var explicit_artifact in explicit_artifacts)
artifacts.Add (explicit_artifact.ArtifactString, explicit_artifact);

continue;
}

// If user tried to specify JavaArtifact or JavaVersion, but did it incorrectly, we do not perform any fallback
// If user tried to specify JavaArtifact, but did it incorrectly, we do not perform any fallback
if (attributes_specified)
continue;

Expand All @@ -212,14 +213,12 @@ public void AddIgnoredDependencies (ITaskItem []? tasks)
{
foreach (var task in tasks.OrEmpty ()) {
var id = task.ItemSpec;
var version = task.GetRequiredMetadata ("AndroidIgnoredJavaDependency", "Version", log);

if (version is null)
continue;

if (version != null && MavenExtensions.TryParseArtifactWithVersion (id, version, log, out var art)) {
log.LogMessage ("Ignoring Java dependency '{0}:{1}' version '{2}'", art.GroupId, art.Id, art.Version);
artifacts.Add (art.ArtifactString, art);
if (MavenExtensions.TryParseArtifacts (id, log, out var parsed)) {
foreach (var art in parsed) {
log.LogMessage ("Ignoring Java dependency '{0}:{1}' version '{2}'", art.GroupId, art.Id, art.Version);
artifacts.Add (art.ArtifactString, art);
}
}
}
}
Expand Down Expand Up @@ -263,7 +262,7 @@ public MSBuildLoggingPomResolver (TaskLoggingHelper logger)

Artifact? RegisterFromTaskItem (ITaskItem item, string itemName, string filename)
{
item.TryParseJavaArtifactAndJavaVersion (itemName, logger, out var artifact, out var _);
item.TryParseJavaArtifact (itemName, logger, out var artifact, out var _);

if (!File.Exists (filename)) {
logger.LogCodedError ("XA4245", Properties.Resources.XA4245, filename);
Expand Down Expand Up @@ -349,7 +348,6 @@ public class Package
public class NuGetPackageVersionFinder
{
readonly LockFile lock_file;
readonly Dictionary<string, Artifact> cache = new Dictionary<string, Artifact> ();
readonly Regex tag = new Regex ("artifact_versioned=(?<GroupId>.+)?:(?<ArtifactId>.+?):(?<Version>.+)\\s?", RegexOptions.Compiled);
readonly Regex tag2 = new Regex ("artifact=(?<GroupId>.+)?:(?<ArtifactId>.+?):(?<Version>.+)\\s?", RegexOptions.Compiled);

Expand All @@ -370,59 +368,59 @@ public class NuGetPackageVersionFinder
}
}

public Artifact? GetJavaInformation (string library, string version, TaskLoggingHelper log)
public List<Artifact> GetArtifactsInNugetPackage (string library, string version, TaskLoggingHelper log)
{
// Check if we already have this one in the cache
var dictionary_key = $"{library.ToLowerInvariant ()}:{version}";

if (cache.TryGetValue (dictionary_key, out var artifact))
return artifact;
var artifacts = new List<Artifact> ();

// Find the LockFileLibrary
var nuget = lock_file.GetLibrary (library, new NuGet.Versioning.NuGetVersion (version));

if (nuget is null) {
log.LogCodedError ("XA4248", Properties.Resources.XA4248, library, version);
return null;
return artifacts;
}

foreach (var path in lock_file.PackageFolders)
if (CheckFilePath (path.Path, nuget) is Artifact art) {
cache.Add (dictionary_key, art);
return art;
}
AddArtifactsFromNuspec (artifacts, path.Path, nuget);

return null;
return artifacts;
}

Artifact? CheckFilePath (string nugetPackagePath, LockFileLibrary package)
void AddArtifactsFromNuspec (List<Artifact> artifacts, string nugetPackagePath, LockFileLibrary package)
{
// Check NuGet tags
var nuspec = package.Files.FirstOrDefault (f => f.EndsWith (".nuspec", StringComparison.OrdinalIgnoreCase));

if (nuspec is null)
return null;
return;

nuspec = Path.Combine (nugetPackagePath, package.Path, nuspec);

if (!File.Exists (nuspec))
return null;
return;

var reader = new NuGet.Packaging.NuspecReader (nuspec);
var tags = reader.GetTags ();

// Try the first tag format
var match = tag.Match (tags);
AddMatchesToCollection (artifacts, tag.Matches (tags));

// Try the second tag format
if (!match.Success)
match = tag2.Match (tags);

if (!match.Success)
return null;
AddMatchesToCollection (artifacts, tag2.Matches (tags));

// TODO: Define a well-known file that can be included in the package like "java-package.txt"
}

return new Artifact (match.Groups ["GroupId"].Value, match.Groups ["ArtifactId"].Value, match.Groups ["Version"].Value);
void AddMatchesToCollection (List<Artifact> list, MatchCollection? matches)
{
if (matches is null || matches.Count == 0)
return;

foreach (Match match in matches) {
var artifact = new Artifact (match.Groups ["GroupId"].Value, match.Groups ["ArtifactId"].Value, match.Groups ["Version"].Value);

if (!list.Any (a => a.VersionedArtifactString == artifact.VersionedArtifactString))
list.Add (artifact);
}
}
}
6 changes: 2 additions & 4 deletions src/Xamarin.Android.Build.Tasks/Tasks/MavenDownload.cs
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,7 @@ public async override System.Threading.Tasks.Task RunTaskAsync ()

var result = new TaskItem (artifact_file);

result.SetMetadata ("JavaArtifact", $"{artifact.GroupId}:{artifact.Id}");
result.SetMetadata ("JavaVersion", artifact.Version);
result.SetMetadata ("JavaArtifact", artifact.VersionedArtifactString);

// Allow user to opt out of dependency verification
if (string.Compare (item.GetMetadataOrDefault ("VerifyDependencies", "true"), "false", true) == 0)
Expand All @@ -121,8 +120,7 @@ public async override System.Threading.Tasks.Task RunTaskAsync ()
var pom_item = new TaskItem (kv.Value);
var pom_artifact = Artifact.Parse (kv.Key);

pom_item.SetMetadata ("JavaArtifact", $"{pom_artifact.GroupId}:{pom_artifact.Id}");
pom_item.SetMetadata ("JavaVersion", pom_artifact.Version);
pom_item.SetMetadata ("JavaArtifact", pom_artifact.VersionedArtifactString);

additionalPoms.Add (pom_item);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -888,14 +888,9 @@ public void AndroidMavenLibrary_AllDependenciesAreVerified ()
var collection = new XamarinAndroidBindingProject ();

// Dependencies ignored by <AndroidIgnoredJavaDependency>
var concurrent = new BuildItem ("AndroidIgnoredJavaDependency", "androidx.concurrent:concurrent-futures");
concurrent.Metadata.Add ("Version", "1.1.0");

var lifecycle = new BuildItem ("AndroidIgnoredJavaDependency", "androidx.lifecycle:lifecycle-runtime");
lifecycle.Metadata.Add ("Version", "2.6.2");

var parcelable = new BuildItem ("AndroidIgnoredJavaDependency", "androidx.versionedparcelable:versionedparcelable");
parcelable.Metadata.Add ("Version", "1.2.0");
var concurrent = new BuildItem ("AndroidIgnoredJavaDependency", "androidx.concurrent:concurrent-futures:1.1.0");
var lifecycle = new BuildItem ("AndroidIgnoredJavaDependency", "androidx.lifecycle:lifecycle-runtime:2.6.2");
var parcelable = new BuildItem ("AndroidIgnoredJavaDependency", "androidx.versionedparcelable:versionedparcelable:1.2.0");

var proj = new XamarinAndroidBindingProject {
Jars = { item, annotations_experimental_androidlib },
Expand All @@ -905,8 +900,7 @@ public void AndroidMavenLibrary_AllDependenciesAreVerified ()

proj.AddReference (collection);
var collection_proj = proj.References.First ();
collection_proj.Metadata.Add ("JavaArtifact", "androidx.collection:collection");
collection_proj.Metadata.Add ("JavaVersion", "1.3.0");
collection_proj.Metadata.Add ("JavaArtifact", "androidx.collection:collection:1.3.0");

using var a = CreateDllBuilder ();
using var b = CreateDllBuilder ();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ public void MalformedMicrosoftPackagesJson ()
BuildEngine = engine,
AndroidLibraries = [
CreateAndroidLibraryTaskItem ("com.google.android.material.jar", pom.FilePath),
CreateAndroidLibraryTaskItem ("com.google.android.material-core.jar", null, "com.google.android:material-core", "1.0"),
CreateAndroidLibraryTaskItem ("com.google.android.material-core.jar", null, "com.google.android:material-core:1.0"),
],
MicrosoftPackagesFile = package_finder.FilePath,
};
Expand All @@ -177,14 +177,16 @@ public void DependencyFulfilledByAndroidLibrary ()
{
using var pom = new PomBuilder ("com.google.android", "material", "1.0")
.WithDependency ("com.google.android", "material-core", "1.0")
.WithDependency ("com.google.android", "material-foo", "1.0")
.BuildTemporary ();

var engine = new MockBuildEngine (TestContext.Out, []);
var task = new JavaDependencyVerification {
BuildEngine = engine,
AndroidLibraries = [
CreateAndroidLibraryTaskItem ("com.google.android.material.jar", pom.FilePath),
CreateAndroidLibraryTaskItem ("com.google.android.material-core.jar", null, "com.google.android:material-core", "1.0"),
CreateAndroidLibraryTaskItem ("com.google.android.material-core.jar", null, "com.google.android:material-core:1.0"),
CreateAndroidLibraryTaskItem ("com.google.android.material-foo.jar", null, "org.jetbrains.kotlin:kotlin-stdlib:2.0.0,com.google.android:material-foo:1.0"),
],
};

Expand All @@ -199,6 +201,7 @@ public void DependencyFulfilledByProjectReferenceExplicitMetadata ()
{
using var pom = new PomBuilder ("com.google.android", "material", "1.0")
.WithDependency ("com.google.android", "material-core", "1.0")
.WithDependency ("com.google.android", "material-foo", "1.0")
.BuildTemporary ();

var engine = new MockBuildEngine (TestContext.Out, []);
Expand All @@ -208,7 +211,8 @@ public void DependencyFulfilledByProjectReferenceExplicitMetadata ()
CreateAndroidLibraryTaskItem ("com.google.android.material.jar", pom.FilePath),
],
ProjectReferences = [
CreateAndroidLibraryTaskItem ("Google.Material.Core.csproj", null, "com.google.android:material-core", "1.0"),
CreateAndroidLibraryTaskItem ("Google.Material.Core.csproj", null, "com.google.android:material-core:1.0"),
CreateAndroidLibraryTaskItem ("Google.Material.Foo.csproj", null, "org.jetbrains.kotlin:kotlin-stdlib:2.0.0,com.google.android:material-foo:1.0"),
],
};

Expand All @@ -223,6 +227,7 @@ public void DependencyFulfilledByPackageReferenceExplicitMetadata ()
{
using var pom = new PomBuilder ("com.google.android", "material", "1.0")
.WithDependency ("com.google.android", "material-core", "1.0")
.WithDependency ("com.google.android", "material-foo", "1.0")
.BuildTemporary ();

var engine = new MockBuildEngine (TestContext.Out, []);
Expand All @@ -232,7 +237,8 @@ public void DependencyFulfilledByPackageReferenceExplicitMetadata ()
CreateAndroidLibraryTaskItem ("com.google.android.material.jar", pom.FilePath),
],
PackageReferences = [
CreateAndroidLibraryTaskItem ("Xamarin.Google.Material.Core", null, "com.google.android:material-core", "1.0"),
CreateAndroidLibraryTaskItem ("Xamarin.Google.Material.Core", null, "com.google.android:material-core:1.0"),
CreateAndroidLibraryTaskItem ("Xamarin.Google.Material.Foo", null, "org.jetbrains.kotlin:kotlin-stdlib:2.0.0,com.google.android:material-foo:1.0"),
],
};

Expand All @@ -247,6 +253,7 @@ public void DependencyIgnored ()
{
using var pom = new PomBuilder ("com.google.android", "material", "1.0")
.WithDependency ("com.google.android", "material-core", "1.0")
.WithDependency ("com.google.android", "material-foo", "1.0")
.BuildTemporary ();

var engine = new MockBuildEngine (TestContext.Out, []);
Expand All @@ -256,7 +263,8 @@ public void DependencyIgnored ()
CreateAndroidLibraryTaskItem ("com.google.android.material.jar", pom.FilePath),
],
IgnoredDependencies = [
CreateAndroidLibraryTaskItem ("com.google.android:material-core", rawVersion: "1.0"),
CreateAndroidLibraryTaskItem ("com.google.android:material-core:1.0"),
CreateAndroidLibraryTaskItem ("org.jetbrains.kotlin:kotlin-stdlib:2.0.0,com.google.android:material-foo:1.0"),
],
};

Expand All @@ -279,7 +287,7 @@ public void DependencyWithoutVersionFulfilled ()
BuildEngine = engine,
AndroidLibraries = [
CreateAndroidLibraryTaskItem ("com.google.android.material.jar", pom.FilePath),
CreateAndroidLibraryTaskItem ("com.google.android.material-core.jar", null, "com.google.android:material-core", "1.0"),
CreateAndroidLibraryTaskItem ("com.google.android.material-core.jar", null, "com.google.android:material-core:1.0"),
],
};

Expand Down Expand Up @@ -312,18 +320,14 @@ public void DependencyWithoutVersionNotFulfilled ()
Assert.AreEqual ("Java dependency 'com.google.android:material-core' is not satisfied.", engine.Errors [0].Message);
}

TaskItem CreateAndroidLibraryTaskItem (string name, string? manifest = null, string? javaArtifact = null, string? javaVersion = null, string? rawVersion = null)
TaskItem CreateAndroidLibraryTaskItem (string name, string? manifest = null, string? javaArtifact = null)
{
var item = new TaskItem (name);

if (manifest is not null)
item.SetMetadata ("Manifest", manifest);
if (javaArtifact is not null)
item.SetMetadata ("JavaArtifact", javaArtifact);
if (javaVersion is not null)
item.SetMetadata ("JavaVersion", javaVersion);
if (rawVersion is not null)
item.SetMetadata ("Version", rawVersion);

return item;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,7 @@ public async Task MavenCentralSuccess ()

var output_item = task.ResolvedAndroidMavenLibraries! [0];

Assert.AreEqual ("com.google.auto.value:auto-value-annotations", output_item.GetMetadata ("JavaArtifact"));
Assert.AreEqual ("1.10.4", output_item.GetMetadata ("JavaVersion"));
Assert.AreEqual ("com.google.auto.value:auto-value-annotations:1.10.4", output_item.GetMetadata ("JavaArtifact"));
Assert.AreEqual (Path.Combine (temp_cache_dir, "central", "com.google.auto.value", "auto-value-annotations", "1.10.4", "auto-value-annotations-1.10.4.pom"), output_item.GetMetadata ("Manifest"));
} finally {
DeleteTempDirectory (temp_cache_dir);
Expand All @@ -164,8 +163,7 @@ public async Task MavenGoogleSuccess ()

var output_item = task.ResolvedAndroidMavenLibraries! [0];

Assert.AreEqual ("androidx.core:core", output_item.GetMetadata ("JavaArtifact"));
Assert.AreEqual ("1.12.0", output_item.GetMetadata ("JavaVersion"));
Assert.AreEqual ("androidx.core:core:1.12.0", output_item.GetMetadata ("JavaArtifact"));
Assert.AreEqual (Path.Combine (temp_cache_dir, "google", "androidx.core", "core", "1.12.0", "core-1.12.0.pom"), output_item.GetMetadata ("Manifest"));
} finally {
DeleteTempDirectory (temp_cache_dir);
Expand Down
Loading

0 comments on commit 6b87b0c

Please sign in to comment.