|
2 | 2 |
|
3 | 3 | This is a collection of tasks you might want to do in this repository, and how to do them.
|
4 | 4 |
|
| 5 | +## Updating Packages |
| 6 | + |
| 7 | +Initially, run: |
| 8 | + |
| 9 | +```bash |
| 10 | +dotnet cake -t:update-config |
| 11 | +``` |
| 12 | + |
| 13 | +Which will lookup Java Maven dependencies and update the `config.json` file |
| 14 | +with the latest versions. |
| 15 | + |
| 16 | +To update other files in the repo, run: |
| 17 | + |
| 18 | +```bash |
| 19 | +dotnet cake utilities.cake -t=generate-component-governance |
| 20 | +dotnet cake utilities.cake -t=generate-namespace-file |
| 21 | +dotnet cake utilities.cake -t=list-artifacts |
| 22 | +``` |
| 23 | + |
| 24 | +For PR descriptions, run: |
| 25 | + |
| 26 | +```bash |
| 27 | +dotnet cake utilities.cake -t=api-diff-markdown-info-pr |
| 28 | +``` |
| 29 | + |
| 30 | +This outputs a bunch of lines like this, you can copy/paste into your PR description: |
| 31 | + |
| 32 | +```md |
| 33 | +======================================== |
| 34 | +api-diff-markdown-info-pr |
| 35 | +======================================== |
| 36 | + 1. `androidx.appcompat:appcompat` - 1.7.0 -> 1.7.1 |
| 37 | + 2. `androidx.appcompat:appcompatresources` - 1.7.0 -> 1.7.1 |
| 38 | + 3. `androidx.autofill:autofill` - 1.1.0 -> 1.3.0 |
| 39 | + 4. `androidx.compose.animation:animation` - 1.7.8 -> 1.8.3 |
| 40 | +``` |
| 41 | + |
5 | 42 | ## Tagging and Releasing
|
6 | 43 |
|
7 | 44 | When ready to release, tag a commit such as:
|
@@ -55,6 +92,156 @@ Then I released the package using the [AndroidX Push NuGet.org
|
55 | 92 | pipeline][androidx-pipeline] and selected the appropriate branch by
|
56 | 93 | selecting a specific build via `Resources > AndroidX`.
|
57 | 94 |
|
| 95 | +## Troubleshooting |
| 96 | + |
| 97 | +This is an example list of problems and how to fix them. |
| 98 | + |
| 99 | +### Example 1 |
| 100 | + |
| 101 | +```log |
| 102 | +generated\androidx.webkit.webkit\obj\Debug\net8.0-android\generated\src\AndroidX.WebKit.ChromiumLibBoundary.IWebViewBuilderBoundaryInterface.cs(202,89): |
| 103 | +error CS0535: 'WebViewBuilderBoundaryInterfaceConfig' does not implement interface member 'IConsumer.Accept(Object?)' |
| 104 | +[generated\androidx.webkit.webkit\androidx.webkit.webkit.csproj] |
| 105 | +``` |
| 106 | + |
| 107 | +In this case, the `WebViewBuilderBoundaryInterfaceConfig` class has: |
| 108 | + |
| 109 | +```csharp |
| 110 | +// Metadata.xml XPath method reference: path="/api/package[@name='org.chromium.support_lib_boundary']/class[@name='WebViewBuilderBoundaryInterface.Config']/method[@name='accept' and count(parameter)=1 and parameter[1][@type='java.util.function.BiConsumer<java.lang.Integer, java.lang.Object>']]" |
| 111 | +[Register ("accept", "(Ljava/util/function/BiConsumer;)V", "GetAccept_Ljava_util_function_BiConsumer_Handler")] |
| 112 | +public virtual unsafe void Accept (global::Java.Util.Functions.IBiConsumer? chromiumConfig) |
| 113 | +``` |
| 114 | + |
| 115 | +Java can change the type of parameters on methods in type inheritance, while C# does *not* allow this. |
| 116 | + |
| 117 | +In C#, the `IConsumer` interface requires: |
| 118 | + |
| 119 | +```csharp |
| 120 | +public virtual unsafe void Accept (global::Java.Lang.Object? chromiumConfig) |
| 121 | +``` |
| 122 | + |
| 123 | +To fix this, I added `source\androidx.webkit\webkit\Additions\AndroidX.WebKit.ChromiumLibBoundary.IWebViewBuilderBoundaryInterface.cs`: |
| 124 | + |
| 125 | +```csharp |
| 126 | +namespace AndroidX.WebKit.ChromiumLibBoundary; |
| 127 | + |
| 128 | +public partial class WebViewBuilderBoundaryInterfaceConfig |
| 129 | +{ |
| 130 | + public virtual unsafe void Accept (Java.Lang.Object? chromiumConfig) |
| 131 | + { |
| 132 | + this.Accept ((Java.Util.Functions.IBiConsumer?) chromiumConfig); |
| 133 | + } |
| 134 | +} |
| 135 | +``` |
| 136 | + |
| 137 | +### Example 2 |
| 138 | + |
| 139 | +```log |
| 140 | +generated\androidx.wear.protolayout.protolayout-expression\obj\Release\net8.0-android\generated\src\AndroidX.Wear.ProtoLayout.Expression.DynamicDataMap.cs(136,44): error CS0111: Type 'DynamicDataMap' already defines a member called 'Get' with the same parameter types |
| 141 | +generated\androidx.wear.protolayout.protolayout-expression\obj\Release\net8.0-android\generated\src\AndroidX.Wear.ProtoLayout.Expression.DynamicDataMap.cs(151,42): error CS0111: Type 'DynamicDataMap' already defines a member called 'Get' with the same parameter types |
| 142 | +generated\androidx.wear.protolayout.protolayout-expression\obj\Release\net8.0-android\generated\src\AndroidX.Wear.ProtoLayout.Expression.DynamicDataMap.cs(166,44): error CS0111: Type 'DynamicDataMap' already defines a member called 'Get' with the same parameter types |
| 143 | +generated\androidx.wear.protolayout.protolayout-expression\obj\Release\net8.0-android\generated\src\AndroidX.Wear.ProtoLayout.Expression.DynamicDataMap.cs(181,25): error CS0111: Type 'DynamicDataMap' already defines a member called 'Get' with the same parameter types |
| 144 | +generated\androidx.wear.protolayout.protolayout-expression\obj\Release\net8.0-android\generated\src\AndroidX.Wear.ProtoLayout.Expression.DynamicDataMap.cs(196,45): error CS0111: Type 'DynamicDataMap' already defines a member called 'Get' with the same parameter types |
| 145 | +generated\androidx.wear.protolayout.protolayout-expression\obj\Release\net8.0-android\generated\src\AndroidX.Wear.ProtoLayout.Expression.DynamicDataMap.cs(211,44): error CS0111: Type 'DynamicDataMap' already defines a member called 'Get' with the same parameter types |
| 146 | +``` |
| 147 | + |
| 148 | +Java you can have multiple methods with the same name, parameters, and *different* return type. C# does not allow this. |
| 149 | + |
| 150 | +So, in this case, I renamed the five methods in C#, so for example: |
| 151 | + |
| 152 | +```csharp |
| 153 | +// Metadata.xml XPath method reference: path="/api/package[@name='androidx.wear.protolayout.expression']/class[@name='DynamicDataMap']/method[@name='get' and count(parameter)=1 and parameter[1][@type='androidx.wear.protolayout.expression.DynamicDataKey<androidx.wear.protolayout.expression.DynamicBuilders.DynamicBool>']]" |
| 154 | +[Register ("get", "(Landroidx/wear/protolayout/expression/DynamicDataKey;)Ljava/lang/Boolean;", "")] |
| 155 | +public unsafe global::Java.Lang.Boolean? Get (global::AndroidX.Wear.ProtoLayout.Expression.DynamicDataKey key) |
| 156 | +{ |
| 157 | + const string __id = "get.(Landroidx/wear/protolayout/expression/DynamicDataKey;)Ljava/lang/Boolean;"; |
| 158 | + try { |
| 159 | + JniArgumentValue* __args = stackalloc JniArgumentValue [1]; |
| 160 | + __args [0] = new JniArgumentValue ((key == null) ? IntPtr.Zero : ((global::Java.Lang.Object) key).Handle); |
| 161 | + var __rm = _members.InstanceMethods.InvokeNonvirtualObjectMethod (__id, this, __args); |
| 162 | + return global::Java.Lang.Object.GetObject<global::Java.Lang.Boolean> (__rm.Handle, JniHandleOwnership.TransferLocalRef); |
| 163 | + } finally { |
| 164 | + global::System.GC.KeepAlive (key); |
| 165 | + } |
| 166 | +} |
| 167 | +``` |
| 168 | + |
| 169 | +You can rename this to `GetBoolean` in `source\androidx.wear.protolayout\protolayout-expression\Transforms\Metadata.xml`, such as: |
| 170 | + |
| 171 | +```xml |
| 172 | +<attr |
| 173 | + path="/api/package[@name='androidx.wear.protolayout.expression']/class[@name='DynamicDataMap']/method[@name='get' and count(parameter)=1 and parameter[1][@type='androidx.wear.protolayout.expression.DynamicDataKey<androidx.wear.protolayout.expression.DynamicBuilders.DynamicBool>']]" |
| 174 | + name="managedName" |
| 175 | + > |
| 176 | + GetBoolean |
| 177 | +</attr> |
| 178 | +``` |
| 179 | + |
| 180 | +I did this for all five methods. |
| 181 | + |
| 182 | +### Example 3 |
| 183 | + |
| 184 | +```log |
| 185 | +generated\com.google.android.libraries.places.places\obj\Release\net8.0-android\generated\src\Xamarin.GoogleAndroid.Libraries.Places.Widget.Listener.IPredictionSelectionListener.cs(136,10): error CS0111: Type 'ErrorEventArgs' already defines a member called 'ErrorEventArgs' with the same parameter types |
| 186 | +``` |
| 187 | + |
| 188 | +This means that more than C# `EventArgs` type is being generated for |
| 189 | +two Java callback methods with the same name, in the same namespace. |
| 190 | + |
| 191 | +To solve this, I added to `source\com.google.android.libraries.places\places\Transforms\Metadata.xml`: |
| 192 | + |
| 193 | +```xml |
| 194 | +<attr |
| 195 | + path="/api/package[@name='com.google.android.libraries.places.widget.listener']/interface[@name='PredictionSelectionListener']/method[@name='onError' and count(parameter)=1 and parameter[1][@type='com.google.android.gms.common.api.Status']]" |
| 196 | + name="argsType" |
| 197 | + > |
| 198 | + PredictionSelectionEventArgs |
| 199 | +</attr> |
| 200 | +``` |
| 201 | + |
| 202 | +### Example 4 |
| 203 | + |
| 204 | +```log |
| 205 | +generated\io.grpc.grpc-api\obj\Debug\net8.0-android\generated\src\Xamarin.Grpc.LongGaugeMetricInstrument.cs(20,84): |
| 206 | +error CS0738: 'LongGaugeMetricInstrument' does not implement interface member 'IMetricInstrument.OptionalLabelKeys'. 'LongGaugeMetricInstrument.OptionalLabelKeys' cannot implement 'IMetricInstrument.OptionalLabelKeys' because it does not have the matching return type of 'IList<string>'. |
| 207 | +generated\io.grpc.grpc-api\obj\Debug\net8.0-android\generated\src\Xamarin.Grpc.LongGaugeMetricInstrument.cs(20,84): |
| 208 | +error CS0738: 'LongGaugeMetricInstrument' does not implement interface member 'IMetricInstrument.RequiredLabelKeys'. 'LongGaugeMetricInstrument.RequiredLabelKeys' cannot implement 'IMetricInstrument.RequiredLabelKeys' because it does not have the matching return type of 'IList<string>'. |
| 209 | +``` |
| 210 | + |
| 211 | +At first, I thought I could do something like: |
| 212 | + |
| 213 | +```xml |
| 214 | +<attr |
| 215 | + path="/api/package[@name='io.grpc']/class[@name='LongGaugeMetricInstrument']/method[@name='getOptionalLabelKeys' and count(parameter)=0]" |
| 216 | + name="managedReturn" |
| 217 | + > |
| 218 | + System.Collections.IList>System.String< |
| 219 | +</attr> |
| 220 | +``` |
| 221 | + |
| 222 | +But this results in the error: |
| 223 | + |
| 224 | +```log |
| 225 | +BINDINGSGENERATOR : error BG0000: System.ArgumentOutOfRangeException: length ('-15') must be a non-negative value. (Parameter 'length') |
| 226 | +``` |
| 227 | + |
| 228 | +So, I'm not sure we support putting generic types in the `managedReturn` value. |
| 229 | + |
| 230 | +So instead, I used a C# additions and an explicit interface implementation: |
| 231 | + |
| 232 | +```csharp |
| 233 | +using System.Collections.Generic; |
| 234 | + |
| 235 | +namespace Xamarin.Grpc; |
| 236 | + |
| 237 | +public partial class LongGaugeMetricInstrument |
| 238 | +{ |
| 239 | + IList<string>? IMetricInstrument.OptionalLabelKeys => (IList<string>?) OptionalLabelKeys; |
| 240 | + |
| 241 | + IList<string>? IMetricInstrument.RequiredLabelKeys => (IList<string>?) RequiredLabelKeys; |
| 242 | +} |
| 243 | +``` |
| 244 | + |
58 | 245 | [1118]: https://github.com/dotnet/android-libraries/pull/1118
|
59 | 246 | [androidx.security]: https://github.com/dotnet/android-libraries/tree/androidx.security
|
60 | 247 | [androidx-pipeline]: https://devdiv.visualstudio.com/DevDiv/_build?definitionId=25324
|
0 commit comments