-
Notifications
You must be signed in to change notification settings - Fork 52
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
Allow for bulk namespace changes #727
Comments
@jonpryor @moljac @mattleibow @Redth Was thinking about something like this and could use some feedback from more experienced binders than myself! |
I think "something" should be required; I can't think of any deliberate scenario where you'd want to have Thus, I think the value for
A problem with this dichotomy is that a nested package-part of |
If we drop the <package-part java="androidx" managed="AndroidX" />
<package-part java="accessibilityservice" managed="AccessibilityService" />
<package-part java="core" managed="Core" />
<package-part java="io" managed="IO" />
<!-- … --> By only matching complete strings at:
We can avoid the
|
What if you also use a <ns-replace value="androidx." path="/package/path/to/type[something]">AndroidX.</ns-replace> |
@jonpryor @moljac @mattleibow @Redth Simplified the proposal based on feedback, and changed from specified with |
If you add support for this in MSBuild, should it just be so flexible that you don't even need For: <attr path="/api/package[@name='androidx.core.accessibilityservice']" name="managedName">AndroidX.Core.AccessibilityService</attr> You could put: <AndroidMetadataAttr Include="/api/package[@name='androidx.core.accessibilityservice']" ManagedName="AndroidX.Core.AccessibilityService" /> Which you could also create "shorthand" for (this would be the equivalent): <AndroidNamespaceReplacement Include="androidx.core.accessibilityservice" Replacement="AndroidX.Core.AccessibilityService" /> It doesn't seem like it's worth inventing a new pattern matching syntax? Should you still use xpath as before, but we create some "shortcuts"? The only drawback is that incremental builds might be worse when |
To address the current proposal:
If we need to do this, we should instead use Regular Expression characters, <AndroidNamespaceReplacement Include='^Androidx' Replacement='Xamarin.AndroidX' /> |
@jonathanpeppers suggested using the
The conceptual problem with this approach is that it implies an exact match when an inexact match is desirable: Even ignoring the implementation complexities, it doesn't look like what you want: it looks like an exact match, but we want an inexact match. I don't think using the |
On Correct, this feature should not be implemented as metadata, for both performance and desired behavior reasons. However, it could be passed to ie, it could look something like: <ns-replace source='androidx' replacement='AndroidX' /> Then we could extend our MSBuild support to also allow for I'm torn if that's actually a good idea or not. 😜 |
I'm up for better ideas for than square brackets, but I'm not a fan of using RegEx characters. I do not think they have any meaning to our average target user. (I've used plenty of regular expressions in my career but did not know those (or any other regex) characters.) My thought with square brackets was some familiarity with "ranges", even though this is clearly not the same mental construct. NuGet versioning: <PackageReference Include="ExamplePackage" Version="[1,3)" /> C# ranges:
After a little more thought, I think maybe periods are my current preferred option: Start of namespace: <AndroidNamespaceReplacement Include='Androidx.' Replacement='Xamarin.AndroidX' /> End of namespace: <AndroidNamespaceReplacement Include='.Androidx' Replacement='Xamarin.AndroidX' /> |
My philosophy is not to be invasive, so I used the fact that
You will see in most of my bindings I did MsBuild custom task delivered via NuGet to create such and I "force" developer to verify and change more appropriate .NET namespace name: I reused our tooling and generated intermediate files ( This improves bindings productivity a lot, by not needing to collect nodes from The general idea was:
Similar nuget was for decompiling support ( Focusing on RegEx imposes requirement that the binder is RegEx expert. Reducing number of potential users (binders). The most time consuming steps are
here come list of my planned nice-to-haves... |
Fair.
I like this idea. |
This is almost contradictory? What is the meaning of the word "metadata" if it doesn't mean "stuff within So… back to the beginning. What is the intent? To simplify package renames, in particular so that nested package names are automatically renamed when a parent package name is changed. What's the benefit of doing this as an Benefits to
Benefits to
If we thought a "Metadata-less binding project" was possible in the "near-term", I'd be personally more inclined toward an item group. Otherwise, I'm more inclined to stick it into |
@moljac: i'm not sure if you're expressing support for using an MSBuild item group or for using Metadata.xml…. Please clarify? |
TL&DR: expressing support for
I mentioned MsBuild combined with NuGet as sample od delivering/shipping new features (improvements) and being minimally intrusive. Drawback could be inner-loop performance (nuget downloads, restore, performance hits for MSBuild custom tasks) |
Yeah, that wasn't worded real well, and requires knowledge of how our metadata system works. I meant it will not use the current system of using XPath to manipulate an XML representation of an api ( It will instead be applied via a new, different processor built just for this purpose. However, the user-specified configuration for this process could be given via a |
Fixes: #727 Today, when `generator` creates a .NET `namespace` from a Java `package`, it applies a simple PascalCase transformation to make the namespace match established .NET naming standards. For example. `package android.database` becomes `namespace Android.Database`. However there are a few scenarios that this is not a good fit for: 1. Word phrases where upper-casing only the first letter is not desirable, e.g. `package androidx` to `namespace AndroidX`. 2. When Java package names are longer than the desired C# namespaces; `com.` is a common Java package prefix, but isn't common in C#: `package com.google.android.material.animation` should become `namespace Google.Android.Material.Animation`. Both of these scenarios can require many repeated `metadata` lines [to fix][0]: <attr path="/api/package[@name='androidx.core.accessibilityservice']" name="managedName">AndroidX.Core.AccessibilityService</attr> <attr path="/api/package[@name='androidx.core.app']" name="managedName">AndroidX.Core.App</attr> … Improve support for this scenario by adding a support for a new `<ns-replace/>` element to `Metadata.xml` transform files. (We may also add support for an `@(AndroidNamespaceReplacement)` item group as suggested in #727 in the future.) <metadata> <ns-replace source='Androidx' replacement='Xamarin.AndroidX' /> <ns-replace source='.Androidx.' replacement='Xamarin.AndroidX' /> <ns-replace source='Com' replacement='' /> <ns-replace source='Com.Google.' replacement='Google' /> <ns-replace source='.Compose' replacement='ComposeUI' /> </metadata> The `//ns-replace/@source` attribute is a Java [PackageName][1] to replace. Note: the Java PackageName grammar is: > *PackageName*: > - *Identifier* > - *PackageName* `.` *Identifier* The `//ns-replace` values *do not* override the [managedName][2] attribute if already specified within metadata. The `//ns-replace/@source` attribute: * Specifies a "match"; when it matches, instances of `@source` are replaced with `@replacement`. * Is interpreted in a *case-insensitive* manner; `Androidx` matches `androidx` and `AndroidX` and `ANDROIDX`. * Only matches on full [Identifier][3]s and [PackageName][1]s; <ns-replace source='Com' replacement='' /> Matches `Com.Google.Library`, but not `Common.Google.Library` or `Google.Imaging.Dicom`. * Multiple "dotted" Identifiers may be used, and all may match *anywhere*, *in order*, in the Java package name. <ns-replace source='Com.Google' replacement='Google' /> will match `com.google.library` and `example.com.google`, but won't match `Common.Google` or `Com.Googles`. * *May start with* a `.`, which means that `//ns-replace/@source` is only matched at the *end* of a Java package name: <ns-replace source='.Compose' replacement='ComposeUI' /> will match `com.google.androidx.compose`, but not `com.google.androidx.compose.writer`. * *May end with* a `.`, which means that `//ns-replace/@source` is only matched at the *beginning* of the package name: <ns-replace source='Androidx.' replacement='Xamarin.AndroidX' /> matches `androidx.core`, but not `com.square.okhttp.androidx`. * May both begin and end with a `.`, which means that only exact (case insensitive) Java package names are matched: <ns-replace source='.Androidx.' replacement='Xamarin.AndroidX' /> matches `androidx`, but not `com.google.androidx.core`. Additionally, duplicate `//ns-replace/@source` values are *ignored*. `//ns-replace` elements are processed "in order" for a given `Metadata.xml` file, but the ordering of `generator --fixup` files / `@(TransformFile)`s is unspecified. Multiple replacements may affect a single namespace: <ns-replace source='Androidx' replacement='Xamarin.AndroidX' /> <ns-replace source='View' replacement='Views' /> changes `Androidx.View` to `Xamarin.AndroidX.Views`. [0]: https://github.com/xamarin/AndroidX/blob/f97553ff428f9b6ea754f173567d220048245a16/source/androidx.core/core/Transforms/Metadata.Namespaces.xml#L10-L34 [1]: https://docs.oracle.com/javase/specs/jls/se8/html/jls-6.html#jls-6.5 [2]: https://docs.microsoft.com/en-us/xamarin/android/platform/binding-java-library/customizing-bindings/java-bindings-metadata#managedname [3]: https://docs.oracle.com/javase/specs/jls/se8/html/jls-3.html#jls-Identifier
Context: dotnet/java-interop#727 Context: dotnet/java-interop@0c90cf5 dotnet/java-interop@0c90cf5c added support for an `<ns-replace/>` element within `Metadata.xml`, which makes it easier to replace a Java package name with an alternate .NET namespace name. Add support for an `@(AndroidNamespaceReplacement)` item group, which makes it possible to express `<ns-replace/>` within MSBuild: <ItemGroup> <AndroidNamespaceReplacement Include='Androidx' Replacement='AndroidX' /> <AndroidNamespaceReplacement Include='Com' Replacement='' /> <AndroidNamespaceReplacement Include='Com.Google.' Replacement='Google' /> </ItemGroup> The `@(AndroidNamespaceReplacement)` item group: * Provides a "match" and "replacement"; `%(Include)` specifies the match for a Java package name, and when it matches, the contents of `%(Include)` are replaced with `%(Replacement)`. * `%(Include)` is interpreted in a *case-insensitive* manner; `Androidx` matches `androidx` and `AndroidX` and `ANDROIDX`. * `%(Include)` only matches on full [Identifier][0]s and [PackageName][1]s; <AndroidNamespaceReplacement Include="Com" Replacement="" /> Matches `Com.Google.Library`, but not `Common.Google.Library` or `Google.Imaging.Dicom`. * `%(Include)` may contain multiple "dotted" Identifiers, and all may match *anywhere*, *in order*, in the Java package name. <AndroidNamespaceReplacement Include="Com.Google" Replacement="Google" /> will match `com.google.library` and `example.com.google`, but won't match `Common.Google` or `Com.Googles`. * `%(Include)` *may start with* a `.`, which means that `%(Include)` is only matched at the *end* of a Java package name: <AndroidNamespaceReplacement Include=".Compose" Replacement="ComposeUI" /> will match `com.google.androidx.compose`, but not `com.google.androidx.compose.writer`. * `%(Include)` *may end with* a `.`, which means that `%(Include)` is only matched at the *beginning* of the package name: <AndroidNamespaceReplacement Include="Androidx." Replacement="Xamarin.AndroidX" /> matches `androidx.core`, but not `com.square.okhttp.androidx`. * `%(Include)` may both begin and end with a `.`, which means that only exact (case insensitive) Java package names are matched: <AndroidNamespaceReplacement Include=".Androidx." Replacement="Xamarin.AndroidX" /> matches `androidx`, but not `com.google.androidx.core`. Duplicate `%(AndroidNamespaceReplacement.Include)` values are *ignored*, even if `%(Replacement)` differs. Order of evaluation of `@(AndroidNamespaceReplacement)` is *unspecified*. The `%(AndroidNamespaceReplacement.Replacement)` item metadata *must* be provided, otherwise an XA4233 error is emitted; given: <AndroidNamespaceReplacement Include="NoReplacement" /> then the following error is produced: error XA4233: The <AndroidNamespaceReplacement> for 'NoReplacement' does not specify a 'Replacement' attribute. `%(AndroidNamespaceReplacement.Replacement)` may be the empty string. The `@(AndroidNamespaceReplacement)` item group is converted into corresponding `<ns-replace/>` elements, and placed into a generated `msbuild-metadata.xml` file which is provided to `generator`, e.g.: <metadata> <ns-replace source='Androidx' replacement='AndroidX' /> <ns-replace source='Com' replacement='' /> <ns-replace source='Com.Google.' replacement='Google' /> </metadata> [0]: https://docs.oracle.com/javase/specs/jls/se8/html/jls-3.html#jls-Identifier [1]: https://docs.oracle.com/javase/specs/jls/se8/html/jls-6.html#jls-6.5
Today, when
generator
creates a .NETnamespace
from a Javapackage
, it applies a simple pascal case transformation to make the namespace match established .NET naming standards.For example:
package android.database
becomesnamespace Android.Database
.However there are a few scenarios that this is not a good fit for.
(1) Words where Pascal case is not desired:
androidx
->AndroidX
(2) Java package names are often longer than C# namespaces:
com.google.android.material.animation
->Google.Android.Material.Animation
.Both of these scenarios can lead to many repeated
metadata
lines to fix:(Source)
Proposal
To help these scenarios, we could introduce a new MSBuild item that would allow simple replacements. Using MSBuild gives users the flexibility of MSBuild like conditionally specifying replacements based on target framework or importing it to multiple projects via
Directory.Build.targets
. Additionally it might spare some users from the complexity ofmetadata
.Examples:
Implementation Notes
These replacements would only be run for
<package>
elements that do not specify a@managedName
attribute. If you use@managedName
you are opting to provide the exact name, we will not process it further.Unlike unused metadata, these replacement will not raise a warning if they are unused.
Case Sensitivity
Replacements take place after the automatic Pascal case transform, but the compare is case-insensitive.
Thus, both of the following are equivalent:
Word Bounds
Replacements take place only on full words (namespace parts).
Thus,
Matches matches
Com.Google.Library
, but notCommon.Google.Library
orGoogle.Imaging.Dicom
.Multiple full words can be used:
Word Position
The word part match can be constrained to the beginning or end of a namespace by appending a
.
or prepending a.
, respectively.matches
Androidx.Core
, but notSquare.OkHttp.Androidx
.Similarly,
matches
Google.AndroidX.Compose
, but notGoogle.Compose.Writer
.Replacement Order
Replacements run in the order specified by the
<ItemGroup>
, however adding to this group at different times may result in an unintended order.Replacements are run sequentially, and multiple replacements may affect a single namespace.
changes
Androidx.View
toXamarin.AndroidX.Views
.The text was updated successfully, but these errors were encountered: