Skip to content

Commit efe88a5

Browse files
[One .NET] temporary Windows & macOS installers for .NET 6
Depends on: #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. Changes to CI: * The `create-installers` make target now creates the .NET 6 `.pkg` installer. The `mac_build` stage creates one additional installer. * Any subsequent stages that download the `installers` artifact, now use a wildcard for the installer they need. This will improve some of the test stages that were downloading both the `.vsix` and `.pkg` installers before. * A `.NET 6 Preview Installers` Github status will appear that contains public download links for the new installers. * A `Build Results - .NET 6 Preview Installers` artifact will contain additional build logs.
1 parent d60d160 commit efe88a5

18 files changed

+612
-34
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,57 @@
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+
///
14+
/// For a Unicode escape the control word \u is used, followed by
15+
/// a 16-bit signed decimal integer giving the Unicode UTF-16 code
16+
/// unit number. More information under 'Character encoding' here:
17+
/// https://en.wikipedia.org/wiki/Rich_Text_Format
18+
/// </summary>
19+
public class ConvertToRichText : Task
20+
{
21+
[Required]
22+
public string SourceFile { get; set; }
23+
24+
[Required]
25+
public string DestinationFile { get; set; }
26+
27+
public override bool Execute ()
28+
{
29+
var text = File.ReadAllText (SourceFile);
30+
31+
text = text
32+
.Replace (@"\", @"\\")
33+
.Replace ("{", @"\{")
34+
.Replace ("}", @"\}")
35+
// Only want to keep "double" new lines
36+
.Replace (Environment.NewLine + Environment.NewLine, $@"\par{Environment.NewLine} \par{Environment.NewLine} ")
37+
.Replace (Environment.NewLine, " ");
38+
39+
Directory.CreateDirectory (Path.GetDirectoryName (DestinationFile));
40+
using (var writer = File.CreateText (DestinationFile)) {
41+
writer.Write (@"{\rtf1\ansi\ansicpg1250\deff0{\fonttbl\f0\fcharset0 Courier New;}\f0\pard ");
42+
foreach (char letter in text) {
43+
if (letter <= 0x7f) {
44+
writer.Write (letter);
45+
} else {
46+
writer.Write ("\\u");
47+
writer.Write (Convert.ToUInt32 (letter));
48+
writer.Write ("?");
49+
}
50+
}
51+
writer.Write (" } ");
52+
}
53+
54+
return !Log.HasLoggedErrors;
55+
}
56+
}
57+
}
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-oss.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,8 +103,8 @@ stages:
103103
displayName: make jenkins
104104
105105
- script: >
106-
echo "make create-installers V=1 CONFIGURATION=$(XA.Build.Configuration)" &&
107-
make create-installers V=1 CONFIGURATION=$(XA.Build.Configuration)
106+
echo "make create-pkg create-vsix V=1 CONFIGURATION=$(XA.Build.Configuration)" &&
107+
make create-pkg create-vsix V=1 CONFIGURATION=$(XA.Build.Configuration)
108108
workingDirectory: $(Build.SourcesDirectory)
109109
displayName: create installers
110110

build-tools/automation/azure-pipelines.yaml

Lines changed: 64 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,13 @@ stages:
203203
artifactName: $(TestAssembliesArtifactName)
204204
targetPath: xamarin-android/bin/Test$(XA.Build.Configuration)
205205

206+
- task: MSBuild@1
207+
displayName: pack all nupkgs
208+
inputs:
209+
solution: $(System.DefaultWorkingDirectory)/xamarin-android/build-tools/create-packs/Microsoft.Android.Sdk.proj
210+
configuration: $(XA.Build.Configuration)
211+
msbuildArguments: /t:CreateAllPacks /restore /p:NuGetLicense=$(System.DefaultWorkingDirectory)/xamarin-android/external/monodroid/tools/scripts/License.txt /bl:$(System.DefaultWorkingDirectory)/xamarin-android/bin/Build$(XA.Build.Configuration)/create-all-packs.binlog
212+
206213
# Create installers
207214
- script: make create-installers V=1 CONFIGURATION=$(XA.Build.Configuration)
208215
workingDirectory: $(System.DefaultWorkingDirectory)/xamarin-android
@@ -227,13 +234,6 @@ stages:
227234
artifactName: $(InstallerArtifactName)
228235
targetPath: xamarin-android/bin/Build$(XA.Build.Configuration)/$(InstallerArtifactName)
229236

230-
- task: MSBuild@1
231-
displayName: pack all nupkgs
232-
inputs:
233-
solution: $(System.DefaultWorkingDirectory)/xamarin-android/build-tools/create-packs/Microsoft.Android.Sdk.proj
234-
configuration: $(XA.Build.Configuration)
235-
msbuildArguments: /t:CreateAllPacks /restore /p:NuGetLicense=$(System.DefaultWorkingDirectory)/xamarin-android/external/monodroid/tools/scripts/License.txt /bl:$(System.DefaultWorkingDirectory)/xamarin-android/bin/Build$(XA.Build.Configuration)/create-all-packs.binlog
236-
237237
- task: NuGetCommand@2
238238
displayName: push nupkgs
239239
inputs:
@@ -1115,6 +1115,60 @@ stages:
11151115

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

1118+
- stage: dotnet_installers
1119+
displayName: .NET 6 Preview Installers
1120+
dependsOn: mac_build
1121+
jobs:
1122+
# Check - "Xamarin.Android (.NET 6 Preview Installers Create .msi and Upload)"
1123+
- job: dotnet_preview_installers
1124+
displayName: Create .msi and Upload
1125+
pool: $(HostedWinVS2019)
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)\bin\Build$(XA.Build.Configuration)\nupkgs
1136+
1137+
- task: DownloadPipelineArtifact@2
1138+
inputs:
1139+
artifactName: $(InstallerArtifactName)
1140+
downloadPath: $(System.DefaultWorkingDirectory)\installer-artifacts
1141+
patterns: Microsoft.*.pkg
1142+
1143+
- task: MSBuild@1
1144+
displayName: msbuild Xamarin.Android.BootstrapTasks
1145+
inputs:
1146+
solution: Xamarin.Android.BootstrapTasks.sln
1147+
configuration: $(XA.Build.Configuration)
1148+
msbuildArguments: /restore /bl:$(System.DefaultWorkingDirectory)\bin\Build$(XA.Build.Configuration)\msbuild-bootstraptasks.binlog
1149+
1150+
- task: MSBuild@1
1151+
displayName: msbuild /t:CreateWorkloadInstallers
1152+
inputs:
1153+
solution: Xamarin.Android.sln
1154+
configuration: $(XA.Build.Configuration)
1155+
msbuildArguments: /t:CreateWorkloadInstallers /bl:$(System.DefaultWorkingDirectory)\bin\Build$(XA.Build.Configuration)\msbuild-workload.binlog
1156+
1157+
- script: copy /Y $(System.DefaultWorkingDirectory)\bin\Build$(XA.Build.Configuration)\*.msi $(System.DefaultWorkingDirectory)\installer-artifacts
1158+
displayName: copy .msi
1159+
1160+
- template: upload-to-storage\win\v1.yml@yaml
1161+
parameters:
1162+
ArtifactsDirectory: $(System.DefaultWorkingDirectory)\installer-artifacts
1163+
Azure.ContainerName: $(Azure.Container.Name)
1164+
Azure.BlobPrefix: $(Build.DefinitionName)/public/$(Build.BuildId)/$(Build.SourceBranchName)/$(Build.SourceVersion)
1165+
GitHub.Context: .NET 6 Preview Installers
1166+
1167+
- template: yaml-templates/upload-results.yaml
1168+
parameters:
1169+
solution: build-tools\Xamarin.Android.Tools.BootstrapTasks\Xamarin.Android.Tools.BootstrapTasks.csproj
1170+
artifactName: Build Results - .NET 6 Preview Installers
1171+
11181172
- stage: finalize_installers
11191173
displayName: Finalize Installers
11201174
dependsOn: mac_build
@@ -1148,6 +1202,9 @@ stages:
11481202
inputs:
11491203
artifactName: $(InstallerArtifactName)
11501204
downloadPath: $(System.DefaultWorkingDirectory)/storage-artifacts
1205+
patterns: |
1206+
xamarin.android*.pkg
1207+
Xamarin.Android*.vsix
11511208
11521209
- powershell: |
11531210
$pkg = Get-ChildItem -Path "$(System.DefaultWorkingDirectory)/storage-artifacts/*" -Include *.pkg -File

build-tools/automation/yaml-templates/run-installer.yaml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,15 @@ steps:
66
inputs:
77
artifactName: $(InstallerArtifactName)
88
downloadPath: $(System.DefaultWorkingDirectory)
9+
patterns: xamarin.android*.pkg
10+
condition: and(succeeded(), eq(variables['agent.os'], 'Darwin'))
11+
12+
- task: DownloadPipelineArtifact@2
13+
inputs:
14+
artifactName: $(InstallerArtifactName)
15+
downloadPath: $(System.DefaultWorkingDirectory)
16+
patterns: Xamarin.Android*.vsix
17+
condition: and(succeeded(), eq(variables['agent.os'], 'Windows_NT'))
918

1019
- powershell: |
1120
$itemPattern = "*.vsix"

0 commit comments

Comments
 (0)