Skip to content

Commit

Permalink
[One .NET] support latest C# 10 language features (#6118)
Browse files Browse the repository at this point in the history
Fixes: #6075
Fixes: #6076

Context: dotnet/sdk#19521

We need to make two sets of changes for C# 10:

 1. Support ["global usings"][0]. Our .NET 6 templates should have no
    `using` statements at the top of `.cs` files.

 2. Set `$(Nullable)`=enable by default in project templates, i.e.
    enable [C#8 nullable reference types][1] by default.

To test this, our .NET 6 MSBuild tests use `$(Nullable)`=enable and
`$(ImplicitUsings)`=enable by default and do not include `using`
statements in `.cs` files.

I've made a new `MainActivity.cs` for our .NET 6 MSBuild tests.
The "legacy" Xamarin.Android tests will use the original file.

Our default `global using` are:

	global using global::Android.App;
	global using global::Android.Widget;
	global using Bundle = global::Android.OS.Bundle;

The last one is intentionally not bringing in `Android.OS`, because
`Android.OS.Environment` would conflict with `System.Environment`,
and the `System` namespace will be in `@(Using)` by default.

`AutoImport.props` should become:

	<ItemGroup Condition=" '$(TargetPlatformIdentifier)' == 'android' and ('$(ImplicitUsings)' == 'true' or '$(ImplicitUsings)' == 'enable') ">
	  <Using Include="Android.App" />
	  <Using Include="Android.Widget" />
	  <Using Include="Android.OS.Bundle" Alias="Bundle" />
	</ItemGroup>

so that these `using`s are present at the time `.csproj` files are
compiled.

Any templates will add:

	<Nullable>enable</Nullable>
	<ImplicitUsings>enable</ImplicitUsings>

If users want to configure these settings, they can remove
`$(ImplicitUsings)` from the `.csproj` completely, or remove specific
`@(Using)` items:

	<ItemGroup>
	  <Using Remove="Android.App" />
	</ItemGroup>

[0]: https://github.com/dotnet/csharplang/blob/b89d4c934041db923f7238b1427cd5f3ae71ed4b/proposals/csharp-10.0/GlobalUsingDirective.md#global-using-alias-directives
[1]: https://docs.microsoft.com/en-us/dotnet/csharp/nullable-references
  • Loading branch information
jonathanpeppers committed Aug 25, 2021
1 parent 02c3347 commit 7fb7c81
Show file tree
Hide file tree
Showing 20 changed files with 76 additions and 96 deletions.
Original file line number Diff line number Diff line change
@@ -1,14 +1,9 @@
using Android.App;
using Android.OS;
using Android.Runtime;
using Android.Widget;

namespace AndroidApp1
{
[Activity(Label = "@string/app_name", MainLauncher = true)]
public class Activity1 : Activity
{
protected override void OnCreate(Bundle savedInstanceState)
protected override void OnCreate(Bundle? savedInstanceState)
{
base.OnCreate(savedInstanceState);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,7 @@
<TargetFramework>net6.0-android</TargetFramework>
<SupportedOSPlatformVersion>SUPPORTED_OS_PLATFORM_VERSION</SupportedOSPlatformVersion>
<RootNamespace Condition="'$(name)' != '$(name{-VALUE-FORMS-}safe_namespace)'">AndroidBinding1</RootNamespace>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
</Project>
2 changes: 2 additions & 0 deletions src/Microsoft.Android.Templates/android/AndroidApp1.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,7 @@
<SupportedOSPlatformVersion>SUPPORTED_OS_PLATFORM_VERSION</SupportedOSPlatformVersion>
<RootNamespace Condition="'$(name)' != '$(name{-VALUE-FORMS-}safe_namespace)'">AndroidApp1</RootNamespace>
<OutputType>Exe</OutputType>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
</Project>
7 changes: 1 addition & 6 deletions src/Microsoft.Android.Templates/android/MainActivity.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,9 @@
using Android.App;
using Android.OS;
using Android.Runtime;
using Android.Widget;

namespace AndroidApp1
{
[Activity(Label = "@string/app_name", MainLauncher = true)]
public class MainActivity : Activity
{
protected override void OnCreate(Bundle savedInstanceState)
protected override void OnCreate(Bundle? savedInstanceState)
{
base.OnCreate(savedInstanceState);

Expand Down
2 changes: 2 additions & 0 deletions src/Microsoft.Android.Templates/androidlib/AndroidLib1.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,7 @@
<TargetFramework>net6.0-android</TargetFramework>
<SupportedOSPlatformVersion>SUPPORTED_OS_PLATFORM_VERSION</SupportedOSPlatformVersion>
<RootNamespace Condition="'$(name)' != '$(name{-VALUE-FORMS-}safe_namespace)'">AndroidLib1</RootNamespace>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
</Project>
2 changes: 0 additions & 2 deletions src/Microsoft.Android.Templates/androidlib/Class1.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
using System;

namespace AndroidLib1
{
public class Class1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ https://github.com/dotnet/designs/blob/4703666296f5e59964961464c25807c727282cae/
<_DefaultJavaSourceJarPattern>**\*-source.jar;**\*-sources.jar;**\*-src.jar</_DefaultJavaSourceJarPattern>
</PropertyGroup>

<ItemGroup Condition=" '$(TargetPlatformIdentifier)' == 'android' and ('$(ImplicitUsings)' == 'true' or '$(ImplicitUsings)' == 'enable') ">
<Using Include="Android.App" />
<Using Include="Android.Widget" />
<Using Include="Android.OS.Bundle" Alias="Bundle" />
</ItemGroup>

<ItemGroup Condition=" '$(EnableDefaultAndroidItems)' == 'true' ">
<!-- Default Resource file inclusion -->
<!-- https://developer.android.com/guide/topics/resources/providing-resources -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ protected override void CleanupTest ()
{
if (HasDevices && TestContext.CurrentContext.Result.Outcome.Status == TestStatus.Failed &&
TestOutputDirectories.TryGetValue (TestContext.CurrentContext.Test.ID, out string outputDir)) {
Directory.CreateDirectory (outputDir);
string local = Path.Combine (outputDir, "screenshot.png");
string deviceLog = Path.Combine (outputDir, "logcat-failed.log");
string remote = "/data/local/tmp/screenshot.png";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@ public void DotNetNew ([Values ("android", "androidlib", "android-bindinglib")]
Assert.IsTrue (dotnet.New ("android-activity"), "`dotnet new android-activity` should succeed");
Assert.IsTrue (dotnet.New ("android-layout", Path.Combine (dotnet.ProjectDirectory, "Resources", "layout")), "`dotnet new android-layout` should succeed");
Assert.IsTrue (dotnet.Build (), "`dotnet build` should succeed");
dotnet.AssertHasNoWarnings ();
}

[Test]
Expand Down Expand Up @@ -550,7 +551,7 @@ public void SupportedOSPlatformVersion ([Values (21, 30)] int minSdkVersion)
};
// Call AccessibilityTraversalAfter from API level 22
// https://developer.android.com/reference/android/view/View#getAccessibilityTraversalAfter()
proj.MainActivity = proj.DefaultMainActivity.Replace ("button.Click", "button.AccessibilityTraversalAfter.ToString ();\nbutton.Click");
proj.MainActivity = proj.DefaultMainActivity.Replace ("button!.Click", "button!.AccessibilityTraversalAfter.ToString ();\nbutton!.Click");

var dotnet = CreateDotNetBuilder (proj);
Assert.IsTrue (dotnet.Build (), "`dotnet build` should succeed");
Expand Down Expand Up @@ -645,7 +646,7 @@ void CreateEmptyFile (params string [] paths)
public void XamarinLegacySdk ()
{
var proj = new XASdkProject (outputType: "Library") {
Sdk = "Xamarin.Legacy.Sdk/0.1.0-alpha2",
Sdk = "Xamarin.Legacy.Sdk/0.1.0-alpha4",
Sources = {
new AndroidItem.AndroidLibrary ("javaclasses.jar") {
BinaryContent = () => ResourceData.JavaSourceJarTestJar,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ public static class KnownProperties
public const string AndroidFastDeploymentType = "AndroidFastDeploymentType";
public const string AndroidClassParser = "AndroidClassParser";
public const string _AndroidAllowDeltaInstall = "_AndroidAllowDeltaInstall";
public const string Nullable = "Nullable";
public const string ImplicitUsings = "ImplicitUsings";
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ static XASdkProject ()
var assembly = typeof (XASdkProject).Assembly;
using (var sr = new StreamReader (assembly.GetManifestResourceStream ("Xamarin.ProjectTools.Resources.Base.AndroidManifest.xml")))
default_android_manifest = sr.ReadToEnd ();
using (var sr = new StreamReader (assembly.GetManifestResourceStream ("Xamarin.ProjectTools.Resources.Base.MainActivity.cs")))
using (var sr = new StreamReader (assembly.GetManifestResourceStream ("Xamarin.ProjectTools.Resources.DotNet.MainActivity.cs")))
default_main_activity_cs = sr.ReadToEnd ();
using (var sr = new StreamReader (assembly.GetManifestResourceStream ("Xamarin.ProjectTools.Resources.Base.LayoutMain.axml")))
default_layout_main = sr.ReadToEnd ();
Expand Down Expand Up @@ -61,6 +61,8 @@ public XASdkProject (string outputType = "Exe", [CallerMemberName] string packag
JavaPackageName = JavaPackageName ?? PackageName.ToLowerInvariant ();
GlobalPackagesFolder = FileSystemUtils.FindNugetGlobalPackageFolder ();
SetProperty (KnownProperties.OutputType, outputType);
SetProperty (KnownProperties.Nullable, "enable");
SetProperty (KnownProperties.ImplicitUsings, "enable");

// Add relevant Android content to our project without writing it to the .csproj file
if (outputType == "Exe") {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ public class XamarinAndroidApplicationProject : XamarinAndroidCommonProject

static XamarinAndroidApplicationProject ()
{
using (var sr = new StreamReader (typeof(XamarinAndroidApplicationProject).Assembly.GetManifestResourceStream ("Xamarin.ProjectTools.Resources.Base.MainActivity.cs")))
var folder = Builder.UseDotNet ? "DotNet" : "Base";
using (var sr = new StreamReader (typeof(XamarinAndroidApplicationProject).Assembly.GetManifestResourceStream ($"Xamarin.ProjectTools.Resources.{folder}.MainActivity.cs")))
default_main_activity_cs = sr.ReadToEnd ();
using (var sr = new StreamReader (typeof(XamarinAndroidApplicationProject).Assembly.GetManifestResourceStream ("Xamarin.ProjectTools.Resources.Base.MainActivity.fs")))
default_main_activity_fs = sr.ReadToEnd ();
Expand All @@ -40,6 +41,8 @@ public XamarinAndroidApplicationProject (string debugConfigurationName = "Debug"
{
if (Builder.UseDotNet) {
SetProperty (KnownProperties.OutputType, "Exe");
SetProperty (KnownProperties.Nullable, "enable");
SetProperty (KnownProperties.ImplicitUsings, "enable");
SetProperty ("XamarinAndroidSupportSkipVerifyVersions", "True");
SetProperty ("_FastDeploymentDiagnosticLogging", "True");

Expand Down Expand Up @@ -81,7 +84,7 @@ public XamarinAndroidApplicationProject (string debugConfigurationName = "Debug"
}

// it is exposed as public because we may want to slightly modify this.
public string DefaultMainActivity {
public virtual string DefaultMainActivity {
get { return Language == XamarinAndroidProjectLanguage.FSharp ? default_main_activity_fs : default_main_activity_cs; }
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ public class XamarinAndroidLibraryProject : XamarinAndroidCommonProject
public XamarinAndroidLibraryProject (string debugConfigurationName = "Debug", string releaseConfigurationName = "Release")
: base (debugConfigurationName, releaseConfigurationName)
{
if (!Builder.UseDotNet) {
if (Builder.UseDotNet) {
SetProperty (KnownProperties.Nullable, "enable");
SetProperty (KnownProperties.ImplicitUsings, "enable");
} else {
SetProperty ("AndroidApplication", "False");
SetProperty ("AndroidResgenFile", Path.Combine ("Resources", "Resource.designer.cs"));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ public XamarinFormsAndroidApplicationProject (string debugConfigurationName = "D
: base (debugConfigurationName, releaseConfigurationName, packageName)
{
if (Builder.UseDotNet) {
// Don't opt into ImplicitUsings
RemoveProperty (KnownProperties.ImplicitUsings);
PackageReferences.Add (KnownPackages.XamarinForms_4_7_0_1142);
} else {
PackageReferences.Add (KnownPackages.XamarinForms_4_0_0_425677);
Expand Down Expand Up @@ -76,6 +78,8 @@ public XamarinFormsAndroidApplicationProject (string debugConfigurationName = "D
MainPage = MainPage_xaml_cs;
}

public override string DefaultMainActivity => default_main_activity_cs;

public string MainPage { get; set; }

protected virtual string MainPageXaml () => ProcessSourceTemplate (MainPage_xaml);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ static XamarinFormsXASdkProject ()
public XamarinFormsXASdkProject (string outputType = "Exe", [CallerMemberName] string packageName = "")
: base (outputType, packageName)
{
// Don't opt into ImplicitUsings
RemoveProperty (KnownProperties.ImplicitUsings);
PackageReferences.Add (KnownPackages.XamarinForms_4_7_0_1142);

// Workaround for AndroidX, see: https://github.com/xamarin/AndroidSupportComponents/pull/239
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"Size": 2681
},
"assemblies/UnnamedProject.dll": {
"Size": 3186
"Size": 3535
},
"classes.dex": {
"Size": 345240
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
namespace ${ROOT_NAMESPACE}
{
[Android.Runtime.Register ("${JAVA_PACKAGENAME}.MainActivity"), Activity (Label = "${PROJECT_NAME}", MainLauncher = true, Icon = "@drawable/icon")]
public class MainActivity : Activity
{
int count = 1;

protected override void OnCreate (Bundle? bundle)
{
base.OnCreate (bundle);

// Set our view from the "main" layout resource
SetContentView (Resource.Layout.Main);

var button = FindViewById<Button> (Resource.Id.myButton);
button!.Click += delegate {
button.Text = string.Format ("{0} clicks!", count++);
};

//${AFTER_ONCREATE}
}
}
//${AFTER_MAINACTIVITY}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,72 +11,7 @@
<Import Project="..\..\..\..\external\xamarin-android-tools\src\Microsoft.Android.Build.BaseTasks\MSBuildReferences.projitems" />
<ItemGroup>
<Compile Remove="Resources\**\*.cs" />
<EmbeddedResource Include="Resources\AndroidX\Tabbar.xml">
<LogicalName>Xamarin.ProjectTools.Resources.AndroidX.Tabbar.xml</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="Resources\AndroidX\Toolbar.xml">
<LogicalName>Xamarin.ProjectTools.Resources.AndroidX.Toolbar.xml</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="Resources\Wear\LayoutMain.axml">
<LogicalName>Xamarin.ProjectTools.Resources.Wear.LayoutMain.axml</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="Resources\Wear\LayoutRectMain.axml">
<LogicalName>Xamarin.ProjectTools.Resources.Wear.LayoutRectMain.axml</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="Resources\Wear\LayoutRoundMain.axml">
<LogicalName>Xamarin.ProjectTools.Resources.Wear.LayoutRoundMain.axml</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="Resources\Wear\MainActivity.cs">
<LogicalName>Xamarin.ProjectTools.Resources.Wear.MainActivity.cs</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="Resources\Wear\Strings.xml">
<LogicalName>Xamarin.ProjectTools.Resources.Wear.Strings.xml</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="Resources\Base\AndroidManifest.xml">
<LogicalName>Xamarin.ProjectTools.Resources.Base.AndroidManifest.xml</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="Resources\Base\Icon.png">
<LogicalName>Xamarin.ProjectTools.Resources.Base.Icon.png</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="Resources\Base\LayoutMain.axml">
<LogicalName>Xamarin.ProjectTools.Resources.Base.LayoutMain.axml</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="Resources\Base\MainActivity.cs">
<LogicalName>Xamarin.ProjectTools.Resources.Base.MainActivity.cs</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="Resources\Base\MainActivity.fs">
<LogicalName>Xamarin.ProjectTools.Resources.Base.MainActivity.fs</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="Resources\Base\AssemblyInfo.fs">
<LogicalName>Xamarin.ProjectTools.Resources.Base.AssemblyInfo.fs</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="Resources\Base\Image.9.png">
<LogicalName>Xamarin.ProjectTools.Resources.Base.Image.9.png</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="Resources\Base\Image2.9.png">
<LogicalName>Xamarin.ProjectTools.Resources.Base.Image2.9.png</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="Resources\Base\test.keystore">
<LogicalName>Xamarin.ProjectTools.Resources.Base.test.keystore</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="Resources\Base\classes.jar">
<LogicalName>Xamarin.ProjectTools.Resources.Base.classes.jar</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="Resources\Base\custom.aotprofile" />
<EmbeddedResource Include="Resources\Base\BuildReleaseArm64SimpleDotNet.apkdesc" />
<EmbeddedResource Include="Resources\Base\BuildReleaseArm64SimpleLegacy.apkdesc" />
<EmbeddedResource Include="Resources\Base\BuildReleaseArm64XFormsDotNet.apkdesc" />
<EmbeddedResource Include="Resources\Base\BuildReleaseArm64XFormsLegacy.apkdesc" />
<EmbeddedResource Include="Resources\Forms\MainActivity.cs" />
<EmbeddedResource Include="Resources\Forms\App.xaml" />
<EmbeddedResource Include="Resources\Forms\App.xaml.cs" />
<EmbeddedResource Include="Resources\Forms\MainPage.xaml" />
<EmbeddedResource Include="Resources\Forms\MainPage.xaml.cs" />
<EmbeddedResource Include="Resources\Forms\MainPageMaps.xaml" />
<EmbeddedResource Include="Resources\Forms\Tabbar.axml" />
<EmbeddedResource Include="Resources\Forms\Toolbar.axml" />
<EmbeddedResource Include="Resources\Forms\colors.xml" />
<EmbeddedResource Include="Resources\Forms\styles.xml" />
<EmbeddedResource Include="Resources\**\*" />
</ItemGroup>
<ItemGroup>
<Content Include="..\..\..\..\.nuget\NuGet.exe">
Expand Down
15 changes: 10 additions & 5 deletions tests/MSBuildDeviceIntegration/Tests/InstallAndRunTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ public string GetData() {
};
proj.SetAndroidSupportedAbis ("armeabi-v7a", "arm64-v8a", "x86", "x86_64");
proj.SetProperty (proj.ReleaseProperties, "MonoSymbolArchive", "True");
proj.MainActivity = proj.DefaultMainActivity.Replace ("//${AFTER_ONCREATE}",
proj.MainActivity = proj.DefaultMainActivity.Replace ("//${AFTER_FORMS_INIT}",
@" var cl = new Library1.Class1(null);
cl.GetData();
");
Expand Down Expand Up @@ -245,7 +245,7 @@ public string GetData() {
Assert.IsTrue (didParse, $"Unable to parse {proj.TargetSdkVersion} as an int.");
SymbolicateAndAssert (archivePath, logcatPath, new string [] {
Path.Combine (Root, lb.ProjectDirectory, "Class1.cs:12"),
Path.Combine (Root, builder.ProjectDirectory, "MainActivity.cs:33"),
Path.Combine (Root, builder.ProjectDirectory, "MainActivity.cs:23"),
Directory.Exists (builder.BuildOutputDirectory)
? Path.Combine ("src", "Mono.Android", "obj", XABuildPaths.Configuration, "monoandroid10", $"android-{apiLevel}", "mcw", "Android.App.Activity.cs:")
: $"src/Mono.Android/obj/Release/monoandroid10/android-{apiLevel}/mcw/Android.App.Activity.cs:",
Expand Down Expand Up @@ -559,11 +559,16 @@ public override Type BindToType (string assemblyName, string typeName)
return null;
}
}
}").Replace ("using System;", @"using System;
using System.IO;
}");

string usings =
@"using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Serialization.Json;");
using System.Runtime.Serialization.Json;
";
proj.MainActivity = usings + proj.MainActivity;

builder = CreateApkBuilder ();
Assert.IsTrue (builder.Install (proj), "Install should have succeeded.");
ClearAdbLogcat ();
Expand Down
6 changes: 2 additions & 4 deletions tests/MSBuildDeviceIntegration/Tests/XASdkDeployTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,7 @@ public void DotNetDebug ()
AssertCommercialBuild ();
AssertHasDevices ();

XASdkProject proj;
proj = new XASdkProject () {
};
var proj = new XASdkProject ();
proj.SetRuntimeIdentifier (DeviceAbi);
string runtimeId = proj.GetProperty (KnownProperties.RuntimeIdentifier);

Expand All @@ -110,7 +108,7 @@ public void DotNetDebug ()
// setup the debugger
var session = new SoftDebuggerSession ();
session.Breakpoints = new BreakpointStore {
{ Path.Combine (Root, dotnet.ProjectDirectory, "MainActivity.cs"), 19 },
{ Path.Combine (Root, dotnet.ProjectDirectory, "MainActivity.cs"), 10 },
};
session.TargetHitBreakpoint += (sender, e) => {
Console.WriteLine ($"BREAK {e.Type}");
Expand Down

0 comments on commit 7fb7c81

Please sign in to comment.