Skip to content

Commit

Permalink
Begin error documentation.
Browse files Browse the repository at this point in the history
  • Loading branch information
jpobst committed Feb 15, 2024
1 parent b9ed6d0 commit 5f21105
Show file tree
Hide file tree
Showing 8 changed files with 186 additions and 108 deletions.
4 changes: 2 additions & 2 deletions Documentation/guides/ResolvingJavaDependencies.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,15 +64,15 @@ This can be done by adding additional `<AndroidLibrary>` items to the project:

```xml
<ItemGroup>
<AndroidLibrary JavaArtifact="my.library:dependency-library" JavaVersion="1.0.0" />
<AndroidLibrary Include="mydependency.jar" JavaArtifact="my.library:dependency-library" JavaVersion="1.0.0" />
</ItemGroup>
```

To include the Java library but not produce C# bindings for it, mark it with `Bind="false"`:

```xml
<ItemGroup>
<AndroidLibrary JavaArtifact="my.library:dependency-library" JavaVersion="1.0.0" Bind="false" />
<AndroidLibrary Include="mydependency.jar" JavaArtifact="my.library:dependency-library" JavaVersion="1.0.0" Bind="false" />
</ItemGroup>
```

Expand Down
81 changes: 81 additions & 0 deletions src/Xamarin.Android.Build.Tasks/Properties/Resources.Designer.cs

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

50 changes: 50 additions & 0 deletions src/Xamarin.Android.Build.Tasks/Properties/Resources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -1008,4 +1008,54 @@ To use a custom JDK path for a command line build, set the 'JavaSdkDirectory' MS
<value>The Android Support libraries are not supported in .NET 9 and later, please migrate to AndroidX. See https://aka.ms/xamarin/androidx for more details.</value>
<comment>The following are literal names and should not be translated: Android Support, AndroidX, .NET.</comment>
</data>
<data name="XA4240" xml:space="preserve">
<value>Could not verify Java dependencies for artifact '{0}' due to missing POM file(s). See other error(s) for details.</value>
<comment>The following are literal names and should not be translated: Java, POM.
{0} - Maven artifact id</comment>
</data>
<data name="XA4241" xml:space="preserve">
<value>Java dependency '{0}' is not satisfied.</value>
<comment>The following are literal names and should not be translated: Java.
{0} - Maven dependency id</comment>
</data>
<data name="XA4242" xml:space="preserve">
<value>Java dependency '{0}' is not satisfied. Microsoft maintains the NuGet package '{1}' that could fulfill this dependency.</value>
<comment>The following are literal names and should not be translated: Java, Microsoft, NuGet.
{0} - Maven dependency id
{1} - NuGet package id</comment>
</data>
<data name="XA4243" xml:space="preserve">
<value>Attribute '{0}' is required when using '{1}' for '{2}' item '{3}'.</value>
<comment>{0}, {1} - MSBuild XML attribute
{2} - MSBuild XML element name
{3} - MSBuild ItemSpec value</comment>
</data>
<data name="XA4244" xml:space="preserve">
<value>Attribute '{0}' cannot be empty for '{1}' item '{2}'.</value>
<comment>{0} - MSBuild XML attribute
{1} - MSBuild XML element name
{2} - MSBuild ItemSpec value</comment>
</data>
<data name="XA4245" xml:space="preserve">
<value>Specified POM file '{0}' does not exist.</value>
<comment>The following are literal names and should not be translated: POM.
{0} - File path to POM file</comment>
</data>
<data name="XA4246" xml:space="preserve">
<value>Could not parse POM file '{0}': '{1}'.</value>
<comment>The following are literal names and should not be translated: POM.
{0} - File path to POM file
{1} - Exception message</comment>
</data>
<data name="XA4247" xml:space="preserve">
<value>Could not resolve POM file for artifact '{0}'.</value>
<comment>The following are literal names and should not be translated: POM.
{0} - Java artifact id</comment>
</data>
<data name="XA4248" xml:space="preserve">
<value>Could not find NuGet package '{0}' version '{1}' in lock file. Ensure NuGet Restore has run since this &lt;PackageReference&gt; was added.</value>
<comment>The following are literal names and should not be translated: NuGet, PackageReference.
{0} - NuGet package id
{1} - NuGet package version</comment>
</data>
</root>
115 changes: 31 additions & 84 deletions src/Xamarin.Android.Build.Tasks/Tasks/JavaDependencyVerification.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,6 @@ public override bool RunTask ()
if (pom_resolver.RegisterFromAndroidLibrary (pom) is Artifact art)
poms_to_verify.Add (art);

//foreach (var pom in AndroidLibraries.Select (al => al.GetMetadata ("Manifest")))
// if (pom.HasValue () && pom_resolver.Register (pom) is Artifact art)
// poms_to_verify.Add (art);

foreach (var pom in AdditionalManifests ?? [])
pom_resolver.RegisterFromAndroidAdditionalJavaManifest (pom);

Expand All @@ -94,7 +90,7 @@ public override bool RunTask ()
foreach (var dependency in resolved_pom.Dependencies.Where (d => (d.IsRuntimeDependency () || d.IsCompileDependency ()) && !d.IsOptional ()))
resolver.EnsureDependencySatisfied (dependency, ms_packages);
} else {
Log.LogError ("Could not verify Java dependencies for artifact '{0}' due to missing POM file(s). See other error(s) for details.", pom);
Log.LogCodedError ("XA4240", Properties.Resources.XA4240, pom);
}
}

Expand All @@ -117,7 +113,7 @@ static bool TryResolveProject (Artifact artifact, IPomResolver resolver, [NotNul

class DependencyResolver
{
List<Artifact> artifacts = new List<Artifact> ();
readonly List<Artifact> artifacts = new List<Artifact> ();

readonly NuGetPackageVersionFinder? finder;
readonly TaskLoggingHelper log;
Expand All @@ -133,30 +129,20 @@ public DependencyResolver (string? lockFile, TaskLoggingHelper log)
public bool EnsureDependencySatisfied (ResolvedDependency dependency, MicrosoftNuGetPackageFinder packages)
{
if (!dependency.Version.HasValue ())
log.LogWarning ("Could not determine required version of Java dependency '{0}:{1}'. Validation of this dependency will not take version into account.", dependency.GroupId, dependency.ArtifactId);
log.LogMessage ("Could not determine required version of Java dependency '{0}:{1}'. Validation of this dependency will not take version into account.", dependency.GroupId, dependency.ArtifactId);

var satisfied = TrySatisfyDependency (dependency);

if (satisfied)
return true;

var suggestion = packages.GetNuGetPackage ($"{dependency.GroupId}:{dependency.ArtifactId}");
var artifact_spec = dependency.ToArtifactString (dependency.Version.HasValue ());

// Message if we couldn't determine the required version
if (!dependency.Version.HasValue ()) {
if (suggestion is string nuget)
log.LogError ("Java dependency '{0}:{1}' is not satisfied. Microsoft maintains the NuGet package '{2}' that could fulfill this dependency.", dependency.GroupId, dependency.ArtifactId, nuget);
else
log.LogError ("Java dependency '{0}:{1}' is not satisfied.", dependency.GroupId, dependency.ArtifactId);

return false;
}

// Message if we could determine the required version
if (suggestion is string nuget2)
log.LogError ("Java dependency '{0}:{1}' version '{2}' is not satisfied. Microsoft maintains the NuGet package '{3}' that could fulfill this dependency.", dependency.GroupId, dependency.ArtifactId, dependency.Version, nuget2);
if (suggestion is string nuget)
log.LogCodedError ("XA4242", Properties.Resources.XA4242, artifact_spec, nuget);
else
log.LogError ("Java dependency '{0}:{1}' version '{2}' is not satisfied.", dependency.GroupId, dependency.ArtifactId, dependency.Version);
log.LogCodedError ("XA4242", Properties.Resources.XA4241, artifact_spec);

return false;
}
Expand All @@ -183,7 +169,13 @@ public void AddPackageReferences (ITaskItem []? tasks)
foreach (var task in tasks.OrEmpty ()) {

// See if JavaArtifact/JavaVersion overrides were used
if (TryParseJavaArtifactAndVersion ("PackageReference", task))
if (task.TryParseJavaArtifactAndJavaVersion ("PackageReference", log, out var explicit_artifact, out var attributes_specified)) {
artifacts.Add (explicit_artifact);
continue;
}

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

// Try parsing the NuGet metadata for Java version information instead
Expand All @@ -204,7 +196,13 @@ public void AddProjectReferences (ITaskItem []? tasks)
{
foreach (var task in tasks.OrEmpty ()) {
// See if JavaArtifact/JavaVersion overrides were used
if (TryParseJavaArtifactAndVersion ("ProjectReference", task))
if (task.TryParseJavaArtifactAndJavaVersion ("ProjectReference", log, out var explicit_artifact, out var attributes_specified)) {
artifacts.Add (explicit_artifact);
continue;
}

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

// There currently is no alternate way to figure this out. Perhaps in
Expand All @@ -228,55 +226,6 @@ public void AddIgnoredDependencies (ITaskItem []? tasks)
}
}

// "type" is PackageReference or ProjectReference
// Returns "true" if JavaArtifact/JavaVersion is used, even if it was used incorrectly and is useless.
// This is so the caller will know to try alternate methods if neither JavaArtifact or JavaVersion were specified.
bool TryParseJavaArtifactAndVersion (string type, ITaskItem task)
{
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");
var has_version = task.HasMetadata ("JavaVersion");

if (has_artifact && !has_version) {
log.LogError ("'JavaVersion' is required when using 'JavaArtifact' for {0} '{1}'.", type, item_name);
return true;
}

if (!has_artifact && has_version) {
log.LogError ("'JavaArtifact' is required when using 'JavaVersion' for {0} '{1}'.", type, item_name);
return true;
}

if (has_artifact && has_version) {
var id = task.GetMetadata ("JavaArtifact");
var version = task.GetMetadata ("JavaVersion");

if (string.IsNullOrWhiteSpace (id)) {
log.LogError ("'JavaArtifact' cannot be empty for {0} '{1}'.", type, item_name);
return true;
}

if (string.IsNullOrWhiteSpace (version)) {
log.LogError ("'JavaVersion' cannot be empty for {0} '{1}'.", type, item_name);
return true;
}

if (MavenExtensions.TryParseArtifactWithVersion (id, version, log, out var art)) {
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);
}

return true;
}

return false;
}

bool TrySatisfyDependency (ResolvedDependency dependency)
{
if (!dependency.Version.HasValue ())
Expand Down Expand Up @@ -321,10 +270,10 @@ public MSBuildLoggingPomResolver (TaskLoggingHelper logger)

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

if (!File.Exists (filename)) {
logger.LogError ("Requested POM file '{0}' does not exist.", filename);
logger.LogCodedError ("XA4245", Properties.Resources.XA4245, filename);
return null;
}

Expand All @@ -348,7 +297,7 @@ public MSBuildLoggingPomResolver (TaskLoggingHelper logger)
return final_artifact;
}
} catch (Exception ex) {
logger.LogError ("Failed to register POM file '{0}': '{1}'", filename, ex);
logger.LogCodedError ("XA4246", Properties.Resources.XA4246, filename, ex.Message);
return null;
}
}
Expand All @@ -358,7 +307,7 @@ public Project ResolveRawProject (Artifact artifact)
if (poms.TryGetValue (artifact.ToString (), out var project))
return project;

logger.LogError ("Unable to resolve POM for artifact '{0}'.", artifact);
logger.LogCodedError ("XA4247", Properties.Resources.XA4247, artifact);

throw new InvalidOperationException ($"No POM registered for {artifact}");
}
Expand Down Expand Up @@ -406,10 +355,10 @@ public class Package

public class NuGetPackageVersionFinder
{
LockFile lock_file;
Dictionary<string, Artifact> cache = new Dictionary<string, Artifact> ();
Regex tag = new Regex ("artifact_versioned=(?<GroupId>.+)?:(?<ArtifactId>.+?):(?<Version>.+)\\s?", RegexOptions.Compiled);
Regex tag2 = new Regex ("artifact=(?<GroupId>.+)?:(?<ArtifactId>.+?):(?<Version>.+)\\s?", RegexOptions.Compiled);
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);

NuGetPackageVersionFinder (LockFile lockFile)
{
Expand All @@ -423,7 +372,7 @@ public class NuGetPackageVersionFinder
var lock_file = lock_file_format.Read (filename);
return new NuGetPackageVersionFinder (lock_file);
} catch (Exception e) {
log.LogError (e.Message);
log.LogMessage ("Could not parse NuGet lock file. Java dependencies fulfilled by NuGet packages may not be available: '{0}'.", e.Message);
return null;
}
}
Expand All @@ -440,7 +389,7 @@ public class NuGetPackageVersionFinder
var nuget = lock_file.GetLibrary (library, new NuGet.Versioning.NuGetVersion (version));

if (nuget is null) {
log.LogError ("Could not find NuGet package '{0}' version '{1}' in lock file. Ensure NuGet Restore has run since this <PackageReference> was added.", library, version);
log.LogCodedError ("XA4248", Properties.Resources.XA4248, library, version);
return null;
}

Expand Down Expand Up @@ -636,8 +585,6 @@ public static IEnumerable<MavenVersionRange> Parse (string range)
if (!range.HasValue ())
yield break;

var versionGroups = new List<string> ();

// Do a pass over the range string to parse out version groups
// eg: (1.0],(1.1,]
var in_group = false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -810,7 +810,7 @@ public void AndroidMavenLibrary_FailsDueToUnverifiedDependency ()
Assert.IsFalse (b.Build (proj), "Build should have failed.");

// Ensure an error was raised
StringAssertEx.Contains ("error : Java dependency 'androidx.annotation:annotation' version '1.2.0' is not satisfied.", b.LastBuildOutput);
StringAssertEx.Contains ("error XA4242: Java dependency 'androidx.annotation:annotation:1.2.0' is not satisfied.", b.LastBuildOutput);
}
}

Expand Down
Loading

0 comments on commit 5f21105

Please sign in to comment.