Skip to content

Commit

Permalink
[One .NET] support latest C# 10 language features
Browse files Browse the repository at this point in the history
Fixes: #6075
Fixes: #6076
Context: xamarin/xamarin-macios#12173

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

1. Support "global usings". Our .NET 6 templates should have no
   `using` statements at the top of `.cs` files.
2. Use `$(Nullable)` `enable` by default in project templates.

To test this, our .NET 6 MSBuild tests use `Nullable=enable` by
default and do no include `using` statements.

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`.

I also had to write our own target to generate the using for `Bundle`,
because the dotnet/sdk always prepends `global::`:

https://github.com/dotnet/sdk/blob/86946f52cd012cefd811b25287a8da034bf082a3/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.GenerateImplicitNamespaceImports.targets#L46
  • Loading branch information
jonathanpeppers committed Aug 4, 2021
1 parent babd603 commit 8004dd1
Show file tree
Hide file tree
Showing 18 changed files with 101 additions and 85 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,6 @@
<TargetFramework>net6.0-android</TargetFramework>
<SupportedOSPlatformVersion>SUPPORTED_OS_PLATFORM_VERSION</SupportedOSPlatformVersion>
<RootNamespace Condition="'$(name)' != '$(name{-VALUE-FORMS-}safe_namespace)'">AndroidBinding1</RootNamespace>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
1 change: 1 addition & 0 deletions src/Microsoft.Android.Templates/android/AndroidApp1.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@
<SupportedOSPlatformVersion>SUPPORTED_OS_PLATFORM_VERSION</SupportedOSPlatformVersion>
<RootNamespace Condition="'$(name)' != '$(name{-VALUE-FORMS-}safe_namespace)'">AndroidApp1</RootNamespace>
<OutputType>Exe</OutputType>
<Nullable>enable</Nullable>
</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
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
<TargetFramework>net6.0-android</TargetFramework>
<SupportedOSPlatformVersion>SUPPORTED_OS_PLATFORM_VERSION</SupportedOSPlatformVersion>
<RootNamespace Condition="'$(name)' != '$(name{-VALUE-FORMS-}safe_namespace)'">AndroidLib1</RootNamespace>
<Nullable>enable</Nullable>
</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 @@ -106,4 +106,7 @@
<FileVersion Condition=" '$(FileVersion)' == '' ">$(ApplicationVersion)</FileVersion>
</PropertyGroup>

<!-- Import this as part of DefaultProperties.target to support Xamarin.Legacy.Sdk -->
<Import Project="Microsoft.Android.Sdk.ImplicitNamespaceImports.targets" />

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<!--
***********************************************************************************************
Microsoft.Android.Sdk.ImplicitNamespaceImports.targets
This file contains @(Import) items for C# 10 global usings.
***********************************************************************************************
-->
<Project>
<!--
There's a master switch, $(DisableImplicitNamespaceImports),
which, if set, will prevent all implicit namespace imports.
This is done automatically, we don't have to check
$(DisableImplicitNamespaceImports) here.
-->
<ItemGroup Condition=" '$(DisableImplicitNamespaceImports_Android)' != 'true' ">
<Import Include="Android.App" />
<Import Include="Android.Widget" />
</ItemGroup>

<!--
NOTE: Bundle is specifically imported by itself, so
Android.OS.Environment doesn't conflict with System.Environment.
We have to write our own .cs file to do this, because dotnet/sdk
always prefixes with `global::`, see:
https://github.com/dotnet/sdk/blob/86946f52cd012cefd811b25287a8da034bf082a3/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.GenerateImplicitNamespaceImports.targets#L46
-->
<Target Name="_AndroidGenerateImplicitNamespaceImports"
Condition=" '$(DisableImplicitNamespaceImports_Android)' != 'true' "
AfterTargets="CoreGenerateImplicitNamespaceImports">
<PropertyGroup>
<_AndroidGeneratedImplicitNamespaceImportFile>$(IntermediateOutputPath)$(MSBuildProjectName).AndroidImplicitNamespaceImports$(DefaultLanguageSourceExtension)</_AndroidGeneratedImplicitNamespaceImportFile>
</PropertyGroup>
<ItemGroup>
<_AndroidImportFileLine Include="// %3Cautogenerated />"/>
<_AndroidImportFileLine Include="global using Bundle = global::Android.OS.Bundle%3B"/>
</ItemGroup>
<WriteLinesToFile
Lines="@(_AndroidImportFileLine)"
File="$(_AndroidGeneratedImplicitNamespaceImportFile)"
Overwrite="true"
WriteOnlyWhenDifferent="true"
/>
<ItemGroup>
<Compile Include="$(_AndroidGeneratedImplicitNamespaceImportFile)" />
<FileWrites Include="$(_AndroidGeneratedImplicitNamespaceImportFile)" />
</ItemGroup>
</Target>

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,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 @@ -606,7 +607,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-alpha3",
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,7 @@ public static class KnownProperties
public const string AndroidFastDeploymentType = "AndroidFastDeploymentType";
public const string AndroidClassParser = "AndroidClassParser";
public const string _AndroidAllowDeltaInstall = "_AndroidAllowDeltaInstall";
public const string Nullable = "Nullable";
}
}

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,7 @@ public XASdkProject (string outputType = "Exe", [CallerMemberName] string packag
JavaPackageName = JavaPackageName ?? PackageName.ToLowerInvariant ();
GlobalPackagesFolder = FileSystemUtils.FindNugetGlobalPackageFolder ();
SetProperty (KnownProperties.OutputType, outputType);
SetProperty (KnownProperties.Nullable, "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,7 @@ public XamarinAndroidApplicationProject (string debugConfigurationName = "Debug"
{
if (Builder.UseDotNet) {
SetProperty (KnownProperties.OutputType, "Exe");
SetProperty (KnownProperties.Nullable, "enable");
SetProperty ("XamarinAndroidSupportSkipVerifyVersions", "True");
SetProperty ("_FastDeploymentDiagnosticLogging", "True");

Expand Down Expand Up @@ -81,7 +83,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 @@ -15,6 +15,7 @@ public XamarinAndroidLibraryProject (string debugConfigurationName = "Debug", st
: base (debugConfigurationName, releaseConfigurationName)
{
if (!Builder.UseDotNet) {
SetProperty (KnownProperties.Nullable, "enable");
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) {
// NOTE: `Xamarin.Forms` namespace will collide with `Android.App` otherwise:
SetProperty ("DisableImplicitNamespaceImports_Android", "true");
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,9 @@ static XamarinFormsXASdkProject ()
public XamarinFormsXASdkProject (string outputType = "Exe", [CallerMemberName] string packageName = "")
: base (outputType, packageName)
{
// NOTE: `Xamarin.Forms` namespace will collide with `Android.App` otherwise:
SetProperty ("DisableImplicitNamespaceImports_Android", "true");

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": 2676
},
"assemblies/UnnamedProject.dll": {
"Size": 3171
"Size": 3535
},
"classes.dex": {
"Size": 316792
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 @@ -9,72 +9,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

0 comments on commit 8004dd1

Please sign in to comment.