Bump external/xamarin-android-tools and fix NRT warnings#11247
Bump external/xamarin-android-tools and fix NRT warnings#11247jonathanpeppers wants to merge 8 commits intomainfrom
Conversation
Bump the submodule from 2fd1240 to ed6aab1 which enables nullable reference types in BaseTasks. Fix all resulting CS8600/CS8602/CS8604 warnings in callers without using the null-forgiving operator. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR updates the external/xamarin-android-tools dependency to a commit that enables nullable reference types (NRT) in BaseTasks, and then adjusts the dotnet/android codebase to address the resulting NRT warnings in build tasks/utilities.
Changes:
- Bump
external/xamarin-android-tools(enables NRT in BaseTasks) and update consumers accordingly. - Add null-handling patterns (null checks/throws, null-coalescing for logging args, null-conditional debug logging) across MSBuild tasks and utilities.
- Tighten some internal build-pipeline assumptions by failing fast when required registered task objects are missing.
Reviewed changes
Copilot reviewed 19 out of 19 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| src/Xamarin.Android.Build.Tasks/Utilities/RtxtParser.cs | Make debug logging tolerant of nullable logger under NRT. |
| src/Xamarin.Android.Build.Tasks/Utilities/FileResourceParser.cs | Adjust debug logging calls for nullable Log under NRT. |
| src/Xamarin.Android.Build.Tasks/Tasks/RewriteMarshalMethods.cs | Add null check for required registered NativeCodeGenState. |
| src/Xamarin.Android.Build.Tasks/Tasks/ResolveAndroidTooling.cs | Coalesce nullable strings when used as message args for coded errors. |
| src/Xamarin.Android.Build.Tasks/Tasks/Legacy/ValidateJavaVersion.cs | Coalesce nullable BuildTools version when used as message arg. |
| src/Xamarin.Android.Build.Tasks/Tasks/Legacy/ResolveAndroidTooling.cs | Coalesce nullable version strings when used as warning args. |
| src/Xamarin.Android.Build.Tasks/Tasks/GetJavaPlatformJar.cs | NRT-driven adjustments around manifest logging / message args. |
| src/Xamarin.Android.Build.Tasks/Tasks/GenerateTypeMappings.cs | Add null check for required registered NativeCodeGenState. |
| src/Xamarin.Android.Build.Tasks/Tasks/GenerateMainAndroidManifest.cs | Add null checks for required registered objects; avoid null output path on cleanup. |
| src/Xamarin.Android.Build.Tasks/Tasks/GenerateLayoutBindings.cs | Make registered widget collection nullable and guard its use. |
| src/Xamarin.Android.Build.Tasks/Tasks/GenerateAdditionalProviderSources.cs | Add null check for required registered NativeCodeGenStateCollection. |
| src/Xamarin.Android.Build.Tasks/Tasks/ConvertCustomView.cs | Coalesce nullable resource directory metadata when logging coded diagnostics. |
| src/Xamarin.Android.Build.Tasks/Tasks/CalculateLayoutCodeBehind.cs | Remove unnecessary null-conditional on non-null widgets. |
| src/Xamarin.Android.Build.Tasks/Tasks/BuildAppBundle.cs | Change debug log formatting while addressing NRT warnings (but see review comment). |
| src/Xamarin.Android.Build.Tasks/Tasks/AndroidWarning.cs | Coalesce nullable resource string / format args under NRT. |
| src/Xamarin.Android.Build.Tasks/Tasks/AndroidError.cs | Coalesce nullable resource string / format args under NRT. |
| src/Xamarin.Android.Build.Tasks/Tasks/AndroidDotnetToolTask.cs | Treat cached mono path as nullable under NRT. |
| src/Xamarin.Android.Build.Tasks/Tasks/Aapt2Compile.cs | Ensure non-null enumerable passed to async processing under NRT. |
Comments suppressed due to low confidence (2)
src/Xamarin.Android.Build.Tasks/Tasks/GenerateLayoutBindings.cs:213
- After the
widgets is null || widgets.Count == 0guard,widgetsis known to be non-null, but later the code still uses the null-forgiving operator (widgets!) when callingGenerateSource. Since this PR aims to fix NRT warnings without!, please refactor to avoid the null-forgiving operator here (e.g., by passing the non-nullwidgetsvariable directly after the guard).
string collectionKey;
if (!GetRequiredMetadata (item, CalculateLayoutCodeBehind.WidgetCollectionKeyMetadata, out collectionKey))
return;
ICollection<LayoutWidget>? widgets = BuildEngine4.GetRegisteredTaskObjectAssemblyLocal<ICollection<LayoutWidget>> (ProjectSpecificTaskObjectKey (collectionKey), RegisteredTaskObjectLifetime.Build);
if (widgets is null || widgets.Count == 0) {
string inputPaths = String.Join ("; ", resourceItems.Select (i => i.ItemSpec));
LogCodedWarning ("XA4222", Properties.Resources.XA4222, inputPaths);
return;
}
src/Xamarin.Android.Build.Tasks/Utilities/FileResourceParser.cs:76
ResourceParser.Logis nullable, but this method now mixesLog?.LogDebugMessage(...)with later unconditionalLog.LogDebugMessage(...)calls in the same method body. IfLogis required for this parser, consider validatingLogis non-null once at the start ofParse()(and then usingLog.consistently) instead of sprinkling null-conditional calls (which can also leave CS8602 warnings on the remainingLog.usages).
public IList<R> Parse (string resourceDirectory, IEnumerable<string> additionalResourceDirectories, IEnumerable<string> aarLibraries, Dictionary<string, string> resourceMap)
{
Log?.LogDebugMessage ($"Parsing Directory {resourceDirectory}");
publicXml = LoadPublicXml ();
var result = new List<R> ();
Dictionary<string, ICollection<R>> resources = new Dictionary<string, ICollection<R>> ();
foreach (var knownType in RtxtParser.knownTypes) {
if (knownType == "styleable") {
resources.Add (knownType, new List<R> ());
continue;
}
resources.Add (knownType, new SortedSet<R> (new RComparer ()));
}
foreach (var dir in Directory.EnumerateDirectories (resourceDirectory, "*", SearchOption.TopDirectoryOnly)) {
foreach (var file in Directory.EnumerateFiles (dir, "*.*", SearchOption.AllDirectories)) {
ProcessResourceFile (file, resources);
}
}
foreach (var dir in additionalResourceDirectories ?? []) {
Log.LogDebugMessage ($"Processing Directory {dir}");
if (Directory.Exists (dir)) {
foreach (var file in Directory.EnumerateFiles (dir, "*.*", SearchOption.AllDirectories)) {
ProcessResourceFile (file, resources);
}
} else {
Log.LogDebugMessage ($"Skipping non-existent directory: {dir}");
}
Use `required` on ResourceParser.Log so it must be set via object initializer, eliminating the need for Log?. Pass log and map as parameters to RtxtParser.ProcessRtxtFile instead of storing them in nullable fields, removing the null-conditional calls and the runtime null check. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The `required` keyword needs CompilerFeatureRequiredAttribute which is only available in net7.0+, not netstandard2.0. Use a constructor parameter for ResourceParser.Log instead, and chain it through FileResourceParser, JavaResourceParser, and ManagedResourceParser. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Move Parser field initialization into the constructor that takes TaskLoggingHelper, removing the old parameterless constructor. Also remove Log! usages now that Log is guaranteed non-null. Add missing using System in RewriteMarshalMethods. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
/review |
|
✅ Android PR Reviewer completed successfully! |
There was a problem hiding this comment.
🤖 Review Summary
Verdict: ✅ LGTM (with minor items)
Overall this is a clean, well-structured NRT fix PR. The ResourceParser refactoring (property → constructor injection for Log) is a good improvement, the removal of ! null-forgiving operators in JavaResourceParser is correct, and the RtxtParser cleanup eliminating instance state in favor of method parameters is cleaner.
Issue counts:
⚠️ 1 warning — behavioral change inConvertCustomView.cswhere?? ""interacts withFixupResourceFilename'sStartsWithlogic- 💡 2 suggestions — fallback message for missing resources, minor formatting
Positive callouts:
- The
nativeCodeGenStates is null→throw InvalidOperationExceptionpattern across multiple tasks is the right approach — fail fast on internal errors rather than letting aNullReferenceExceptioncrash later - Good catch removing the unnecessary
?.inCalculateLayoutCodeBehind(widgets?.Count→widgets.Count) andGetJavaPlatformJar(min_sdk?.Value→min_sdk.Value) inside null-guarded blocks - The
ResourceParserconstructor injection pattern properly enforces theLogdependency at construction time, which is much better than the previous mutable property
CI: Public checks passing ✅
Generated by Android PR Reviewer for issue #11247 · ● 12.1M
| switch (level) { | ||
| case TraceLevel.Error: | ||
| Log.FixupResourceFilenameAndLogCodedError ("XA1002", message, file, resdir?.ItemSpec, resource_name_case_map); | ||
| Log.FixupResourceFilenameAndLogCodedError ("XA1002", message, file, resdir?.ItemSpec ?? "", resource_name_case_map); |
There was a problem hiding this comment.
🤖 resdir?.ItemSpec ?? "" changes behavior vs. the pre-NRT code.
Previously, when resdir was null, null was passed to FixupResourceFilename, which has a if (resourceDir != null && ...) guard that skipped the fixup and kept the raw file path. Now, passing "" causes StartsWith("") to always be true, so the fixup path executes unconditionally — stripping leading path separators and prepending "Resources/", mangling the filename in the error message.
Consider guarding the call instead:
case TraceLevel.Error:
if (resdir != null)
Log.FixupResourceFilenameAndLogCodedError ("XA1002", message, file, resdir.ItemSpec, resource_name_case_map);
else
Log.LogCodedError ("XA1002", file: file, lineNumber: 0, message: message);
break;Same issue on line 62 for the TraceLevel.Warning case.
Rule: NRT fix must preserve runtime behavior
There was a problem hiding this comment.
Good catch — fixed. Now guarding on resdir != null and falling back to Log.LogCodedError/Log.LogCodedWarning directly when there is no resource directory, preserving the original behavior.
| public ManagedResourceParser (TaskLoggingHelper log) : base (log) { } | ||
| class CompareTuple : IComparer<(int Key, CodeMemberField Value)> |
There was a problem hiding this comment.
🤖 💡 Formatting — Missing blank line between the new constructor and the CompareTuple nested class declaration.
Rule: Minimal diffs / consistent formatting
There was a problem hiding this comment.
Fixed, added the blank line.
| Code, | ||
| Properties.Resources.ResourceManager.GetString (ResourceName, Properties.Resources.Culture), | ||
| FormatArguments | ||
| Properties.Resources.ResourceManager.GetString (ResourceName, Properties.Resources.Culture) ?? "", |
There was a problem hiding this comment.
🤖 💡 Error handling — GetString() ?? "" silently turns a missing/mistyped ResourceName into an empty error message, which would be very hard to diagnose.
Consider a more informative fallback, e.g.:
var message = Properties.Resources.ResourceManager.GetString (ResourceName, Properties.Resources.Culture)
?? $"(Missing resource: {ResourceName})";Same applies to AndroidWarning.cs.
Rule: Log messages must have context (Postmortem #6)
There was a problem hiding this comment.
Good idea — changed both AndroidError and AndroidWarning to use ?? $"(Missing resource: {ResourceName})" so a misconfigured resource name is diagnosable.
- ConvertCustomView: guard resdir null check properly instead of using ?? "" which changes FixupResourceFilename behavior - AndroidError/AndroidWarning: use informative fallback message for missing resources instead of empty string - ManagedResourceParser: add missing blank line after constructor Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Bumps external/xamarin-android-tools from
2fd1240toed6aab1, which enables nullable reference types inBaseTasks.Fixes all resulting CS8600/CS8602/CS8604 NRT warnings across 18 files without using
!(null-forgiving operator).Key changes in xamarin-android-tools
ed6aab1[BaseTasks] Enable nullable reference types ([AndroidClientHandler] Prevent orphaned thread in DoProcessRequest #344)4fd9ae0[Tools.AndroidSdk] Remove unusedkey_namevariables (use ant version 1.9.8 as 1.9.7 is no longer available at ibiblio #345)3bfeba6[copilot] Add/reviewGitHub agentic workflow (Add support for Round Icon #346)NRT fix patterns used
value ?? ""for nullable strings passed toparams object[]GetRegisteredTaskObjectAssemblyLocal/UnregisterTaskObjectAssemblyLocalresults that must existLog?.LogDebugMessage(...)for nullableTaskLoggingHelper?.: e.g.min_sdk?.Value->min_sdk.Valueinsideif (min_sdk != null)blocksstring? mono = ...for nullable return typesSupersedes #11243