Skip to content

Commit 814d63d

Browse files
[One .NET] temporary Windows & macOS installers for .NET 6
Depends on: dotnet#5195 For .NET 6 Preview 1, we will need to provide our own installers for the Android workload. This will also unblock the Xamarin.Forms / MAUI team as they build on top of the iOS and Android workloads for .NET 6. We will eventually do some kind of "insertion" process to provide our `.nupkg` files to the dotnet/installer repo, so these installers will go away completely at some point. For now, this creates two new installers: * Microsoft.Android.Workload.msi - installs to `C:\Program Files\dotnet\` * Microsoft.Android.Workload.pkg - installs to `/usr/local/share/dotnet/` Both installers have the following file structure underneath the root directory: * sdk\5.0.100-rtm.20509.5\EnableWorkloadResolver.sentinel * sdk-manifests\5.0.100\Microsoft.Android.Workload\** * packs\Microsoft.Android.Ref\** * packs\Microsoft.Android.Sdk\** The installers will have a hard dependency on .NET 5.0.100-rtm.20509.5, so we will need to have clear instructions for installing the correct version of .NET for the Android workload. The Windows installer is made using WIX, to mirror what dotnet/installer is using: * https://wixtoolset.org/ * https://github.com/dotnet/installer/blob/861a1dd12cb80bd834d0e51442d46ee7d1a4023f/src/redist/targets/GenerateMSIs.targets The `.msi` will need to be built on Windows and the `.pkg` will need to be built on macOS. `create-dotnet-msi.csproj` will download the WIX toolset to `~\android-toolchain\wix` and call `candle.exe` and `light.exe` appropriately. `create-dotnet-pkg.csproj` is based on `create-pkg.csproj`, and no additional tooling is needed.
1 parent d60d160 commit 814d63d

File tree

11 files changed

+613
-8
lines changed

11 files changed

+613
-8
lines changed

Configuration.props

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@
7272
<!-- Version number from: https://github.com/dotnet/installer#installers-and-binaries -->
7373
<DotNetPreviewVersionBand Condition=" '$(DotNetPreviewVersionBand)' == '' ">5.0.100</DotNetPreviewVersionBand>
7474
<DotNetPreviewVersionFull Condition=" '$(DotNetPreviewVersionFull)' == '' ">$(DotNetPreviewVersionBand)-rtm.20509.5</DotNetPreviewVersionFull>
75+
<WixToolPath Condition=" '$(WixToolPath)' == '' ">$(AndroidToolchainDirectory)\wix\</WixToolPath>
7576
<AndroidCmakeVersion Condition=" '$(AndroidCmakeVersion)' == '' ">3.10.2</AndroidCmakeVersion>
7677
<AndroidCmakeVersionPath Condition=" '$(AndroidCmakeVersionPath)' == '' ">$(AndroidCmakeVersion).4988404</AndroidCmakeVersionPath>
7778
<AndroidSdkCmakeDirectory>$(AndroidSdkDirectory)\cmake\$(AndroidCmakeVersionPath)</AndroidSdkCmakeDirectory>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
using System;
2+
using System.IO;
3+
using Microsoft.Build.Framework;
4+
using Microsoft.Build.Utilities;
5+
6+
namespace Xamarin.Android.Tools.BootstrapTasks
7+
{
8+
/// <summary>
9+
/// Used for converting plain text license files to .rtf format.
10+
/// Assumes the file is line wrapped with Environment.NewLine:
11+
/// * Double new lines are preserved.
12+
/// * Single new lines are replaced with spaces.
13+
/// </summary>
14+
public class ConvertToRichText : Task
15+
{
16+
[Required]
17+
public string SourceFile { get; set; }
18+
19+
[Required]
20+
public string DestinationFile { get; set; }
21+
22+
public override bool Execute ()
23+
{
24+
var text = File.ReadAllText (SourceFile);
25+
26+
text = text
27+
.Replace (@"\", @"\\")
28+
.Replace ("{", @"\{")
29+
.Replace ("}", @"\}")
30+
// Only want to keep "double" new lines
31+
.Replace (Environment.NewLine + Environment.NewLine, $@"\par{Environment.NewLine} \par{Environment.NewLine} ")
32+
.Replace (Environment.NewLine, " ");
33+
34+
Directory.CreateDirectory (Path.GetDirectoryName (DestinationFile));
35+
using (var writer = File.CreateText (DestinationFile)) {
36+
writer.Write (@"{\rtf1\ansi\ansicpg1250\deff0{\fonttbl\f0\fcharset0 Courier New;}\f0\pard ");
37+
writer.Write (text);
38+
writer.Write (" } ");
39+
}
40+
41+
return !Log.HasLoggedErrors;
42+
}
43+
}
44+
}
Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
using System.IO;
2+
using System.Linq;
3+
using System.Security.Cryptography;
4+
using System.Text;
5+
using System.Xml;
6+
using Microsoft.Build.Framework;
7+
using Microsoft.Build.Utilities;
8+
9+
namespace Xamarin.Android.Tools.BootstrapTasks
10+
{
11+
/// <summary>
12+
/// Generates a .wix file for the contents of ~/android-toolchain/dotnet/packs
13+
/// The .wix file can be used to generate the .msi installer for Windows.
14+
/// </summary>
15+
public class GenerateWixFile : Task
16+
{
17+
[Required]
18+
public string Template { get; set; }
19+
20+
[Required]
21+
public string DestinationFile { get; set; }
22+
23+
[Required]
24+
public string DotNetPath { get; set; }
25+
26+
[Required]
27+
public string DotNetVersion { get; set; }
28+
29+
[Required]
30+
public string MSIVersion { get; set; }
31+
32+
public override bool Execute ()
33+
{
34+
var settings = new XmlWriterSettings {
35+
OmitXmlDeclaration = true,
36+
Indent = true,
37+
};
38+
39+
var directories = new StringBuilder ();
40+
var components = new StringBuilder ();
41+
using (var packWriter = XmlWriter.Create (directories, settings))
42+
using (var componentWriter = XmlWriter.Create (components, settings)) {
43+
44+
// Components
45+
componentWriter.WriteStartElement ("ComponentGroup");
46+
componentWriter.WriteAttributeString ("Id", "ProductComponents");
47+
componentWriter.WriteStartElement ("ComponentRef");
48+
componentWriter.WriteAttributeString ("Id", "EnableWorkloadResolver");
49+
componentWriter.WriteEndElement (); // </ComponentRef>
50+
51+
// dotnet
52+
packWriter.WriteStartElement ("Directory");
53+
packWriter.WriteAttributeString ("Id", "dotnet");
54+
packWriter.WriteAttributeString ("Name", "dotnet");
55+
56+
// sdk
57+
packWriter.WriteStartElement ("Directory");
58+
packWriter.WriteAttributeString ("Id", "sdk");
59+
packWriter.WriteAttributeString ("Name", "sdk");
60+
61+
// DOTNETVERSION
62+
packWriter.WriteStartElement ("Directory");
63+
packWriter.WriteAttributeString ("Id", "DOTNETVERSION");
64+
packWriter.WriteAttributeString ("Name", DotNetVersion);
65+
packWriter.WriteAttributeString ("FileSource", Path.Combine (DotNetPath, "sdk", DotNetVersion));
66+
67+
// EnableWorkloadResolver
68+
packWriter.WriteStartElement ("Component");
69+
packWriter.WriteAttributeString ("Id", "EnableWorkloadResolver");
70+
packWriter.WriteStartElement ("File");
71+
packWriter.WriteAttributeString ("Id", "EnableWorkloadResolver");
72+
packWriter.WriteAttributeString ("Name", "EnableWorkloadResolver.sentinel");
73+
packWriter.WriteAttributeString ("KeyPath", "yes");
74+
packWriter.WriteEndElement (); // </File>
75+
packWriter.WriteEndElement (); // </Component>
76+
packWriter.WriteEndElement (); // </Directory> DOTNETVERSION
77+
packWriter.WriteEndElement (); // </Directory> sdk
78+
79+
// sdk-manifests
80+
var sdk_manifests_root = Path.Combine (DotNetPath, "sdk-manifests");
81+
packWriter.WriteStartElement ("Directory");
82+
packWriter.WriteAttributeString ("Id", "sdk_manifests");
83+
packWriter.WriteAttributeString ("Name", "sdk-manifests");
84+
85+
// 5.0.100
86+
var sdk_manifests = Directory.EnumerateDirectories (sdk_manifests_root).FirstOrDefault ();
87+
if (string.IsNullOrEmpty (sdk_manifests)) {
88+
Log.LogError ($"Cannot find child directory of: {sdk_manifests_root}");
89+
return false;
90+
}
91+
var version_band = Path.GetFileName (sdk_manifests);
92+
packWriter.WriteStartElement ("Directory");
93+
packWriter.WriteAttributeString ("Id", "DOTNETVERSIONBAND");
94+
packWriter.WriteAttributeString ("Name", version_band);
95+
packWriter.WriteAttributeString ("FileSource", sdk_manifests);
96+
var workload = Path.Combine (sdk_manifests, "Microsoft.NET.Workload.Android");
97+
if (Directory.Exists (workload)) {
98+
RecurseDirectory (sdk_manifests, packWriter, componentWriter, workload);
99+
} else {
100+
Log.LogError ($"Cannot find directory: {workload}");
101+
return false;
102+
}
103+
packWriter.WriteEndElement (); // </Directory> version_band
104+
packWriter.WriteEndElement (); // </Directory> sdk-manifests
105+
106+
// packs
107+
var packs_dir = Path.Combine (DotNetPath, "packs");
108+
packWriter.WriteStartElement ("Directory");
109+
packWriter.WriteAttributeString ("Id", "packs");
110+
packWriter.WriteAttributeString ("Name", "packs");
111+
foreach (var directory in Directory.EnumerateDirectories (packs_dir, "Microsoft.Android.*")) {
112+
RecurseDirectory (packs_dir, packWriter, componentWriter, directory);
113+
}
114+
115+
packWriter.WriteEndDocument (); // </Directory>
116+
componentWriter.WriteEndDocument (); // </ComponentGroup>
117+
}
118+
119+
var template = File.ReadAllText (Template);
120+
var contents = template
121+
.Replace ("@MSIVERSION@", MSIVersion)
122+
.Replace ("@DIRECTORIES@", directories.ToString ())
123+
.Replace ("@COMPONENTS@", components.ToString ());
124+
125+
Log.LogMessage (MessageImportance.Low, "Writing XML to {0}: {1}", DestinationFile, contents);
126+
File.WriteAllText (DestinationFile, contents);
127+
128+
return !Log.HasLoggedErrors;
129+
}
130+
131+
static void RecurseDirectory (string top_dir, XmlWriter packWriter, XmlWriter componentWriter, string directory)
132+
{
133+
var directoryId = GetId (top_dir, directory);
134+
packWriter.WriteStartElement ("Directory");
135+
packWriter.WriteAttributeString ("Id", directoryId);
136+
packWriter.WriteAttributeString ("Name", Path.GetFileName (directory));
137+
packWriter.WriteAttributeString ("FileSource", directory);
138+
foreach (var child in Directory.EnumerateDirectories (directory)) {
139+
var directoryName = Path.GetFileName (child);
140+
if (directoryName.StartsWith (".") || directoryName.StartsWith ("_"))
141+
continue;
142+
RecurseDirectory (top_dir, packWriter, componentWriter, child);
143+
}
144+
foreach (var file in Directory.EnumerateFiles (directory)) {
145+
var fileName = Path.GetFileName (file);
146+
if (fileName.StartsWith (".") || fileName.StartsWith ("_"))
147+
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>
160+
}
161+
packWriter.WriteEndElement (); // </Directory>
162+
}
163+
164+
static string GetId (string top_dir, string path)
165+
{
166+
if (string.IsNullOrEmpty (path))
167+
return path;
168+
if (path.Length > top_dir.Length + 1) {
169+
path = path.Substring (top_dir.Length + 1);
170+
}
171+
return GetHashString (path);
172+
}
173+
174+
static byte [] GetHash (string inputString)
175+
{
176+
using (var algorithm = SHA256.Create ())
177+
return algorithm.ComputeHash (Encoding.UTF8.GetBytes (inputString));
178+
}
179+
180+
static string GetHashString (string inputString)
181+
{
182+
var sb = new StringBuilder ("S", 65);
183+
foreach (byte b in GetHash (inputString))
184+
sb.Append (b.ToString ("X2"));
185+
return sb.ToString ();
186+
}
187+
}
188+
}

build-tools/automation/azure-pipelines.yaml

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1115,6 +1115,112 @@ stages:
11151115

11161116
- template: yaml-templates/fail-on-issue.yaml
11171117

1118+
- stage: dotnet_installers
1119+
displayName: .NET 6 Preview
1120+
dependsOn: mac_build
1121+
jobs:
1122+
# Check - "Xamarin.Android (.NET 6 Preview .pkg Installer)"
1123+
- job: dotnet_pkg_installer
1124+
displayName: .pkg Installer
1125+
pool: $(MacMojaveBuildPool)
1126+
workspace:
1127+
clean: all
1128+
steps:
1129+
- checkout: self
1130+
submodules: recursive
1131+
1132+
- task: DownloadPipelineArtifact@2
1133+
inputs:
1134+
artifactName: $(NuGetArtifactName)
1135+
downloadPath: $(System.DefaultWorkingDirectory)/storage-artifacts
1136+
1137+
- task: UseDotNet@2
1138+
displayName: install .NET Core $(DotNetCoreVersion)
1139+
inputs:
1140+
version: $(DotNetCoreVersion)
1141+
1142+
- script: >
1143+
mkdir -p $(System.DefaultWorkingDirectory)/bin/Build$(XA.Build.Configuration)/nupkgs &&
1144+
cp $(System.DefaultWorkingDirectory)/storage-artifacts/*.nupkg $(System.DefaultWorkingDirectory)/bin/Build$(XA.Build.Configuration)/nupkgs
1145+
displayName: copy .nupkgs
1146+
1147+
- task: MSBuild@1
1148+
displayName: msbuild Xamarin.Android.BootstrapTasks
1149+
inputs:
1150+
solution: Xamarin.Android.BootstrapTasks.sln
1151+
configuration: $(XA.Build.Configuration)
1152+
msbuildArguments: /restore /bl:$(System.DefaultWorkingDirectory)/bin/Build$(XA.Build.Configuration)/msbuild-bootstraptasks.binlog
1153+
1154+
- task: MSBuild@1
1155+
displayName: msbuild /t:CreateWorkloadInstallers
1156+
inputs:
1157+
solution: Xamarin.Android.sln
1158+
configuration: $(XA.Build.Configuration)
1159+
msbuildArguments: /t:CreateWorkloadInstallers /bl:$(System.DefaultWorkingDirectory)/bin/Build$(XA.Build.Configuration)/msbuild-workload.binlog
1160+
1161+
- script: cp $(System.DefaultWorkingDirectory)/bin/Build$(XA.Build.Configuration)/*.pkg $(Build.ArtifactStagingDirectory)
1162+
displayName: copy pkg
1163+
1164+
- template: upload-to-storage.yml@yaml
1165+
parameters:
1166+
BuildPackages: $(Build.ArtifactStagingDirectory)
1167+
AzureContainerName: $(Azure.Container.Name)
1168+
AzureUploadLocation: $(Build.DefinitionName)/public/$(Build.BuildId)/$(Build.SourceBranchName)/$(Build.SourceVersion)
1169+
SourceDirectory: $(System.DefaultWorkingDirectory)
1170+
1171+
- template: yaml-templates/upload-results.yaml
1172+
parameters:
1173+
solution: build-tools/Xamarin.Android.Tools.BootstrapTasks/Xamarin.Android.Tools.BootstrapTasks.csproj
1174+
artifactName: dotnet-preview-pkg
1175+
1176+
# Check - "Xamarin.Android (.NET 6 Preview .msi Installer)"
1177+
- job: dotnet_msi_installer
1178+
displayName: .msi Installer
1179+
pool: $(HostedWinVS2019)
1180+
workspace:
1181+
clean: all
1182+
steps:
1183+
- checkout: self
1184+
submodules: recursive
1185+
1186+
- task: DownloadPipelineArtifact@2
1187+
inputs:
1188+
artifactName: $(NuGetArtifactName)
1189+
downloadPath: $(System.DefaultWorkingDirectory)\storage-artifacts
1190+
1191+
- script: >
1192+
mkdir $(System.DefaultWorkingDirectory)\bin\Build$(XA.Build.Configuration)\nupkgs &&
1193+
copy /Y $(System.DefaultWorkingDirectory)\storage-artifacts\*.nupkg $(System.DefaultWorkingDirectory)\bin\Build$(XA.Build.Configuration)\nupkgs
1194+
displayName: copy .nupkgs
1195+
1196+
- task: MSBuild@1
1197+
displayName: msbuild Xamarin.Android.BootstrapTasks
1198+
inputs:
1199+
solution: Xamarin.Android.BootstrapTasks.sln
1200+
configuration: $(XA.Build.Configuration)
1201+
msbuildArguments: /restore /bl:$(System.DefaultWorkingDirectory)\bin\Build$(XA.Build.Configuration)\msbuild-bootstraptasks.binlog
1202+
1203+
- task: MSBuild@1
1204+
displayName: msbuild /t:CreateWorkloadInstallers
1205+
inputs:
1206+
solution: Xamarin.Android.sln
1207+
configuration: $(XA.Build.Configuration)
1208+
msbuildArguments: /t:CreateWorkloadInstallers /bl:$(System.DefaultWorkingDirectory)\bin\Build$(XA.Build.Configuration)\msbuild-workload.binlog
1209+
1210+
- script: copy /Y $(System.DefaultWorkingDirectory)\bin\Build$(XA.Build.Configuration)\*.msi $(Build.ArtifactStagingDirectory)
1211+
displayName: copy msi
1212+
1213+
- template: upload-to-storage\win\v1.yml@yaml
1214+
parameters:
1215+
ArtifactsDirectory: $(Build.ArtifactStagingDirectory)
1216+
Azure.ContainerName: $(Azure.Container.Name)
1217+
Azure.BlobPrefix: $(Build.DefinitionName)/public/$(Build.BuildId)/$(Build.SourceBranchName)/$(Build.SourceVersion)
1218+
1219+
- template: yaml-templates/upload-results.yaml
1220+
parameters:
1221+
solution: build-tools\Xamarin.Android.Tools.BootstrapTasks\Xamarin.Android.Tools.BootstrapTasks.csproj
1222+
artifactName: dotnet-preview-msi
1223+
11181224
- stage: finalize_installers
11191225
displayName: Finalize Installers
11201226
dependsOn: mac_build
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
3+
4+
<Product Name="Microsoft.NET.Workload.Android"
5+
Id="*"
6+
Language="1033"
7+
Version="@MSIVERSION@"
8+
Manufacturer="Microsoft"
9+
UpgradeCode="86a83cc9-6b75-489f-be10-27988df907c6">
10+
<Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine" Platform="x64" />
11+
<MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
12+
<MediaTemplate CompressionLevel="high" EmbedCab="yes" />
13+
<UIRef Id="WixUI_Minimal" />
14+
<Feature Id="ProductFeature" Title="Microsoft.NET.Workload.Android">
15+
<ComponentGroupRef Id="ProductComponents" />
16+
</Feature>
17+
<WixVariable Id="WixUILicenseRtf" Value="LICENSE.rtf" />
18+
</Product>
19+
20+
<Fragment>
21+
<Directory Id="TARGETDIR" Name="SourceDir">
22+
<Directory Id="ProgramFiles64Folder">
23+
@DIRECTORIES@
24+
</Directory>
25+
</Directory>
26+
</Fragment>
27+
28+
<Fragment>
29+
@COMPONENTS@
30+
</Fragment>
31+
32+
</Wix>

0 commit comments

Comments
 (0)