Skip to content

Support multiple-binding projects in Java Dependency Verification #9013

Closed

Description

@jpobst

Context: jpobst/Prototype.Android.MavenBindings#13
Context: https://github.com/xamarin/xamarin-android/blob/main/Documentation/docs-mobile/features/maven/java-dependency-verification.md

Today, our Java Dependency Verification feature is built around our recommended practice of 1 binding library per project. This is reflected in the fact that JavaArtifact and JavaVersion only support a single library:

<PackageReference 
  Include="Xamarin.Kotlin.StdLib" 
  Version="1.7.10" 
  JavaArtifact="org.jetbrains.kotlin:kotlin-stdlib" 
  JavaVersion="1.7.10" />

However a user may choose to place multiple Java libraries in a single package (or project), and we have no way to express that.

We should expand our support to allow expressing multiple Java libraries.

Note <PackageReference> is used as an example, but this extends to everywhere, like <ProjectReference> and NuGet package tags.

Solution 1

One possible way to do this is to allow JavaArtifact to contain the JavaVersion, and then allow for multiple libraries to be specified, separated by a semicolon:

<PackageReference 
  Include="Xamarin.Kotlin.StdLib" 
  Version="1.7.10" 
  JavaArtifact="org.jetbrains.kotlin:kotlin-stdlib:1.7.10;org.jetbrains.kotlin:kotlin-stdlib-ktx:1.7.10" />

Solution 2

Another possible way is to add a new JavaArtifacts (plural) metadata with the same semantics as the JavaArtifact described in Solution 1. This new attribute would be mutually exclusive with JavaArtifact/JavaVersion.

<PackageReference 
  Include="Xamarin.Kotlin.StdLib" 
  Version="1.7.10" 
  JavaArtifacts="org.jetbrains.kotlin:kotlin-stdlib:1.7.10;org.jetbrains.kotlin:kotlin-stdlib-ktx:1.7.10" />

It feels like Solution 1 is better.

Since we have only released this as a preview, would it be better to go further and scrap JavaVersion altogether, and always use JavaArtifact with a version? The original idea was that having them separate made it easier to maintain version numbers centrally using Update if desired, like:

<PackageReference 
  Update="Xamarin.Kotlin.StdLib" 
  JavaVersion="1.7.10" />

But I don't know if this is actually useful in practice? 🤷

Activity

  1. added this to the .NET 9 milestone on Jun 10, 2024
  2. self-assigned this
    on Jun 10, 2024
  3. jonpryor commented on Jun 12, 2024

    @jonpryor
    Contributor

    I have a "silly allergy" to lines which are "too wide", and I can see JavaArtifact="A:1;B:2;C:3;…" as resulting in very wide lines. This can be somewhat improved by allowing newlines in the %(JavaArtifact) value a'la dotnet/java-interop#1086 (comment):

    <PackageReference
        Include="Xamarin.Kotlin.StdLib" 
        Version="1.7.10" 
        JavaArtifact="
            org.jetbrains.kotlin:kotlin-stdlib:1.7.10
            ;org.jetbrains.kotlin:kotlin-stdlib-ktx:1.7.10
            ;insert.another:package.here:1.2.3"
    />

    XML would permit this, it's just that whatever parses %(JavaArtifact) would also need to support newlines.

    Additionally, this doesn't feel "scalable"; it feels like it'll need to be copied "everywhere". For example, you have Binding.csproj which binds multiple Java libraries, and then you have N projects which use <ProjectReference Include="…\Binding.csproj" />. Presumably every one of these N projects would also need to be updated to mention %(JavaArtifact)?

    For existing NuGet packages, needing to mention %(JavaArtifact) at every callsite may be unavoidable. For new NuGet packages and @(ProjectgReference), it should be possible to instead have Binding.csproj provide the required information, instead of having every reference provide the required information.

    A way to do this would be to use the <CallTarget/> task. IIRC MSBuild internally does this for lots of things, e.g. to compute the list of output @(Content) items which need to be copied into the referencing project, etc.

  4. jonpryor commented on Jun 13, 2024

    @jonpryor
    Contributor

    @jpobst asked:

    Since we have only released this as a preview, would it be better to go further and scrap %(JavaVersion) altogether, and always use %(JavaArtifact) with a version?

    Yes. And also we should find a way to not need to mention %(JavaArtifact) for most users.

    And another plug for my above <CallTarget/> suggestion, NuGet packages should also be able to provide the %(JavaArtifact) information from .targets files included within the NuGet package. This means that for new NuGet packages, "callers"/users of @(PackageReference) wouldn't need to specify %(JavaArtifact) at all. %(JavaArtifact) would only need to be used for older packages.

  5. jpobst commented on Jun 13, 2024

    @jpobst
    ContributorAuthor

    Yes. And also we should find a way to not need to mention %(JavaArtifact) for most users.

    Note that we already have this today. Users using updated NuGet packages do not need to specify %(JavaArtifact). We parse the NuGet metadata tags for a well-defined string (that we need to add documentation for) to determine what artifact it binds. The issue is we currently only support a single tag, we need to extend this to support multiple tags:

    https://github.com/xamarin/xamarin-android/blob/d6e47a84b9622fcea3d52ca8549e1e0b032173a4/src/Xamarin.Android.Build.Tasks/Tasks/JavaDependencyVerification.cs#L411-L419

    @(ProjectReference) is more problematic, as you currently always have to specify %(JavaArtifact). Ideally we could scan the referenced project somehow and find them automatically but that doesn't exist yet. The currently existing mechanism of %(JavaArtifact) only supports specifying a single artifact.

    NOTE: @(ProjectReference) enhancement has been moved to #9093. This issue will only deal with supporting multiple libraries per deployment unit.

  6. jasells commented on Jun 13, 2024

    @jasells

    I vote in favor of this as a feature, especially for "framework" dependency scenarios.

    Users using updated NuGet packages do not need to specify %(JavaArtifact). We parse the NuGet metadata tags for a well-defined string (that we need to add documentation for) to determine what artifact it binds.

    You can see an example of how this could work in a package-project here.

    I modded the prototype task to correctly resolve all dependencies in that package here. The usage is a standard package ref:
    <PackageReference Include="XDev.IO.Ktor" Version="2.3.2.5" /> in the consuming project file.

    @(ProjectReference) is more problematic, as you currently always have to specify %(JavaArtifact). Ideally we could scan the referenced project somehow and find them automatically but that doesn't exist yet. The currently existing mechanism of %(JavaArtifact) only supports specifying a single artifact.

    That's one reason why I went the route of packing the project with multiple native libs that I had when trying to use the original prototype task. The change to the task code was fairly trivial.

  7. added
    enhancementProposed change to current functionality.
    on Jul 9, 2024
  8. locked and limited conversation to collaborators on Sep 20, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Area: BindingsIssues in Java Library Binding projects.enhancementProposed change to current functionality.

Type

No type

Projects

No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions