Skip to content

Bump external/xamarin-android-tools and fix NRT warnings#11247

Open
jonathanpeppers wants to merge 8 commits intomainfrom
jonathanpeppers/bump-xamarin-android-tools-nrt-fixes
Open

Bump external/xamarin-android-tools and fix NRT warnings#11247
jonathanpeppers wants to merge 8 commits intomainfrom
jonathanpeppers/bump-xamarin-android-tools-nrt-fixes

Conversation

@jonathanpeppers
Copy link
Copy Markdown
Member

Bumps external/xamarin-android-tools from 2fd1240 to ed6aab1, which enables nullable reference types in BaseTasks.

Fixes all resulting CS8600/CS8602/CS8604 NRT warnings across 18 files without using ! (null-forgiving operator).

Key changes in xamarin-android-tools

NRT fix patterns used

  • Null-coalescing for params args: value ?? "" for nullable strings passed to params object[]
  • Null checks + throw: for GetRegisteredTaskObjectAssemblyLocal/UnregisterTaskObjectAssemblyLocal results that must exist
  • Null-conditional calls: Log?.LogDebugMessage(...) for nullable TaskLoggingHelper
  • Remove unnecessary ?.: e.g. min_sdk?.Value -> min_sdk.Value inside if (min_sdk != null) blocks
  • Nullable variable declarations: e.g. string? mono = ... for nullable return types

Supersedes #11243

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>
Copilot AI review requested due to automatic review settings April 29, 2026 13:41
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 == 0 guard, widgets is known to be non-null, but later the code still uses the null-forgiving operator (widgets!) when calling GenerateSource. Since this PR aims to fix NRT warnings without !, please refactor to avoid the null-forgiving operator here (e.g., by passing the non-null widgets variable 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.Log is nullable, but this method now mixes Log?.LogDebugMessage(...) with later unconditional Log.LogDebugMessage(...) calls in the same method body. If Log is required for this parser, consider validating Log is non-null once at the start of Parse() (and then using Log. consistently) instead of sprinkling null-conditional calls (which can also leave CS8602 warnings on the remaining Log. 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}");
				}

Comment thread src/Xamarin.Android.Build.Tasks/Tasks/GetJavaPlatformJar.cs
Comment thread src/Xamarin.Android.Build.Tasks/Tasks/AndroidWarning.cs
Comment thread src/Xamarin.Android.Build.Tasks/Tasks/AndroidError.cs
Comment thread src/Xamarin.Android.Build.Tasks/Tasks/BuildAppBundle.cs
@jonathanpeppers jonathanpeppers marked this pull request as draft April 29, 2026 14:11
jonathanpeppers and others added 5 commits April 29, 2026 09:18
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>
@jonathanpeppers jonathanpeppers marked this pull request as ready for review April 30, 2026 13:52
@jonathanpeppers
Copy link
Copy Markdown
Member Author

/review

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 30, 2026

Android PR Reviewer completed successfully!

Copy link
Copy Markdown

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 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 in ConvertCustomView.cs where ?? "" interacts with FixupResourceFilename's StartsWith logic
  • 💡 2 suggestions — fallback message for missing resources, minor formatting

Positive callouts:

  • The nativeCodeGenStates is nullthrow InvalidOperationException pattern across multiple tasks is the right approach — fail fast on internal errors rather than letting a NullReferenceException crash later
  • Good catch removing the unnecessary ?. in CalculateLayoutCodeBehind (widgets?.Countwidgets.Count) and GetJavaPlatformJar (min_sdk?.Valuemin_sdk.Value) inside null-guarded blocks
  • The ResourceParser constructor injection pattern properly enforces the Log dependency 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);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 ⚠️ Correctnessresdir?.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

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines +22 to 23
public ManagedResourceParser (TaskLoggingHelper log) : base (log) { }
class CompareTuple : IComparer<(int Key, CodeMemberField Value)>
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 💡 Formatting — Missing blank line between the new constructor and the CompareTuple nested class declaration.

Rule: Minimal diffs / consistent formatting

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed, added the blank line.

Code,
Properties.Resources.ResourceManager.GetString (ResourceName, Properties.Resources.Culture),
FormatArguments
Properties.Resources.ResourceManager.GetString (ResourceName, Properties.Resources.Culture) ?? "",
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 💡 Error handlingGetString() ?? "" 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)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants