From 6b87b0c5ec53a1a6b9ad25167d7b0225e31f9dfe Mon Sep 17 00:00:00 2001 From: Jonathan Pobst Date: Mon, 15 Jul 2024 13:11:33 -0500 Subject: [PATCH] [XABT] Allow deployment artifacts to specify mutiple JavaDependencyVerification JavaArtifact libraries. --- .../Properties/Resources.Designer.cs | 9 ++ .../Properties/Resources.resx | 5 + .../Tasks/JavaDependencyVerification.cs | 96 +++++++++---------- .../Tasks/MavenDownload.cs | 6 +- .../BindingBuildTest.cs | 14 +-- .../Tasks/JavaDependencyVerificationTests.cs | 26 ++--- .../Tasks/MavenDownloadTests.cs | 6 +- .../Utilities/MavenExtensions.cs | 75 ++++++++++----- 8 files changed, 137 insertions(+), 100 deletions(-) diff --git a/src/Xamarin.Android.Build.Tasks/Properties/Resources.Designer.cs b/src/Xamarin.Android.Build.Tasks/Properties/Resources.Designer.cs index 435a1deedc8..1a07b4ffa27 100644 --- a/src/Xamarin.Android.Build.Tasks/Properties/Resources.Designer.cs +++ b/src/Xamarin.Android.Build.Tasks/Properties/Resources.Designer.cs @@ -1264,6 +1264,15 @@ public static string XA4248 { } } + /// + /// Looks up a localized string similar to Maven artifact specification '{0}' is invalid. The correct format is 'group_id:artifact_id:version'.. + /// + public static string XA4249 { + get { + return ResourceManager.GetString("XA4249", resourceCulture); + } + } + /// /// Looks up a localized string similar to Native library '{0}' will not be bundled because it has an unsupported ABI. Move this file to a directory with a valid Android ABI name such as 'libs/armeabi-v7a/'.. /// diff --git a/src/Xamarin.Android.Build.Tasks/Properties/Resources.resx b/src/Xamarin.Android.Build.Tasks/Properties/Resources.resx index ca26aa0bbec..188a879e3c6 100644 --- a/src/Xamarin.Android.Build.Tasks/Properties/Resources.resx +++ b/src/Xamarin.Android.Build.Tasks/Properties/Resources.resx @@ -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 + + Maven artifact specification '{0}' is invalid. The correct format is 'group_id:artifact_id:version'. + The following are literal names and should not be translated: Maven, group_id, artifact_id +{0} - A Maven artifact specification + \ No newline at end of file diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/JavaDependencyVerification.cs b/src/Xamarin.Android.Build.Tasks/Tasks/JavaDependencyVerification.cs index 441e1b3ccd5..3c5d751b665 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/JavaDependencyVerification.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/JavaDependencyVerification.cs @@ -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); + } } } } @@ -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); @@ -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; @@ -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); + } } } } @@ -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); @@ -349,7 +348,6 @@ public class Package public class NuGetPackageVersionFinder { readonly LockFile lock_file; - readonly Dictionary cache = new Dictionary (); readonly Regex tag = new Regex ("artifact_versioned=(?.+)?:(?.+?):(?.+)\\s?", RegexOptions.Compiled); readonly Regex tag2 = new Regex ("artifact=(?.+)?:(?.+?):(?.+)\\s?", RegexOptions.Compiled); @@ -370,59 +368,59 @@ public class NuGetPackageVersionFinder } } - public Artifact? GetJavaInformation (string library, string version, TaskLoggingHelper log) + public List 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 (); // 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 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 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); + } } } diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/MavenDownload.cs b/src/Xamarin.Android.Build.Tasks/Tasks/MavenDownload.cs index 3ac8d2383c1..81010e803bc 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/MavenDownload.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/MavenDownload.cs @@ -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) @@ -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); diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BindingBuildTest.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BindingBuildTest.cs index df1fb580f45..14e72a467b4 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BindingBuildTest.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BindingBuildTest.cs @@ -888,14 +888,9 @@ public void AndroidMavenLibrary_AllDependenciesAreVerified () var collection = new XamarinAndroidBindingProject (); // Dependencies ignored by - 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 }, @@ -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 (); diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/JavaDependencyVerificationTests.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/JavaDependencyVerificationTests.cs index f854c422b7e..03c2124022d 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/JavaDependencyVerificationTests.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/JavaDependencyVerificationTests.cs @@ -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, }; @@ -177,6 +177,7 @@ 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, []); @@ -184,7 +185,8 @@ public void DependencyFulfilledByAndroidLibrary () 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"), ], }; @@ -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, []); @@ -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"), ], }; @@ -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, []); @@ -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"), ], }; @@ -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, []); @@ -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"), ], }; @@ -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"), ], }; @@ -312,7 +320,7 @@ 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); @@ -320,10 +328,6 @@ TaskItem CreateAndroidLibraryTaskItem (string name, string? manifest = null, str 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; } diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/MavenDownloadTests.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/MavenDownloadTests.cs index c2ad0117fe4..749dadd35d9 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/MavenDownloadTests.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/MavenDownloadTests.cs @@ -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); @@ -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); diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/MavenExtensions.cs b/src/Xamarin.Android.Build.Tasks/Utilities/MavenExtensions.cs index a9b881adec6..32ac70508cb 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/MavenExtensions.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/MavenExtensions.cs @@ -1,6 +1,7 @@ #nullable enable using System; +using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; @@ -17,6 +18,7 @@ namespace Xamarin.Android.Tasks; static class MavenExtensions { static readonly char [] separator = [':']; + static readonly char [] artifacts_separators = [';', ',', '\r', '\n']; /// /// Shortcut for !string.IsNullOrWhiteSpace (s) @@ -54,47 +56,76 @@ public static bool TryParseArtifactWithVersion (string id, string version, TaskL return true; } - public static bool TryParseJavaArtifactAndJavaVersion (this ITaskItem task, string type, TaskLoggingHelper log, [NotNullWhen (true)] out Artifact? artifact, out bool attributesSpecified) + public static bool TryParseArtifacts (string id, TaskLoggingHelper log, out List artifacts) { - artifact = null; - var item_name = task.ItemSpec; + artifacts = new List (); + var result = true; - // Convert "../../src/blah/Blah.csproj" to "Blah.csproj" - if (type == "ProjectReference") - item_name = Path.GetFileName (item_name); + var arts = id.Split (artifacts_separators, StringSplitOptions.RemoveEmptyEntries); - var has_artifact = task.HasMetadata ("JavaArtifact"); - var has_version = task.HasMetadata ("JavaVersion"); + foreach (var art in arts) { + + if (Artifact.TryParse (art, out var a)) { + artifacts.Add (a); + continue; + } + + log.LogCodedError ("XA4249", Properties.Resources.XA4249, art); + result = false; + } - // Lets callers know if user attempted to specify JavaArtifact or JavaVersion, even if they did it incorrectly - attributesSpecified = has_artifact || has_version; + return result; + } - if (has_artifact && !has_version) { - log.LogCodedError ("XA4243", Properties.Resources.XA4243, "JavaVersion", "JavaArtifact", type, item_name); + public static bool TryParseJavaArtifact (this ITaskItem task, string type, TaskLoggingHelper log, [NotNullWhen (true)]out Artifact? artifact, out bool attributesSpecified) + { + var result = TryParseJavaArtifacts (task, type, log, out var artifacts, out attributesSpecified); + + if (!result) { + artifact = null; return false; } - if (!has_artifact && has_version) { - log.LogCodedError ("XA4243", Properties.Resources.XA4243, "JavaArtifact", "JavaVersion", type, item_name); + // TODO: Need a new message saying that only one JavaArtifact is allowed + if (artifacts.Count > 1) { + log.LogCodedError ("XA4243", Properties.Resources.XA4243, "JavaArtifact", type, task.ItemSpec); + artifact = null; return false; } - if (has_artifact && has_version) { + artifact = artifacts.FirstOrDefault (); + + return artifact is not null; + } + + public static bool TryParseJavaArtifacts (this ITaskItem task, string type, TaskLoggingHelper log, out List artifacts, out bool attributesSpecified) + { + artifacts = new List (); + var item_name = task.ItemSpec; + + // Convert "../../src/blah/Blah.csproj" to "Blah.csproj" + if (type == "ProjectReference") + item_name = Path.GetFileName (item_name); + + var has_artifact = task.HasMetadata ("JavaArtifact"); + + // Lets callers know if user attempted to specify JavaArtifact, even if they did it incorrectly + attributesSpecified = has_artifact; + + if (has_artifact) { var id = task.GetMetadata ("JavaArtifact"); - var version = task.GetMetadata ("JavaVersion"); if (string.IsNullOrWhiteSpace (id)) { log.LogCodedError ("XA4244", Properties.Resources.XA4244, "JavaArtifact", type, item_name); return false; } - if (string.IsNullOrWhiteSpace (version)) { - log.LogCodedError ("XA4244", Properties.Resources.XA4244, "JavaVersion", type, item_name); - return false; - } + if (TryParseArtifacts (id, log, out var parsed)) { + foreach (var art in parsed) { + log.LogMessage ("Found Java dependency '{0}:{1}' version '{2}' from {3} '{4}' (JavaArtifact)", art.GroupId, art.Id, art.Version, type, item_name); + artifacts.Add (art); + } - if (TryParseArtifactWithVersion (id, version, log, out artifact)) { - log.LogMessage ("Found Java dependency '{0}:{1}' version '{2}' from {3} '{4}' (JavaArtifact)", artifact.GroupId, artifact.Id, artifact.Version, type, item_name); return true; } }