Skip to content

Commit 2ac196d

Browse files
[One .NET] dotnet new project and item templates
Context: https://docs.microsoft.com/dotnet/core/tutorials/cli-templates-create-template-pack Context: https://github.com/dotnet/templating/wiki This implements basic Android templates that are contained in a `Microsoft.Android.Templates.nupkg` file. This is included in the .NET 6 Workload installers and installed to: C:\Program Files\dotnet\templates /usr/local/share/dotnet/templates A `postinstall` action in the installers runs `dotnet new --install`, as there does not currently appear to be any extra template functionality for [.NET Workloads][0]. Some example project templates: dotnet new android --output MyAndroidApp --packageName com.mycompany.myandroidapp dotnet new androidlib --output MyAndroidLibrary dotnet new android-bindinglib --output MyJavaBinding And item templates: dotnet new android-activity --name LoginActivity --namespace MyAndroidApp dotnet new android-layout --name MyLayout --output Resources/layout Note that the `android-bindinglib` template is not a special project type. It has additional help files for writing bindings as we have in the current Xamarin.Android templates. I also updated the `XASdkTests` to `dotnet new` each template and `dotnet build` the resulting output. [0]: https://github.com/dotnet/designs/blob/bc5d2c1664299a4a640e3185e87a6b99426b6b89/accepted/2020/workloads/workloads.md
1 parent 113ffcc commit 2ac196d

File tree

48 files changed

+538
-18
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+538
-18
lines changed

Documentation/guides/OneDotNet.md

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -189,12 +189,45 @@ It is recommended to migrate to the new linker settings, as
189189
There are currently a few "verbs" we are aiming to get working in
190190
Xamarin.Android:
191191

192+
dotnet new
192193
dotnet build
193194
dotnet publish
194195
dotnet run
195196

196-
Currently in .NET 5 console apps, `dotnet publish` is where all the
197-
work to produce a self-contained "app" happens:
197+
### dotnet new
198+
199+
To support `dotnet new`, we created a few basic project and item
200+
templates for Android that are named following the patterns and naming
201+
of existing .NET templates:
202+
203+
Templates Short Name Language Tags
204+
-------------------------------------------- ------------------- ---------- ----------------------
205+
Android Activity template android-activity [C#] Android
206+
Android Java Library Binding android-bindinglib [C#] Android
207+
Android Layout template android-layout [C#] Android
208+
Android Class library androidlib [C#] Android
209+
Android Application android [C#] Android
210+
Console Application console [C#],F#,VB Common/Console
211+
Class library classlib [C#],F#,VB Common/Library
212+
Razor Page page [C#] Web/ASP.NET
213+
ASP.NET Core Web App webapp [C#] Web/MVC/Razor Pages
214+
215+
To create different types of Android projects:
216+
217+
dotnet new android --output MyAndroidApp --packageName com.mycompany.myandroidapp
218+
dotnet new androidlib --output MyAndroidLibrary
219+
dotnet new android-bindinglib --output MyJavaBinding
220+
221+
Once the projects are created, some basic item templates can also be
222+
used such as:
223+
224+
dotnet new android-activity --name LoginActivity --namespace MyAndroidApp
225+
dotnet new android-layout --name MyLayout --output Resources/layout
226+
227+
### dotnet build & publish
228+
229+
Currently in .NET console apps, `dotnet publish` is where all the work
230+
to produce a self-contained "app" happens:
198231

199232
* The linker via the `<IlLink/>` MSBuild task
200233
* .NET Core's version of AOT, named "ReadyToRun"
@@ -223,12 +256,18 @@ Play, ad-hoc distribution, etc. It could be able to sign the `.apk` or
223256
`.aab` with different keys. As a starting point, this will currently
224257
copy the output to a `publish` directory on disk.
225258

259+
[illink]: https://github.com/mono/linker/blob/master/src/linker/README.md
260+
261+
### dotnet run
262+
226263
`dotnet run` can be used to launch applications on a
227264
device or emulator via the `--project` switch:
228265

229266
dotnet run --project HelloAndroid.csproj
230267

231-
[illink]: https://github.com/mono/linker/blob/master/src/linker/README.md
268+
Alternatively, you could use the `Run` MSBuild target such as:
269+
270+
dotnet build HelloAndroid.csproj -t:Run
232271

233272
### Preview testing
234273

build-tools/Xamarin.Android.Tools.BootstrapTasks/Xamarin.Android.Tools.BootstrapTasks/GenerateWixFile.cs

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,23 @@ public override bool Execute ()
111111
foreach (var directory in Directory.EnumerateDirectories (packs_dir, "Microsoft.Android.*")) {
112112
RecurseDirectory (packs_dir, packWriter, componentWriter, directory);
113113
}
114+
packWriter.WriteEndElement (); // </Directory> packs
115+
116+
// templates
117+
var templates_dir = Path.Combine (DotNetPath, "templates");
118+
packWriter.WriteStartElement ("Directory");
119+
packWriter.WriteAttributeString ("Id", "templates");
120+
packWriter.WriteAttributeString ("Name", "templates");
121+
foreach (var directory in Directory.EnumerateDirectories (templates_dir)) {
122+
foreach (var file in Directory.EnumerateFiles (directory, "Microsoft.Android.Templates.*.nupkg")) {
123+
packWriter.WriteStartElement ("Directory");
124+
packWriter.WriteAttributeString ("Id", "DOTNETTEMPLATESDIR");
125+
packWriter.WriteAttributeString ("Name", Path.GetFileName (directory));
126+
packWriter.WriteAttributeString ("FileSource", directory);
127+
AddFile (templates_dir, packWriter, componentWriter, file, componentId: "ANDROIDTEMPLATES");
128+
packWriter.WriteEndElement (); // </Directory> DOTNETTEMPLATES
129+
}
130+
}
114131

115132
packWriter.WriteEndDocument (); // </Directory>
116133
componentWriter.WriteEndDocument (); // </ComponentGroup>
@@ -145,22 +162,28 @@ static void RecurseDirectory (string top_dir, XmlWriter packWriter, XmlWriter co
145162
var fileName = Path.GetFileName (file);
146163
if (fileName.StartsWith (".") || fileName.StartsWith ("_"))
147164
continue;
148-
var componentId = GetId (top_dir, file);
149-
packWriter.WriteStartElement ("Component");
150-
packWriter.WriteAttributeString ("Id", componentId);
151-
packWriter.WriteStartElement ("File");
152-
packWriter.WriteAttributeString ("Id", componentId);
153-
packWriter.WriteAttributeString ("Name", Path.GetFileName (file));
154-
packWriter.WriteAttributeString ("KeyPath", "yes");
155-
packWriter.WriteEndElement (); // </File>
156-
packWriter.WriteEndElement (); // </Component>
157-
componentWriter.WriteStartElement ("ComponentRef");
158-
componentWriter.WriteAttributeString ("Id", componentId);
159-
componentWriter.WriteEndElement (); // </ComponentRef>
165+
AddFile (top_dir, packWriter, componentWriter, file);
160166
}
161167
packWriter.WriteEndElement (); // </Directory>
162168
}
163169

170+
static void AddFile (string top_dir, XmlWriter packWriter, XmlWriter componentWriter, string file, string componentId = null)
171+
{
172+
if (string.IsNullOrEmpty (componentId))
173+
componentId = GetId (top_dir, file);
174+
packWriter.WriteStartElement ("Component");
175+
packWriter.WriteAttributeString ("Id", componentId);
176+
packWriter.WriteStartElement ("File");
177+
packWriter.WriteAttributeString ("Id", componentId);
178+
packWriter.WriteAttributeString ("Name", Path.GetFileName (file));
179+
packWriter.WriteAttributeString ("KeyPath", "yes");
180+
packWriter.WriteEndElement (); // </File>
181+
packWriter.WriteEndElement (); // </Component>
182+
componentWriter.WriteStartElement ("ComponentRef");
183+
componentWriter.WriteAttributeString ("Id", componentId);
184+
componentWriter.WriteEndElement (); // </ComponentRef>
185+
}
186+
164187
static string GetId (string top_dir, string path)
165188
{
166189
if (string.IsNullOrEmpty (path))

build-tools/create-dotnet-msi/Microsoft.NET.Workload.Android.wix.in

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,17 @@
1919
<ComponentGroupRef Id="ProductComponents" />
2020
</Feature>
2121
<WixVariable Id="WixUILicenseRtf" Value="LICENSE.rtf" />
22+
<CustomAction Id="dotnet_new_install" ExeCommand="dotnet.exe new --install &quot;[#ANDROIDTEMPLATES]&quot;" Directory="dotnet" />
23+
<CustomAction Id="dotnet_new_uninstall" ExeCommand="dotnet.exe new --uninstall Microsoft.Android.Templates" Directory="dotnet" />
24+
<!-- For conditions see: https://stackoverflow.com/a/17608049 -->
25+
<InstallExecuteSequence>
26+
<Custom Action="dotnet_new_install" After="InstallFinalize">
27+
NOT Installed
28+
</Custom>
29+
<Custom Action="dotnet_new_uninstall" After="InstallFinalize">
30+
REMOVE="ALL" AND NOT UPGRADINGPRODUCTCODE
31+
</Custom>
32+
</InstallExecuteSequence>
2233
</Product>
2334

2435
<Fragment>

build-tools/create-dotnet-pkg/create-dotnet-pkg.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
<PayloadDir>$(OutputPath)\pkg\archive</PayloadDir>
1414
<PkgOutputPath>$(OutputPath)\pkg\packages</PkgOutputPath>
1515
<PkgResourcesPath>$(OutputPath)\pkg\resources</PkgResourcesPath>
16+
<PkgScriptsDir>$(MSBuildThisFileDirectory)scripts</PkgScriptsDir>
1617
<PkgDistributionDestination>$(OutputPath)\pkg\distribution.xml</PkgDistributionDestination>
1718
<LicenseDestination>$(PkgResourcesPath)\en.lproj</LicenseDestination>
1819
</PropertyGroup>
@@ -35,6 +36,7 @@
3536
<_FilesToCopy Include="$(DotNetPreviewPath)\sdk-manifests\$(DotNetPreviewVersionBand)\Microsoft.NET.Workload.Android\**\*" />
3637
<_FilesToCopy Include="$(DotNetPreviewPath)\packs\Microsoft.Android.Ref\**\*" />
3738
<_FilesToCopy Include="$(DotNetPreviewPath)\packs\Microsoft.Android.Sdk.osx-x64\**\*" />
39+
<_FilesToCopy Include="$(DotNetPreviewPath)\templates\**\Microsoft.Android.Templates.*.nupkg" />
3840
</ItemGroup>
3941
<MakeDir Directories="$(PkgOutputPath);$(PayloadDir);$(PkgResourcesPath);$(LicenseDestination)"/>
4042
<ReplaceFileContents
@@ -62,6 +64,7 @@
6264
<PkgBuildArgs Include="--identifier com.microsoft.net.workload.android.pkg" />
6365
<PkgBuildArgs Include="--version $(AndroidPackVersionLong)"/>
6466
<PkgBuildArgs Include="--install-location &quot;$(PkgInstallDir)&quot; "/>
67+
<PkgBuildArgs Include="--scripts &quot;$(PkgScriptsDir)&quot; "/>
6568
<PkgBuildArgs Include="&quot;$(PkgOutputPath)/microsoft.net.workload.android.pkg&quot; "/>
6669
</ItemGroup>
6770
<Exec Command="pkgbuild @(PkgBuildArgs, ' ')" />
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#!/bin/sh
2+
3+
INSTALLATION_ROOT=/usr/local/share/dotnet
4+
NUPKG=$(find $INSTALLATION_ROOT/templates -name 'Microsoft.Android.Templates.*.nupkg')
5+
6+
$INSTALLATION_ROOT/dotnet new --install $NUPKG

build-tools/create-packs/Directory.Build.targets

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@
8282
<Exec Command="$(DotNetPreviewTool) pack @(_GlobalProperties, ' ') -p:AndroidHostRID=osx-x64 &quot;$(MSBuildThisFileDirectory)Microsoft.Android.Sdk.proj&quot;" Condition=" '$(HostOS)' == 'Darwin' " />
8383
<Exec Command="$(DotNetPreviewTool) pack @(_GlobalProperties, ' ') -p:AndroidHostRID=win-x64 &quot;$(MSBuildThisFileDirectory)Microsoft.Android.Sdk.proj&quot;" Condition=" '$(HostOS)' != 'Linux' " /> <!-- Windows pack should be built both Windows and macOS -->
8484
<Exec Command="$(DotNetPreviewTool) pack @(_GlobalProperties, ' ') &quot;$(MSBuildThisFileDirectory)Microsoft.NET.Workload.Android.proj&quot;" />
85+
<Exec Command="$(DotNetPreviewTool) pack @(_GlobalProperties, ' ') &quot;$(XamarinAndroidSourcePath)src\templates\templates.csproj&quot;" />
8586
</Target>
8687

8788
<Target Name="ExtractWorkloadPacks"
@@ -92,6 +93,7 @@
9293
<_WLPacks Include="$(XamarinAndroidSourcePath)bin\Build$(Configuration)\nupkgs\Microsoft.Android.Sdk.osx-x64.*.nupkg" Condition=" '$(HostOS)' == 'Darwin' " />
9394
<_WLPacks Include="$(XamarinAndroidSourcePath)bin\Build$(Configuration)\nupkgs\Microsoft.Android.Sdk.win-x64.*.nupkg" Condition=" '$(HostOS)' == 'Windows' " />
9495
<_WLPacks Include="$(XamarinAndroidSourcePath)bin\Build$(Configuration)\nupkgs\Microsoft.Android.Ref.*.nupkg" />
96+
<_WLTemplates Include="$(XamarinAndroidSourcePath)bin\Build$(Configuration)\nupkgs\Microsoft.Android.Templates.*.nupkg" />
9597
<!-- Runtime packs are not yet supported by workloads -->
9698
<!-- <_WLPacks Include="$(XamarinAndroidSourcePath)bin\Build$(Configuration)\nupkgs\Microsoft.Android.Runtime.*.nupkg" /> -->
9799
</ItemGroup>
@@ -106,6 +108,10 @@
106108
SourceFiles="@(_WLPacks)"
107109
DestinationFolder="$(DotNetPreviewPath)packs\$([System.String]::Copy('%(_WLPacks.Filename)').Replace('.$(_WLPackVersion)', ''))\$(_WLPackVersion)"
108110
/>
111+
<Copy
112+
SourceFiles="@(_WLTemplates)"
113+
DestinationFolder="$(DotNetPreviewPath)templates\$(DotNetPreviewVersionFull)"
114+
/>
109115
<ItemGroup>
110116
<_UnixExecutables Include="$(DotNetPreviewPath)packs\Microsoft.Android.Sdk.*\*\tools\$(HostOS)\**\*.*" />
111117
<_FilesToTouch Include="$(DotNetPreviewPath)sdk-manifests\$(DotNetPreviewVersionBand)\Microsoft.NET.Workload.Android\**" />
@@ -115,6 +121,11 @@
115121
Condition=" '$(HostOS)' == 'Darwin' or '$(HostOS)' == 'Linux' "
116122
Command="chmod +x &quot;%(_UnixExecutables.Identity)&quot;"
117123
/>
124+
<Exec
125+
Command="$(DotNetPreviewTool) new --install &quot;%(_WLTemplates.FileName)%(_WLTemplates.Extension)&quot;"
126+
WorkingDirectory="$(DotNetPreviewPath)templates\$(DotNetPreviewVersionFull)"
127+
ContinueOnError="true"
128+
/>
118129
<!-- Some files had timestamps in the future -->
119130
<Touch Files="@(_FilesToTouch)" />
120131
<MakeDir Directories="$([System.IO.Path]::GetDirectoryName ($(_WorkloadResolverFlagFile)))" />
@@ -129,7 +140,12 @@
129140
<_PackFilesToDelete Include="$(DotNetPreviewPath)sdk-manifests\$(DotNetPreviewVersionBand)\Microsoft.Android.Workload\**\*.*" />
130141
<_PackFilesToDelete Include="$(DotNetPreviewPath)sdk-manifests\$(DotNetPreviewVersionBand)\Microsoft.NET.Workload.Android\**\*.*" />
131142
<_PackFilesToDelete Include="$(DotNetPreviewPath)packs\Microsoft.Android*\**\*.*" />
143+
<_PackFilesToDelete Include="$(DotNetPreviewPath)templates\$(DotNetPreviewVersionFull)\Microsoft.Android.Templates.*.nupkg" />
132144
</ItemGroup>
145+
<Exec
146+
Command="$(DotNetPreviewTool) new --uninstall Microsoft.Android.Templates"
147+
ContinueOnError="true"
148+
/>
133149
<RemoveDir Directories="%(_PackFilesToDelete.RootDir)%(_PackFilesToDelete.Directory)" />
134150
<Delete Files="$(_WorkloadResolverFlagFile)" />
135151
</Target>

src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/XASdkTests.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,16 @@ public void DotNetBuildLibrary (bool isRelease, bool duplicateAar)
177177
StringAssert.Contains ("public const int MyLayout", resource_designer_text);
178178
}
179179

180+
[Test]
181+
public void DotNetNew ([Values ("android", "androidlib", "android-bindinglib")] string template)
182+
{
183+
var dotnet = CreateDotNetBuilder ();
184+
Assert.IsTrue (dotnet.New (template), $"`dotnet new {template}` should succeed");
185+
Assert.IsTrue (dotnet.New ("android-activity"), "`dotnet new android-activity` should succeed");
186+
Assert.IsTrue (dotnet.New ("android-layout", Path.Combine (dotnet.ProjectDirectory, "Resources", "layout")), "`dotnet new android-layout` should succeed");
187+
Assert.IsTrue (dotnet.Build (), "`dotnet build` should succeed");
188+
}
189+
180190
[Test]
181191
public void DotNetPack ([Values ("net6.0-android", "net6.0-android30")] string targetFramework)
182192
{
@@ -461,6 +471,16 @@ void CreateEmptyFile (params string [] paths)
461471
}
462472
}
463473

474+
DotNetCLI CreateDotNetBuilder (string relativeProjectDir = null)
475+
{
476+
if (string.IsNullOrEmpty (relativeProjectDir)) {
477+
relativeProjectDir = Path.Combine ("temp", TestName);
478+
}
479+
TestOutputDirectories [TestContext.CurrentContext.Test.ID] =
480+
FullProjectDirectory = Path.Combine (Root, relativeProjectDir);
481+
return new DotNetCLI (Path.Combine (FullProjectDirectory, $"{TestName}.csproj"));
482+
}
483+
464484
DotNetCLI CreateDotNetBuilder (XASdkProject project, string relativeProjectDir = null)
465485
{
466486
if (string.IsNullOrEmpty (relativeProjectDir)) {

src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Common/DotNetCLI.cs

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,18 @@ public class DotNetCLI
2121
readonly XASdkProject project;
2222
readonly string projectOrSolution;
2323

24-
public DotNetCLI (XASdkProject project, string projectOrSolution)
24+
public DotNetCLI (string projectOrSolution)
2525
{
26-
this.project = project;
2726
this.projectOrSolution = projectOrSolution;
2827
ProjectDirectory = Path.GetDirectoryName (projectOrSolution);
2928
}
3029

30+
public DotNetCLI (XASdkProject project, string projectOrSolution)
31+
: this (projectOrSolution)
32+
{
33+
this.project = project;
34+
}
35+
3136
/// <summary>
3237
/// Runs the `dotnet` tool with the specified arguments.
3338
/// </summary>
@@ -73,6 +78,16 @@ protected bool Execute (params string [] args)
7378
return succeeded;
7479
}
7580

81+
public bool New (string template, string output = null)
82+
{
83+
var arguments = new List<string> {
84+
"new",
85+
template,
86+
"--output", $"\"{output ?? ProjectDirectory}\"",
87+
};
88+
return Execute (arguments.ToArray ());
89+
}
90+
7691
public bool Build (string target = null, string [] parameters = null)
7792
{
7893
var arguments = GetDefaultCommandLineArgs ("build", target, parameters);
@@ -123,14 +138,16 @@ List<string> GetDefaultCommandLineArgs (string verb, string target = null, strin
123138
var arguments = new List<string> {
124139
verb,
125140
$"\"{projectOrSolution}\"",
126-
$"/p:Configuration={project.Configuration}",
127141
"/noconsolelogger",
128142
$"/flp1:LogFile=\"{BuildLogFile}\";Encoding=UTF-8;Verbosity={Verbosity}",
129143
$"/bl:\"{Path.Combine (testDir, "msbuild.binlog")}\""
130144
};
131145
if (!string.IsNullOrEmpty (target)) {
132146
arguments.Add ($"/t:{target}");
133147
}
148+
if (project != null) {
149+
arguments.Add ($"/p:Configuration={project.Configuration}");
150+
}
134151
if (Directory.Exists (AndroidSdkPath)) {
135152
arguments.Add ($"/p:AndroidSdkDirectory=\"{AndroidSdkPath}\"");
136153
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<Project>
2+
<Import Project="..\..\build-tools\scripts\XAVersionInfo.targets" />
3+
<PropertyGroup>
4+
<BeforePack>
5+
_GetDefaultPackageVersion;
6+
$(BeforePack);
7+
</BeforePack>
8+
</PropertyGroup>
9+
<Target Name="_GetDefaultPackageVersion"
10+
DependsOnTargets="GetXAVersionInfo" >
11+
<PropertyGroup>
12+
<PackageVersion>$(AndroidPackVersionLong)+sha.$(XAVersionHash)</PackageVersion>
13+
</PropertyGroup>
14+
</Target>
15+
</Project>
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{
2+
"$schema": "http://json.schemastore.org/template",
3+
"author": "Microsoft",
4+
"classifications": [ "Android" ],
5+
"name": "Android Activity template",
6+
"description": "An Android Activity class",
7+
"tags": {
8+
"language": "C#",
9+
"type": "item"
10+
},
11+
"identity": "Microsoft.Android.AndroidActivity",
12+
"shortName": "android-activity",
13+
"sourceName": "Activity1",
14+
"primaryOutputs": [
15+
{ "path": "Activity1.cs" }
16+
],
17+
"defaultName": "Activity1",
18+
"symbols": {
19+
"namespace": {
20+
"description": "namespace for the generated code",
21+
"replaces": "AndroidApp1",
22+
"type": "parameter"
23+
}
24+
}
25+
}

0 commit comments

Comments
 (0)