Skip to content

[Xamarin.Android.Build.Tasks] use copilot to opt into NRT #9770

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

Merged
merged 3 commits into from
Feb 21, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 107 additions & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# Instructions for AIs

This repository is .NET for Android.

This is the main branch targeting .NET 10.

## Nullable Reference Types

When opting C# code into nullable reference types:

* Add `#nullable enable` at the top of the file.

* Don't *ever* use `!` to handle `null`!

* Declare variables non-nullable, and check for `null` at entry points.

* Use `throw new ArgumentNullException (nameof (parameter))` in `netstandard2.0` projects.

* Use `ArgumentNullException.ThrowIfNull (parameter)` in Android projects that will be .NET 10+.

* `[Required]` properties in MSBuild task classes should always be non-nullable with a default value.

* Non-`[Required]` properties should be nullable and have null-checks in C# code using them.

* For MSBuild task properties like:

```csharp
public string NonRequiredProperty { get; set; }
public ITaskItem [] NonRequiredItemGroup { get; set; }

[Output]
public string OutputProperty { get; set; }
[Output]
public ITaskItem [] OutputItemGroup { get; set; }

[Required]
public string RequiredProperty { get; set; }
[Required]
public ITaskItem [] RequiredItemGroup { get; set; }
```

Fix them such as:

```csharp
public string? NonRequiredProperty { get; set; }
public ITaskItem []? NonRequiredItemGroup { get; set; }

[Output]
public string? OutputProperty { get; set; }
[Output]
public ITaskItem []? OutputItemGroup { get; set; }

[Required]
public string RequiredProperty { get; set; } = "";
[Required]
public ITaskItem [] RequiredItemGroup { get; set; } = [];
```

If you see a `string.IsNullOrEmpty()` check:

```csharp
if (!string.IsNullOrEmpty (NonRequiredProperty)) {
// Code here
}
```

Convert this to:

```csharp
if (NonRequiredProperty is { Length: > 0 }) {
// Code here
}
```

## Formatting

C# code uses tabs (not spaces) and the Mono code-formatting style defined in `.editorconfig`

* Your mission is to make diffs as absolutely as small as possible, preserving existing code formatting.

* If you encounter additional spaces or formatting within existing code blocks, LEAVE THEM AS-IS.

* If you encounter code comments, LEAVE THEM AS-IS.

* Place a space prior to any parentheses `(` or `[`

* Use `""` for empty string and *not* `string.Empty`

* Use `[]` for empty arrays and *not* `Array.Empty<T>()`

Examples of properly formatted code:

```csharp
Foo ();
Bar (1, 2, "test");
myarray [0] = 1;

if (someValue) {
// Code here
}

try {
// Code here
} catch (Exception e) {
// Code here
}
```
32 changes: 17 additions & 15 deletions src/Xamarin.Android.Build.Tasks/Tasks/D8.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#nullable enable

using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
using System.Collections.Generic;
Expand All @@ -15,32 +17,32 @@ public class D8 : JavaToolTask
public override string TaskPrefix => "DX8";

[Required]
public string JarPath { get; set; }
public string JarPath { get; set; } = "";

/// <summary>
/// Output for *.dex files. R8 can be invoked for just --main-dex-list-output, so this is not [Required]
/// </summary>
public string OutputDirectory { get; set; }
public string? OutputDirectory { get; set; }

/// <summary>
/// It is loaded to calculate --min-api, which is used by desugaring part to determine which levels of desugaring it performs.
/// </summary>
public string AndroidManifestFile { get; set; }
public string? AndroidManifestFile { get; set; }

// general d8 feature options.
public bool Debug { get; set; }
public bool EnableDesugar { get; set; } = true;

// Java libraries to embed or reference
public string ClassesZip { get; set; }
public string? ClassesZip { get; set; }
[Required]
public string JavaPlatformJarPath { get; set; }
public ITaskItem [] JavaLibrariesToEmbed { get; set; }
public ITaskItem [] AlternativeJarLibrariesToEmbed { get; set; }
public ITaskItem [] JavaLibrariesToReference { get; set; }
public ITaskItem [] MapDiagnostics { get; set; }
public string JavaPlatformJarPath { get; set; } = "";
public ITaskItem []? JavaLibrariesToEmbed { get; set; }
public ITaskItem []? AlternativeJarLibrariesToEmbed { get; set; }
public ITaskItem []? JavaLibrariesToReference { get; set; }
public ITaskItem []? MapDiagnostics { get; set; }

public string ExtraArguments { get; set; }
public string? ExtraArguments { get; set; }

protected override string GenerateCommandLineCommands ()
{
Expand All @@ -55,22 +57,22 @@ protected virtual CommandLineBuilder GetCommandLineBuilder ()
{
var cmd = new CommandLineBuilder ();

if (!string.IsNullOrEmpty (JavaOptions)) {
if (JavaOptions is { Length: > 0 }) {
cmd.AppendSwitch (JavaOptions);
}
cmd.AppendSwitchIfNotNull ("-Xmx", JavaMaximumHeapSize);
cmd.AppendSwitchIfNotNull ("-classpath ", JarPath);
cmd.AppendSwitch (MainClass);

if (!string.IsNullOrEmpty (ExtraArguments))
if (ExtraArguments is { Length: > 0 })
cmd.AppendSwitch (ExtraArguments); // it should contain "--dex".
if (Debug)
cmd.AppendSwitch ("--debug");
else
cmd.AppendSwitch ("--release");

//NOTE: if this is blank, we can omit --min-api in this call
if (!string.IsNullOrEmpty (AndroidManifestFile)) {
if (AndroidManifestFile is { Length: > 0 }) {
var doc = AndroidAppManifest.Load (AndroidManifestFile, MonoAndroidHelper.SupportedVersions);
if (doc.MinSdkVersion.HasValue) {
MinSdkVersion = doc.MinSdkVersion.Value;
Expand All @@ -90,7 +92,7 @@ protected virtual CommandLineBuilder GetCommandLineBuilder ()
}
} else if (JavaLibrariesToEmbed != null) {
Log.LogDebugMessage (" processing ClassesZip, JavaLibrariesToEmbed...");
if (!string.IsNullOrEmpty (ClassesZip) && File.Exists (ClassesZip)) {
if (ClassesZip is { Length: > 0 } && File.Exists (ClassesZip)) {
injars.Add (ClassesZip);
}
foreach (var jar in JavaLibrariesToEmbed) {
Expand All @@ -114,7 +116,7 @@ protected virtual CommandLineBuilder GetCommandLineBuilder ()
foreach (var diagnostic in MapDiagnostics) {
var from = diagnostic.ItemSpec;
var to = diagnostic.GetMetadata ("To");
if (string.IsNullOrEmpty (from) || string.IsNullOrEmpty (to))
if (from is not { Length: > 0 } || to is not { Length: > 0 })
continue;
cmd.AppendSwitch ("--map-diagnostics");
cmd.AppendSwitch (from);
Expand Down
20 changes: 11 additions & 9 deletions src/Xamarin.Android.Build.Tasks/Tasks/R8.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#nullable enable

using System;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
Expand All @@ -17,22 +19,22 @@ public class R8 : D8
public override string TaskPrefix => "R8S";

[Required]
public string AndroidSdkBuildToolsPath { get; set; }
public string AndroidSdkBuildToolsPath { get; set; } = "";

// multidex
public bool EnableMultiDex { get; set; }
public ITaskItem [] CustomMainDexListFiles { get; set; }
public string MultiDexMainDexListFile { get; set; }
public ITaskItem []? CustomMainDexListFiles { get; set; }
public string? MultiDexMainDexListFile { get; set; }

// proguard-like configuration settings
public bool EnableShrinking { get; set; } = true;
public bool IgnoreWarnings { get; set; }
public string AcwMapFile { get; set; }
public string ProguardGeneratedReferenceConfiguration { get; set; }
public string ProguardGeneratedApplicationConfiguration { get; set; }
public string ProguardCommonXamarinConfiguration { get; set; }
public string ProguardMappingFileOutput { get; set; }
public string [] ProguardConfigurationFiles { get; set; }
public string? AcwMapFile { get; set; }
public string? ProguardGeneratedReferenceConfiguration { get; set; }
public string? ProguardGeneratedApplicationConfiguration { get; set; }
public string? ProguardCommonXamarinConfiguration { get; set; }
public string? ProguardMappingFileOutput { get; set; }
public string []? ProguardConfigurationFiles { get; set; }

protected override string MainClass => "com.android.tools.r8.R8";

Expand Down
45 changes: 25 additions & 20 deletions src/Xamarin.Android.Build.Tasks/Tasks/ResolveAndroidTooling.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#nullable enable

using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
using System;
Expand All @@ -20,54 +22,54 @@ public class ResolveAndroidTooling : AndroidTask
{
public override string TaskPrefix => "RAT";

public string TargetPlatformVersion { get; set; }
public string? TargetPlatformVersion { get; set; }

public string AndroidSdkPath { get; set; }
public string? AndroidSdkPath { get; set; }

public string AndroidSdkBuildToolsVersion { get; set; }
public string? AndroidSdkBuildToolsVersion { get; set; }

public string CommandLineToolsVersion { get; set; }
public string? CommandLineToolsVersion { get; set; }

public string ProjectFilePath { get; set; }
public string? ProjectFilePath { get; set; }

public string SequencePointsMode { get; set; }
public string? SequencePointsMode { get; set; }

public bool AotAssemblies { get; set; }

public bool AndroidApplication { get; set; } = true;

[Output]
public string AndroidApiLevel { get; set; }
public string? AndroidApiLevel { get; set; }

[Output]
public string AndroidApiLevelName { get; set; }
public string? AndroidApiLevelName { get; set; }

[Output]
public string AndroidSdkBuildToolsPath { get; set; }
public string? AndroidSdkBuildToolsPath { get; set; }

[Output]
public string AndroidSdkBuildToolsBinPath { get; set; }
public string? AndroidSdkBuildToolsBinPath { get; set; }

[Output]
public string ZipAlignPath { get; set; }
public string? ZipAlignPath { get; set; }

[Output]
public string AndroidSequencePointsMode { get; set; }
public string? AndroidSequencePointsMode { get; set; }

[Output]
public string LintToolPath { get; set; }
public string? LintToolPath { get; set; }

[Output]
public string ApkSignerJar { get; set; }
public string? ApkSignerJar { get; set; }

[Output]
public bool AndroidUseApkSigner { get; set; }

[Output]
public string Aapt2Version { get; set; }
public string? Aapt2Version { get; set; }

[Output]
public string Aapt2ToolPath { get; set; }
public string? Aapt2ToolPath { get; set; }

protected static readonly bool IsWindows = Path.DirectorySeparatorChar == '\\';
protected static readonly string ZipAlign = IsWindows ? "zipalign.exe" : "zipalign";
Expand All @@ -88,7 +90,7 @@ public override bool RunTask ()
string toolsZipAlignPath = Path.Combine (AndroidSdkPath, "tools", ZipAlign);
bool findZipAlign = (string.IsNullOrEmpty (ZipAlignPath) || !Directory.Exists (ZipAlignPath)) && !File.Exists (toolsZipAlignPath);

var lintPaths = MonoAndroidHelper.AndroidSdk.GetCommandLineToolsPaths (CommandLineToolsVersion)
var lintPaths = MonoAndroidHelper.AndroidSdk.GetCommandLineToolsPaths (CommandLineToolsVersion ?? "")
.SelectMany (p => new[]{
p,
Path.Combine (p, "bin"),
Expand All @@ -102,7 +104,7 @@ public override bool RunTask ()
}
}

foreach (var dir in MonoAndroidHelper.AndroidSdk.GetBuildToolsPaths (AndroidSdkBuildToolsVersion)) {
foreach (var dir in MonoAndroidHelper.AndroidSdk.GetBuildToolsPaths (AndroidSdkBuildToolsVersion ?? "")) {
Log.LogDebugMessage ("Trying build-tools path: {0}", dir);
if (dir == null || !Directory.Exists (dir))
continue;
Expand Down Expand Up @@ -222,7 +224,7 @@ bool GetAapt2Version (string aapt2Exe)
// because the path to aapt2 is in the key and the value is a string.
var key = ($"{nameof (ResolveAndroidTooling)}.{nameof (Aapt2Version)}", aapt2Tool);
var cached = BuildEngine4.GetRegisteredTaskObject (key, RegisteredTaskObjectLifetime.AppDomain) as string;
if (!string.IsNullOrEmpty (cached)) {
if (cached is { Length: > 0 }) {
Log.LogDebugMessage ($"Using cached value for {nameof (Aapt2Version)}: {cached}");
Aapt2Version = cached;
return true;
Expand Down Expand Up @@ -254,7 +256,10 @@ bool GetAapt2Version (string aapt2Exe)

protected int GetMaxStableApiLevel ()
{
return MonoAndroidHelper.SupportedVersions.MaxStableVersion.ApiLevel;
var stableVersion = MonoAndroidHelper.SupportedVersions.MaxStableVersion;
if (stableVersion is null)
throw new ArgumentNullException ("MaxStableVersion");
return stableVersion.ApiLevel;
}
}
}
Loading
Loading