-
Notifications
You must be signed in to change notification settings - Fork 53
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Java.Interop.Tools.Maven] Initial commit. #1179
Conversation
64b12e1
to
c1b64bb
Compare
8178ac5
to
1bb76b2
Compare
1bb76b2
to
cf1efa0
Compare
2ac4f22
to
d7be7c4
Compare
src/Java.Interop.Tools.Maven/Repositories/InMemoryMavenRepository.cs
Outdated
Show resolved
Hide resolved
60e7b95
to
52addaf
Compare
{ | ||
foreach (var property_set in stack) { | ||
foreach (var prop in property_set) | ||
value = value.Replace ($"${{{prop.Key}}}", prop.Value); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This gives me some concern, if only because this could produce lots of garbage, all depending on stack
.
I think I'd prefer a "optimization check" + use of StringBuilder.Replace()
:
if (stack.Count == 0 || !value.Contains("${") {
return value;
}
var s = new StringBuilder (value);
foreach (var property_set in stack) {
foreach (var prop in property_set) {
s.Replace ($"${{{prop.Key}}}", prop.Value);
}
}
return s.ToString ();
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added the optimization check. 👍
From my research looking at POM files, this code will rarely be hit with the optimization check, and when it is hit it will generally be for small numbers of properties. The general guidance seems to be that string.Replace
is faster than StringBuilder.Replace
for small numbers of replaces, despite the extra garbage, as it avoids the overhead of creating the StringBuilder
and converting it ToString
at the end.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't recall reading the guidance about "string.Replace
is faster … for small numbers of replace". Would be interesting to read more.
Convention elsewhere is to use We should thus have:
I also wonder if the "sub-namespaces" are being too granular here; do we really need |
[System.Diagnostics.DebuggerStepThroughAttribute()] | ||
[System.ComponentModel.DesignerCategoryAttribute("code")] | ||
[System.Xml.Serialization.XmlRootAttribute("project")] | ||
public partial class Project |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
…then again, maybe we want generated code to go into its own sub-namespace…?
|
||
// https://github.com/mganss/XmlSchemaClassGenerator | ||
// This code was generated by XmlSchemaClassGenerator version 2.1.1057.0 using the following command: | ||
// xscgen maven-4.0.0.xsd --nullable --nc --nr |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where is maven-4.0.0.xsd
? Should that be added to this project for "reference" purpsoes?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we should add it to our repo, but adding a URL for it to the comment seems like a good idea.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Given that this is a generated file, should we move this comment block & related into Project.Partial.cs
, so that we don't have to manually update this file whenever we regenerate it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Relatedly, I imagine you didn't build mganss/XmlSchemaClassGenerator yourself, but instead used a NuGet package such as https://www.nuget.org/packages/XmlSchemaClassGenerator.Console. Would be useful to put this into a .targets
file to make updating easier.
|
||
public interface IMavenRepository | ||
{ | ||
// This name is used for on-disk caching purposes, and thus must be compatible with file system naming rules. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure if this means that directory names can also be included or not. Is foo/bar.xml
valid?
If directories names can't be included, perhaps this should be named FileName
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Basically we need to place each Maven repository in its own directory in our cache to prevent collisions if they contain the same Java artifact. In this example, it is "central" or "google":
- /MavenCacheDirectory
- /central/androidx.core/core/1.9.0/core-1.9.0.aar
- /google/androidx.core/core/1.9.0/core-1.9.0.aar
I will try to describe it a little better in the comment.
Name = name; | ||
base_url = baseUrl; | ||
|
||
client = new HttpClient { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
General guidelines for HttpClient
usage is that HttpClient
instances should either be long-lived and shared (and static
) or through IHttpClientFactory
(though I'm not quite sure what that actually looks like in practice…).
new
ing up and ~immediately disposing of HttpClient
instances is not a good idea.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Related: HttpClient.BaseAddress
is a read+write property.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This shouldn't have been too bad, as MavenRepository
itself is generally expected to be long-lived, but switched to using a static
HttpClient
to be better.
return true; | ||
} | ||
|
||
public static MavenRepository Google = new MavenRepository ("https://dl.google.com/android/maven2/", "google"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be readonly
, as I can't imagine a scenario where someone should be able to do MavenRepository.Google = someOtherValue
.
|
||
public static MavenRepository Google = new MavenRepository ("https://dl.google.com/android/maven2/", "google"); | ||
|
||
public static MavenRepository Central = new MavenRepository ("https://repo1.maven.org/maven2/", "central"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be readonly
.
/// <summary> | ||
/// Wraps an <see cref="IMavenRepository"/> and caches files in a local directory. | ||
/// </summary> | ||
public class CachedMavenRepository : IMavenRepository |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Naming question: I think this should be named MavenRepositoryCache
.
Rationale/5 minutes of investigation: consider dotnet/runtime, commit dotnet/runtime@4765dd1:
# class contains `Cache`
% git grep 'public.*class.*Cache' src/libraries | wc -l
83
# class uses `Cached`
% git grep 'public.*class.*Caching' src/libraries | wc -l
1
# class uses `Caching`
% git grep 'public.*class.*Caching' src/libraries | wc -l
10
# class ends with `Cache`
% git grep 'public.*class.*Cache\>' src/libraries | wc -l
20
Type names containing Cache
include:
CacheEntryExtensions
MemoryCacheEntryExtensions
These don't feel like a good pattern to me, even if this has the most matches.
Cached
has one match:
XmlCachedSchemaSetResolver
Cache
as a suffix feels better:
MemoryDistributedCache
RuleCache<T>
CredentialCache
Caching
, meanwhile, only shows up as sub-namespaces:
src/libraries/Microsoft.Extensions.Caching.Memory/ref/Microsoft.Extensions.Caching.Memory.cs: public partial class MemoryDistributedCache : Microsoft.Extensions.Caching.Distributed.IDistributedCache
src/libraries/Microsoft.Extensions.Caching.Memory/ref/Microsoft.Extensions.Caching.Memory.cs: public partial class MemoryCache : Microsoft.Extensions.Caching.Memory.IMemoryCache, System.IDisposable
src/libraries/Microsoft.Extensions.Caching.Memory/ref/Microsoft.Extensions.Caching.Memory.cs: public partial class MemoryCacheOptions : Microsoft.Extensions.Options.IOptions<Microsoft.Extensions.Caching.Memory.MemoryCacheOptions>
src/libraries/Microsoft.Extensions.Caching.Memory/ref/Microsoft.Extensions.Caching.Memory.cs: public partial class MemoryDistributedCacheOptions : Microsoft.Extensions.Caching.Memory.MemoryCacheOptions
src/libraries/Microsoft.Extensions.Diagnostics/tests/DefaultMetricsFactoryTests.cs: public class NoCachingMeterFactory : IMeterFactory
src/libraries/System.Linq.Expressions/tests/Dynamic/CallSiteCachingTests.cs: public class CallSiteCachingTests
src/libraries/System.Runtime.Caching/ref/System.Runtime.Caching.cs: public abstract partial class CacheEntryChangeMonitor : System.Runtime.Caching.ChangeMonitor
src/libraries/System.Runtime.Caching/ref/System.Runtime.Caching.cs: public abstract partial class FileChangeMonitor : System.Runtime.Caching.ChangeMonitor
src/libraries/System.Runtime.Caching/ref/System.Runtime.Caching.cs: public sealed partial class HostFileChangeMonitor : System.Runtime.Caching.FileChangeMonitor
src/libraries/System.Runtime.Caching/ref/System.Runtime.Caching.cs: public partial class MemoryCache : System.Runtime.Caching.ObjectCache, System.Collections.IEnumerable, System.IDisposable
So Caching
feels like a bad idea.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think MavenRepositoryCache
implies that this is a cache of MavenRepository
, which it is not.
It is a wrapper around a MavenRepository
which adds caching features to it.
It feels like CachedMavenRepository
is better as MavenRepository
is the noun and Cached
(or Caching
) is the adjective describing the modification to the MavenRepository
.
using (var sw = File.Create (file)) | ||
repo_stream!.CopyTo (sw); | ||
|
||
using (repo_stream) { } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This feels weird.
I think this would feel better:
using (var sw = File.Create (file))
using (repo_stream) {
repo_stream.CopyTo (sw);
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed that is better.
|
||
if (repository.TryGetFile (artifact, filename, out var repo_stream)) { | ||
using (var sw = File.Create (file)) | ||
repo_stream!.CopyTo (sw); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since you've annotated TryGetFile()
with [NotNullWhen (true)]
elsewhere, why is repo_stream!
required?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It doesn't appear to be, it was likely meant to be temporary. Removed.
}; | ||
|
||
if (parent is not null) | ||
p.Parent = new Parent { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This makes me wonder if there should be a Parent(Parent)
constructor…
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Honestly, a better case could be made that Project
should not have a public
constructor, so I made it internal
and changed this to generate xml that can be deserialized.
|
||
static class TestDataExtensions | ||
{ | ||
public static Project CreateProject (Artifact artifact, Project? parent = null) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Given that this is within an *Extensions
class, I'm surprised this isn't an extension method.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Meh, it's a test assembly and I didn't want an entire class for it. 😁
Unfortunately this convention seems outdated, as modern tooling does not play nicely with it. For one, it's kinda annoying that it forces a minimum size of a file explorer in order to know which directory you want to expand: The other issue is that the "new item" templating system builds your namespace based on Thus adding a new class namespace Java.Interop.Tools.JavaCallableWrappers.Java.Interop.Tools.JavaCallableWrappers
{
class Foo
{
}
}
This is largely related. I don't really care about the "sub-namespaces", but I do want them to be in "sub-folders" rather than all sitting in the project root. Then when you do Add New Item VS automatically puts them in the matching "sub-namespace". I've always just manually fixed them before to not be in "sub-namespaces", but I thought this seems to be the way modern .NET tooling is pushing and maybe I should stop fighting it on new projects. 🤷♂️ I can remove the "sub-namespaces" if needed. |
dbf91ae
to
f938d56
Compare
|
||
public virtual Project ResolveRawProject (Artifact artifact) | ||
{ | ||
if (poms.TryGetValue (artifact.ToString (), out var project)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It feels "implicitly weird" that we require that Project.ToString()
be identical to Artifact.ToString()
. While Artifact.ToString()
does have a comment stating "This is a "well-known" format we use, it should not be changed", Project.ToString()
doesn't (currently), nor is there a comment stating that these formats should be consistent with each other.
I think it would be clearer and more explicit if we instead had "something" that was identically named between the two types to name the association. Possible ideas:
ArtifactString
("rhymes" withResolvedDependency.ToArtifactString()
)Group_Artifact_Version
(that's what it contains…)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I went with ArtifactString
and VersionedArtifactString
throughout the codebase to make this more explicit and consistent.
|
||
public interface IPomResolver | ||
{ | ||
Project ResolveRawProject (Artifact artifact); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is "Raw" serving in this name? Would this be better as just ResolveProject()
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Leftover from earlier designs, changed simply to Resolve
.
|
||
namespace Java.Interop.Tools.Maven; | ||
|
||
public interface IPomResolver |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Naming? Why not name this IProjectResolver
, as it (currently) only resolves Project
s? Is it so we can resolve other things in the future while reusing the same abstraction?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pom
and Project
are synonyms, standardized on Project
.
|
||
static class MavenNetExtensions | ||
{ | ||
public static string GetInheritedProperty (this ResolvedDependency dependency, ResolvedProject project, Func<ResolvedDependency, string?> property) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why extension methods and not actual methods, when the type you're providing extension methods for is in the same assembly? Is this so that the method isn't visible via IntelliSense by default?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moved to actual methods.
|
||
MavenVersion (string rawVersion) => RawVersion = rawVersion; | ||
|
||
public static MavenVersion Parse (string version) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this mirror Artifact
and have a TryParse()
method as well?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
…except it can't fail…. Weird semantics, especially around .IsValid
…
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, it is weird. There doesn't seem to be any validation on Maven version numbers, users can't technically put anything they want. If it isn't a "valid" Maven version, it just gets treated like an opaque string for sorting and version matching purposed.
If you do not follow Maven versioning standards in your project versioning scheme, then for version comparison, Maven interprets the entire version as a simple string.
From https://docs.oracle.com/middleware/1212/core/MAVEN/maven_version.htm#MAVEN8855.
|
||
public partial class Project | ||
{ | ||
public static Project Parse (Stream stream) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Given that Stream
will also contain XML, why have a ParseXml()
method vs naming both (Stream)
and (string)
overloads Parse()
?
Relatedly, there should probably also be a Parse(XmlTextReader)
overload, so that both Parse(Stream)
and Parse(string)
can share most of their logic.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alternate naming logic: do what XDocument
does, which has:
XDocument.Load()
loads fromStream
s,XmlReader
s, etc.Load(string)
loads from a file.XDocument.Parse()
loads from XML.
Thus, I think we should rename Parse(Stream)
to Load(Stream)
, add a Load(XmlReader)
overload, and rename ParseXml(string)
to Parse(string)
:
public static Project Load(XmlReader reader)
{
var serializer = new XmlSerializer (typeof (Project));
return (Project) serializer.Deserialize (reader);
}
public static Project Load(Stream stream)
{
return Load (new XmlTextReader (stream) {
Namespaces = false;
});
}
public static Project Parse(string xml)
{
using (var sr = new StringReader (xml))
return Load (new XmlTextReader (sr) {
Namespaces = false;
});
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I use Parse
and ParseXml
to roughly match XmlDocument.Load
and XmlDocument.LoadXml
for the reason you state: Parse/Load (string)
generally refers to a file rather than an XML snippet.
But Load
and Parse
work as well.
|
||
var serializer = new XmlSerializer (typeof (Project)); | ||
|
||
using (var sr = new StreamReader (stream)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why does this wrap a Stream
in a StreamReader
? The XmlTextReader(Stream)
constructor exists.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
|
||
void ResolveCore (PropertyStack properties) | ||
{ | ||
if (IsSuperPom) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is it called a "super POM" instead of a "root POM"? Maybe it's just me, but "super" doesn't imply (to me) that it has no parents; "root" does. (/
has no parent directory, C:\
has no parent directory, etc.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
…and "super POM" might be "better" in that it's the existing terminology of the space, but it doesn't feel "self documenting", as it were.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Correct, using Maven's terminology for this concept:
https://maven.apache.org/guides/introduction/introduction-to-the-pom.html#super-pom
base_url = baseUrl.TrimEnd ('/'); | ||
} | ||
|
||
static MavenRepository () |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any reason to have an explicit static constructor vs. just initializing in the field?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
Addendum/followup to my comment: this patch is what I'm thinking: diff --git a/NuGet.Config b/NuGet.Config
index 7d88f585..4e263ee0 100644
--- a/NuGet.Config
+++ b/NuGet.Config
@@ -6,7 +6,6 @@
-->
<configuration>
<packageSources>
- <clear />
<add key="dotnet-public" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public/nuget/v3/index.json" protocolVersion="3" />
<!-- For Microsoft.DotNet.GenAPI -->
<add key="dotnet-eng" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-eng/nuget/v3/index.json" protocolVersion="3" />
diff --git a/src/Java.Interop.Tools.Maven/Java.Interop.Tools.Maven.csproj b/src/Java.Interop.Tools.Maven/Java.Interop.Tools.Maven.csproj
index 9c0f50dc..9b296e87 100644
--- a/src/Java.Interop.Tools.Maven/Java.Interop.Tools.Maven.csproj
+++ b/src/Java.Interop.Tools.Maven/Java.Interop.Tools.Maven.csproj
@@ -22,6 +22,13 @@
<ItemGroup>
<PackageReference Include="Microsoft.SourceLink.GitHub" PrivateAssets="All" />
+ <PackageReference
+ Include="XmlSchemaClassGenerator.Console"
+ Version="2.1.1057"
+ ReferenceOutputAssembly="false"
+ GeneratePathProperty="true"
+ />
</ItemGroup>
+ <Import Project="Java.Interop.Tools.Maven.targets" />
</Project>
diff --git a/src/Java.Interop.Tools.Maven/Java.Interop.Tools.Maven.targets b/src/Java.Interop.Tools.Maven/Java.Interop.Tools.Maven.targets
new file mode 100644
index 00000000..2d6016c2
--- /dev/null
+++ b/src/Java.Interop.Tools.Maven/Java.Interop.Tools.Maven.targets
@@ -0,0 +1,18 @@
+<Project>
+ <Target Name="UpdateProjectSchema"
+ AfterTargets="ResolveAssemblyReferences">
+ <PropertyGroup>
+ <_Xscgen>"$(PkgXmlSchemaClassGenerator_Console)/tools/net462/XmlSchemaClassGenerator.Console.exe"</_Xscgen>
+ <MavenXsd Condition=" '$(MavenXsd)' == '' ">maven-4.0.0.xsd</MavenXsd>
+ </PropertyGroup>
+ <ItemGroup>
+ <_XscgenOpt Include="$(MavenXsd)" />
+ <_XscgenOpt Include="--nullable" />
+ <_XscgenOpt Include="--nc" />
+ <_XscgenOpt Include="--nr" />
+ <_XscgenOpt Include="-o" />
+ <_XscgenOpt Include=""$(MSBuildThisFileDirectory)"" />
+ </ItemGroup>
+ <Exec Command="$(Runtime) $(_Xscgen) @(_XscgenOpt, ' ')" />
+ </Target>
+</Project> which I then executed with:
There are multiple problems with this approach, which are all solvable:
And by different, namespace Pom
{
public partial class Model {}
// …
} This PR (#1179) does not contain a I'm not using "xscgen", but I am using the NuGet package that the github repo links to, with the command-line arguments you mention, but the output doesn't match. Am I using the right XSD? https://maven.apache.org/xsd/maven-4.0.0.xsd? |
@jpobst: I'm increasingly wondering how you created
Using Using However, there's still lots of differences:
@@ -26,10 +25,10 @@ namespace Java.Interop.Tools.Maven.Models
[System.ComponentModel.DescriptionAttribute(@"3.0.0+ The <code>&lt;project&gt;</code> element is the root of the descriptor. The following table lists all of the possible child elements. 3.0.0+ The <code>&lt;project&gt;</code> element is the root of the descriptor. The following table lists all of the possible child elements.")]
[System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1057.0")]
[System.SerializableAttribute()]
- [System.Xml.Serialization.XmlTypeAttribute("Project")]
+ [System.Xml.Serialization.XmlTypeAttribute("Model", Namespace="http://maven.apache.org/POM/4.0.0")]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
- [System.Xml.Serialization.XmlRootAttribute("project")]
+ [System.Xml.Serialization.XmlRootAttribute("project", Namespace="http://maven.apache.org/POM/4.0.0")]
public partial class Project
{ Even though we renamed @@ -225,9 +224,9 @@ namespace Java.Interop.Tools.Maven.Models
}
/// <summary>
- /// <para xml:lang="en">Initializes a new instance of the <see cref="Model" /> class.</para>
+ /// <para xml:lang="en">Initializes a new instance of the <see cref="Project" /> class.</para>
/// </summary>
- internal Project()
+ public Project()
{
this._licenses = new System.Collections.ObjectModel.Collection<License>();
this._developers = new System.Collections.ObjectModel.Collection<Developer>(); Your comment mentions @@ -2593,17 +2592,17 @@ namespace Java.Interop.Tools.Maven.Models
[System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1057.0")]
[System.SerializableAttribute()]
- [System.Xml.Serialization.XmlTypeAttribute("ModelProperties", AnonymousType=true)]
+ [System.Xml.Serialization.XmlTypeAttribute("ModelProperties", Namespace="http://maven.apache.org/POM/4.0.0", AnonymousTyp
e=true)]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
public partial class ModelProperties
{
[System.Xml.Serialization.XmlIgnoreAttribute()]
- private System.Collections.ObjectModel.Collection<System.Xml.Linq.XElement> _any;
+ private System.Collections.ObjectModel.Collection<System.Xml.XmlElement> _any;
[System.Xml.Serialization.XmlAnyElementAttribute()]
- public System.Collections.ObjectModel.Collection<System.Xml.Linq.XElement> Any
+ public System.Collections.ObjectModel.Collection<System.Xml.XmlElement> Any
{
get
{ More additions of The only way I could find to get If I checkout https://github.com/mganss/XmlSchemaClassGenerator and build |
@jpobst: I'm also increasingly thinking that my above comments about the differences between my generated This is processing Why should the generated types be
However, this idea mirrors that of "full bindings for I'm currently inclined to "make namespace Java.Interop.Tools.Maven.Models {
// All hand-written
public partial class Project {};
}
namespace Java.Interop.Tools.Maven.Models.v4_0 {
// generated output, using the default names, e.g. `Model` instead of `Project`
public partial class Model {}
// hand-written partial class declaration
public partial class Model : Project {}
} This way, generated "stuff" is off in a separate (versioned) namespace, and we provide a common abstraction that we control in the form of |
6896260
to
77087b7
Compare
Correct, the generated output was manually edited to suit our purposes (and to initially be compatible with https://github.com/Redth/MavenNet/blob/master/MavenNet/Models/PomMavenXsdModels.cs). I think we're overthinking this. In general, we consider JI tool assemblies to be internal API. (For example, we changed the public API of We likely won't update the schema class unless a new version includes new dependency resolution features that we want to support. |
Indeed! That said, even ignoring the API compatibility concerns, I generally prefer that generated stuff remain generatable. To that end, I've added That leaves the question of diff --git a/src/Java.Interop.Tools.Maven/Java.Interop.Tools.Maven.csproj b/src/Java.Interop.Tools.Maven/Java.Interop.Tools.Maven.csproj
index 9c0f50dc..2c9774a6 100644
--- a/src/Java.Interop.Tools.Maven/Java.Interop.Tools.Maven.csproj
+++ b/src/Java.Interop.Tools.Maven/Java.Interop.Tools.Maven.csproj
@@ -22,6 +22,13 @@
<ItemGroup>
<PackageReference Include="Microsoft.SourceLink.GitHub" PrivateAssets="All" />
+ <PackageReference
+ Include="XmlSchemaClassGenerator.Console"
+ Version="2.1.1107"
+ ReferenceOutputAssembly="false"
+ GeneratePathProperty="true"
+ />
</ItemGroup>
+ <Import Project="Java.Interop.Tools.Maven.targets" />
</Project>
diff --git a/src/Java.Interop.Tools.Maven/Java.Interop.Tools.Maven.targets b/src/Java.Interop.Tools.Maven/Java.Interop.Tools.Maven.targets
new file mode 100644
index 00000000..f57c545b
--- /dev/null
+++ b/src/Java.Interop.Tools.Maven/Java.Interop.Tools.Maven.targets
@@ -0,0 +1,20 @@
+<Project>
+ <Target Name="UpdateProjectSchema"
+ AfterTargets="ResolveAssemblyReferences">
+ <PropertyGroup>
+ <_Xscgen>"$(PkgXmlSchemaClassGenerator_Console)/tools/net462/XmlSchemaClassGenerator.Console.exe"</_Xscgen>
+ <MavenXsd Condition=" '$(MavenXsd)' == '' ">maven-4.0.0.xsd</MavenXsd>
+ </PropertyGroup>
+ <ItemGroup>
+ <_XscgenOpt Include="$(MavenXsd)" />
+ <_XscgenOpt Include="--namespace http://maven.apache.org/POM/4.0.0=Java.Interop.Tools.Maven.Models" />
+ <_XscgenOpt Include="--typeNameSubstitute T:Model=Project" />
+ <_XscgenOpt Include="--nullable" />
+ <_XscgenOpt Include="--pcl" />
+ <_XscgenOpt Include="--netCore" />
+ <_XscgenOpt Include="--nullableReferenceAttributes" />
+ <_XscgenOpt Include="-o "Models"" />
+ </ItemGroup>
+ <Exec Command="$(Runtime) $(_Xscgen) @(_XscgenOpt, ' ')" />
+ </Target>
+</Project> This results in a rather sizeable 3348 line diff between @@ -23,13 +22,9 @@ namespace Java.Interop.Tools.Maven.Models
/// <para>The <code>&lt;project&gt;</code> element is the root of the descriptor.
/// The following table lists all of the possible child elements.</para>
/// </summary>
- [System.ComponentModel.DescriptionAttribute(@"3.0.0+ The <code>&lt;project&gt;</code> element is the
root of the descriptor. The following table lists all of the possible child elements. 3.0.0+ The <code>&lt;project&a
mp;gt;</code> element is the root of the descriptor. The following table lists all of the possible child elements.")]
- [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1057.0")]
- [System.SerializableAttribute()]
- [System.Xml.Serialization.XmlTypeAttribute("Project")]
- [System.Diagnostics.DebuggerStepThroughAttribute()]
- [System.ComponentModel.DesignerCategoryAttribute("code")]
- [System.Xml.Serialization.XmlRootAttribute("project")]
+ [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1107.0")]
+ [System.Xml.Serialization.XmlTypeAttribute("Model", Namespace="http://maven.apache.org/POM/4.0.0")]
+ [System.Xml.Serialization.XmlRootAttribute("project", Namespace="http://maven.apache.org/POM/4.0.0")]
public partial class Project
{
@@ -37,7 +32,6 @@ namespace Java.Interop.Tools.Maven.Models
/// <para>4.0.0+</para>
/// <para>Declares to which version of project descriptor this POM conforms.</para>
/// </summary>
- [System.ComponentModel.DescriptionAttribute("4.0.0+ Declares to which version of project descriptor this POM conforms.")]
[System.Diagnostics.CodeAnalysis.AllowNullAttribute()]
[System.Diagnostics.CodeAnalysis.MaybeNullAttribute()]
[System.Xml.Serialization.XmlElementAttribute("modelVersion")]
… *lots* of DescriptionAttribute removals…
@@ -225,9 +201,9 @@ namespace Java.Interop.Tools.Maven.Models
}
/// <summary>
- /// <para xml:lang="en">Initializes a new instance of the <see cref="Model" /> class.</para>
+ /// <para xml:lang="en">Initializes a new instance of the <see cref="Project" /> class.</para>
/// </summary>
- internal Project()
+ public Project()
{
this._licenses = new System.Collections.ObjectModel.Collection<License>();
this._developers = new System.Collections.ObjectModel.Collection<Developer>();
… |
Change: dotnet/java-interop@3436a30...5bca8ad * dotnet/java-interop@5bca8ad6: [build] Automatically add NullableAttributes.cs for netstandard2.0 (dotnet/java-interop#1188) * dotnet/java-interop@45437e22: [Java.Interop] suppress IL3050 with `#pragma` (dotnet/java-interop#1201) * dotnet/java-interop@1c9c8c9c: [Java.Interop.Tools.Maven] Initial commit. (dotnet/java-interop#1179) With dotnet/java-interop@5bca8ad6, all instances of nullable attributes types defined in `NullableAttributes.cs` are now `internal`. However, `Xamarin.Android.Build.Tasks` was consuming the `public` types from its reference to `Java.Interop.Tools.JavaCallableWrappers`, so this change resulted in a build break: MavenExtensions.cs(26,32): error CS0122: 'NotNullWhenAttribute' is inaccessible due to its protection level MamJsonParser.cs(92,43): error CS0122: 'NotNullWhenAttribute' is inaccessible due to its protection level MamJsonParser.cs(92,81): error CS0122: 'NotNullWhenAttribute' is inaccessible due to its protection level We can apply almost the same logic from dotnet/java-interop@5bca8ad6 to `xamarin-android`; the difference is that neither of the `netstandard2.0` projects in xamarin-android that need the attributes have `$(Nullable)='enabled'` and thus our `Condition` fails. (`Xamarin.Android.Build.Tasks.csproj` and `Xamarin.Android.Tools.JavadocImporter.csproj` include `.cs` files from `external/Java.Interop` that have been NRT annotated and thus need the nullable attribute types.) We add `$(Nullable)='annotations'` to the `@(Compile)` which allows one to use NRT syntax but does not emit any NRT warnings since these assemblies have not been converted. We then modify the `NullableAttributes.cs` inclusion logic to additionally key off the `$(Nullable)=annotations` value. Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Jonathan Pobst <[email protected]>
Create a new
Java.Interop.Tools.Maven.dll
assembly that contains functionality for:This assembly is used by dotnet/android#8649 and replaces our previous usage of
MavenNet.dll
.