Repository navigation
Binding: Vision #5352
Description
The current binding infrastructure is "janky":
- "Somehow obtain" a
.aaror.jarfile to bind. - Add it to a Binding project, and set the appropriate build action, e.g.
@(LibraryProjectZip)or@(EmbeddedJar). - Build it.
In practice, (1) and (3) can be incredibly annoying. (At least for @jonpryor, it's not always obvious where to obtain the files referenced by a Maven artifact, and (3) is full of peril.)
(3) will remain "out of scope" for this vision; fixing (3) involves ensuring that generator can always emit compilable output, and that we expand the Java constructs that generator supports. We should absolutely do this, but addressing these deficits involves finding the "less-than-ideal" constructs, filing issues for them, and fixing them.
Which leaves (1) and (2): can anything be done there?
Artifact Retrieval & Binding
Instead of a .aar or .jar file as being the "primary starting point", can we instead have a Maven artifact ID be the primary point?
Then, given a set of Maven artifact IDs:
- Specify the Maven IDs.
- Obtain the entire dependency graph for the maven artifacts.
- Find existing bindings for everything within (1).
- For any dependencies not already bound, bind them.
Specify the Maven IDs
How should the Maven IDs be specified? What's the "user interface"?
Obtain Maven Dependency Graph
I believe @moljac has code to do this, as part of Binderator: https://github.com/xamarin/XamarinComponents/tree/master/Util/Xamarin.AndroidBinderator
Find existing bindings
It appears that NuGet.org allows searching arbitrary package metadata. If we ensure that all new NuGet packages contain NuGet.org-searchable package metadata containing the Maven artifact ID & version, then it should be possible to find existing bindings.
@moljac is working on this.
Bind unbound packages
For any maven artifacts in the dependency graph which haven't been bound, presumably including the "root" dependencies, we should bind them.
Proposal: Item Group?
We can imagine using an MSBuild item group to specify the "root" maven IDs, either directly:
<MavenArtifact Include="androidx.car" ArtifactId="car" Version="1.0.0-alpha7" />or by using a file (file format? Gradle? JSON? XML?) to contain the information.
Implicit in this idea is that these are like @(PackageReference) item group: the containing .csproj can have as many of them as desired, which in turn means that the .csproj could emit "many" output assemblies (dozens?), one per bound library as well as the "intentioned" output assembly.
Additionally, how would @(TransformFile) (e.g. metadata.xml) work in such an environment? All @(TransformFile)s apply to all unbound artifacts?
Proposal: dotnet new?
Instead of using an item group, add a new tool, e.g.
dotnet new maven-binding --group-id androidx.car --artifact-id car --version 1.0.0-alpha7
This command would in turn emit a solution, with one .csproj project per unbound artifact. This "answers" the @(TransformFile) question: each project uses has its own set of @(TransformFile)s.
Less well defined is this: how does one update an existing "binding solution"? Changing the root artifact version will "ripple" across the entire solution; is it possible to update such a directory tree in-place? Or would we need to re-invoke the dotnet new maven-binding command into a new directory, and require the user manually copy over any required @(TransformFile)s & partial classes and such?
Activity
jonathanpeppers commentedon Dec 1, 2020
When working on #5348 I found you can do
dotnet new maven-binding --forceto write on top of existing directories and files. It does not delete any existing files when using--force.jpobst commentedon Dec 2, 2020
It sounds like this proposal is basically doing 2 things for the user:
#1probably isn't too interesting. I don't think it used to be this way, but now you can just search on Maven and it provides an easy link to download the artifact: example.We could probably even automate this without much effort in today's projects if we wanted to and resolve it at compile time:
#2is definitely more interesting. I'm not sure how much effort it normally takes.Additional Considerations to Flesh Out
NuGet Metadata
From my reading of that link, I don't think NuGet supports searching arbitrary metadata. It seems to only support searching the official NuGet supported metadata. Thus we would need to decide which of those fields we would use.
Additions considerations:
com.squareup.okhttp3.okhttp.androidx.coreon NuGet?Visual Studio UX
How extensible is the built-in VS template UI system? Can we prompt the user to enter Maven information that we can use? Is the template system robust enough that it can run our custom logic to query Maven and resolve dependencies in order to generate projects?
If we can only do this outside of VS, do we think users will find and use it?
Updates
Once a package has been bound and a new release is posted to Maven, what does the update process look like for updating our binding?
Cases to consider:
Publishing to NuGet
We need to understand what the target user is going to do with the binding: consume it locally or publish to NuGet.
If publishing to NuGet then there are a few additional issues like:
moljac commentedon Dec 2, 2020
Trivial.
Not only Maven. What if we have bindings? maven gives a sheet about
nuget.orgI am testing client API for search
https://github.com/HolisticWare-Xamarin-Tools/HolisticWare.Xamarin.Tools.Bindings.XamarinAndroid.FassBinderMeister/blob/master/source/HolisticWare.Xamarin.Tools.NuGet/NuGetClient.cs#L36-L73
NOTE:
I did not play with that more than 15 minutes.
dunno. Trying to have something - then I can make new steps...
Writing xummary and pseudo-code. Coming soon
moljac commentedon Dec 2, 2020
Nope. Side project. Will be easily embeddable. Or code copied with whatever changes needed (namespace changes etc)
MavenArtifactwould not normaly appear in .NET library or project, but Xamarin.Android. Thus we could add some convetion-based rules... Like checking for serialized data files (json, xml) with artifacts to bind...If the ItemGroup is defined we could:
or
Why working with files?
It would be easier to have such files as "interfaces" for design and testing.
Now more details:
We have 2 sources od ultimate divine truth (priority list):
nuget.orgmaven.google.com)Pick this:
What I am trying to implement right now:
query
nuget.orgif there is already something like "androidx car car" on nuget.org. Grab versions and metadatahttps://github.com/HolisticWare-Xamarin-Tools/HolisticWare.Xamarin.Tools.Bindings.XamarinAndroid.FassBinderMeister/blob/master/source/HolisticWare.Xamarin.Tools.NuGet/NuGetClient.cs#L43
If there is NO such nuget package or the version is GREATER-THAN
Version="1.0.0-alpha7"put in the List TODOs_ToBindCheck dependencies from maven POM
moljac commentedon Dec 2, 2020
POM does not have stable name (yet) it is basically Maven 4.0.0 XSD schema transformed into C# classes
https://github.com/HolisticWare-Xamarin-Tools/HolisticWare.Xamarin.Tools.Bindings.XamarinAndroid.FassBinderMeister/blob/master/docs/maven-4.0.0.xsd
https://github.com/HolisticWare-Xamarin-Tools/HolisticWare.Xamarin.Tools.Bindings.XamarinAndroid.FassBinderMeister/blob/master/source/HolisticWare.Xamarin.Tools.Maven/Models.GeneratedFromXSD.Original/maven-4.0.0.cs
I used XML to from AndroidX and GPS-FB to generate C#, - BUT there were some fields missing. So last week I generated C# from maven XSD
moljac commentedon Dec 2, 2020
My plan is 20201202 for next 2 weeks:
create minimal API for step 3 in Binding: Vision #5352 (comment)
pack/publish some HolisticWare packages with API
create Cake scripts for testing that can be copied ANYWHERE (C# libs for MSBuild tasks or dotnet tools)
jpobst commentedon Dec 2, 2020
An additional consideration from @Redth:
Maven is like NuGet, there can be many feeds, some of which may require authentication. So we may need to support specifying a URL, and possibly a username/password as well.
Redth commentedon Dec 3, 2020
We should really back up a step here and sit down to discuss a few things:
moljac commentedon Dec 8, 2020
In very few cases they do. Usually expect to be done by Microsoft or some partner. Basically wishes to have some SDK bindings are submitted as issues in (AndroidX/AndroidSupport, GPS-FB and XamarinComponents). Some are submitted as internal mails and very few (for me personally) over social media.
2.1 find/download artifact,
2.2 analyze artifact (dependencies and nuget packages)
Challenges I personally have:
I use following repositories (in this order)
https://maven.google.com/web/index.html
https://mvnrepository.com/
https://search.maven.org/
For published bindings on NuGet.org this is not necessary
dotnet new?BuildAction?Improving productivity will most likely increase abstraction (add one or more levels of APIs) that might drive some users away from bindings.
Needed APIs:
NuGet querying for info about bindings
After few days of more intensive work with NuGet client API the conclusion is that there is much more work
than expected. NuGet.org does not have perfect search, so client API is limited too.
Few problems:
Searchdoes not retrieve DependencySets, so filtering by TargetFramework is more complicated then I thoughSearchAPI input is string as keyword retrieving tons of results.Current solution is to provide API with
Predicateor plannedPredicateBuilderfor custom searchand to extend it with Metadata query which retrieves Package DependencySetsProperly
Maven APIs (extended - more than in MavenNET)
API that uses data from NuGet API and Maven API and prepares bindings
NOTE: More details to come.
I work with Google's maven right now, but I do plan to add others (mavenCentral and jcenter/Bintray).
I would not bother with private repos or repos that need authentication. There must be some reason artifacts are not public. We could add that later.
moljac commentedon Dec 8, 2020
Maven API (extended)
20201208
Initial data for current version of
binderator:https://github.com/xamarin/AndroidX/blob/master/config.json#L172-L179
NOTE:
For AndroidX and GPS-FB there is almost 200 artifacts to bind with growing number.
So, I want to free my time so I can do other stuff including these improvements.
Thus starting with
binderator'sconfig.jsonBecause it is manual process (currently) order of json data is important for me, because I can easily check/verify
versions and names.
Artifact Versions
So with 20201208 there is extended info with versions (available for artifact):
This will provide opportunity to verify version given for bindings ("1.0.0-alpha6" would fail) and to
make plan for future bindings or even automate (generate next config, based on current)
Cache
WIP - deserializing locally dumped Maven repo data - if not grab it.
ProjectObjectModel POM
Currently POM is grabbed and deserialized.
WIP on extracting important data like dependecies:
Extras
order of serialized data
Goal is to have order of serialized data as close as possible to
binderator's config files,so that manual work can be continued.
cache
serialization with buddy (metadata classes)
textual data in debug mode
moljac commentedon Dec 8, 2020
NuGet API
20201208
Currently there are 3 Nuget Client API use cases:
Search
GetMetadata
GetVersions
Search is very limited regarding filtering and customizations and searches using
Contains()API.So, API with Predicate had to be added and
Furthermore Search does not return DependecySets to filter out based on TargetFramework, so for each
nuget
GetMetadataAsync(...)must be called. I open an issue in Nuget Client API repo. The idea was notto surface NuGetClient classes, but for now I had to.
Search
https://www.nuget.org/packages?q=car
https://www.nuget.org/packages?q=androidx+car
https://www.nuget.org/packages?q=car+androidx
So, the closest API in few days that returns the same results (3 results for "androidx+car" search ) is:
And it returns serialized:
Extras
order of serialized data
Goal is to have order of serialized data as close as possible to
binderator's config files,so that manual work can be continued.
cache
custom predicates (as static members) for filtering
serialization with buddy (metadata classes)
textual data in debug mode
Redth commentedon Sep 14, 2021
I did a spike on a lot of this some time ago: https://github.com/Redth/Xamarin.Binding.Helpers
The idea was to use a .gradle file to specify the dependencies for the binding and let gradle/maven resolve the dependency graph for you, since that's what it's good at. With a small plugin added to the .gradle file, I was able to serialize all of the dependency graph from gradle in a way that msbuild tasks could parse it out and use it to obtain the dependency tree info (maven artifact group, id, version, binaries, etc). This could all be completely automated such that a gradle config is created for the top level maven artifacts specified by the developer and gradle can be downloaded and invoked on behalf of the user to get this info. The bonus here is things like maven feed authentication do not need re-inventing or engineering.
Once the dependency graph was known, all of the actual aar/jar binaries could be downloaded and included as appropriate items in the binding project. However, as mentioned in this issue several times, there's already lots of nuget packages out there that ship these artifacts in binding projects and would conflict during builds. Often these packages are desirable instead of just bundling the artifacts since their binding c# api surface may be required.
I did some work in the same repo around matching up NuGet packages. The idea of having metadata in nuget packages to match up their maven artifacts is great.... except... we'll likely never be able to get the entire ecosystem updated to this model. If that's an assumption then this mechanism would only be reliable for packages we control, or otherwise well known community packages. If we assume that, we can pretty much identify that set of packages already and map out some rules for the artifacts that packages map.
This is exactly what I did here: https://github.com/Redth/Xamarin.Binding.Helpers/blob/main/Xamarin.Binding.Helpers/NuGetResolvers/AndroidXMavenNugetResolver.cs
There are others for Firebase, GPS, and other well known package: https://github.com/Redth/Xamarin.Binding.Helpers/blob/main/Xamarin.Binding.Helpers/NuGetResolvers/KnownMavenNugetResolver.cs#L12-L98
It's really not that bad of a set of rules/mappings to have/maintain perhaps in addition to new packages being able to publish maven artifact related metadata (and fallback to these mappings).
The idea was that these rules would apply, and/or you could specify explicit mappings yourself if you know they exist, like this:
So, for this whole library you would basically reference and android studio / gradle project in your xamarin binding project like this:
This would even go build the native project for you and include the module's binary (aar) as a so that it would generate bindings (or not if you disabled that option). It would link in the entire dependency tree of maven artifacts based on the android project's declared compile dependencies, but it would substitute those for NuGet packages according to the resolvers or explicit mapping override info you specified.
This isn't a perfect approach by any means, and I could see taking it a step further to not require the android studio project at all, but this is a good start to look at.
Generally you wouldn't need to do this
jonpryor commentedon Sep 12, 2022
It looks like
gradlew -I INIT-SCRIPTcan be specified multiple times: https://github.com/gradle/gradle/blob/edb2cc76c1d398633843303a42ad683db0fe8393/subprojects/core/src/main/java/org/gradle/initialization/StartParameterBuildOptions.java#L238-L251Consequently, it should be possible to distribute a
.gradlefile with "something" (Android SDK, NuGet package) and pass this file togradlewwhen invoking it. (Perhaps via a@(AndroidGradleInitScript)item group?)Assuming this works, it means we can reliably add a Gradle task to retrieve the artifacts of the Gradle build, so that we can automagically determine paths to built artifacts + dependencies.
jpobst commentedon Sep 13, 2022
I've been giving this some thought recently, and maybe we should do less automatically for the user (since that's where the dragons live), and focus first on helping the user ensure they are creating correct bindings.
TL:DR
We will initially focus on tackling two pain points of binding from Maven:
.jar/.aarand the related.pomfrom Maven.pomto verify that required Java dependencies are being fulfilledjpobst commentedon Apr 17, 2024
These features have been implemented for .NET 9 in the following PRs:
I think the rest of this issue is conjectural and not really actionable. Any additional steps we want to consider taking should probably be proposed and fleshed out in new issue(s).