Skip to content

Commit 0464c5b

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 For now, 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 1a8116a commit 0464c5b

File tree

9 files changed

+477
-8
lines changed

9 files changed

+477
-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: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
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.Android.Workload");
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+
File.WriteAllText (DestinationFile, contents);
126+
127+
return !Log.HasLoggedErrors;
128+
}
129+
130+
static void RecurseDirectory (string top_dir, XmlWriter packWriter, XmlWriter componentWriter, string directory)
131+
{
132+
var directoryId = GetId (top_dir, directory);
133+
packWriter.WriteStartElement ("Directory");
134+
packWriter.WriteAttributeString ("Id", directoryId);
135+
packWriter.WriteAttributeString ("Name", Path.GetFileName (directory));
136+
packWriter.WriteAttributeString ("FileSource", directory);
137+
foreach (var child in Directory.EnumerateDirectories (directory)) {
138+
var directoryName = Path.GetFileName (child);
139+
if (directoryName.StartsWith (".") || directoryName.StartsWith ("_"))
140+
continue;
141+
RecurseDirectory (top_dir, packWriter, componentWriter, child);
142+
}
143+
foreach (var file in Directory.EnumerateFiles (directory)) {
144+
var fileName = Path.GetFileName (file);
145+
if (fileName.StartsWith (".") || fileName.StartsWith ("_"))
146+
continue;
147+
var componentId = GetId (top_dir, file);
148+
packWriter.WriteStartElement ("Component");
149+
packWriter.WriteAttributeString ("Id", componentId);
150+
packWriter.WriteStartElement ("File");
151+
packWriter.WriteAttributeString ("Id", componentId);
152+
packWriter.WriteAttributeString ("Name", Path.GetFileName (file));
153+
packWriter.WriteAttributeString ("KeyPath", "yes");
154+
packWriter.WriteEndElement (); // </File>
155+
packWriter.WriteEndElement (); // </Component>
156+
componentWriter.WriteStartElement ("ComponentRef");
157+
componentWriter.WriteAttributeString ("Id", componentId);
158+
componentWriter.WriteEndElement (); // </ComponentRef>
159+
}
160+
packWriter.WriteEndElement (); // </Directory>
161+
}
162+
163+
static string GetId (string top_dir, string path)
164+
{
165+
if (string.IsNullOrEmpty (path))
166+
return path;
167+
if (path.Length > top_dir.Length + 1) {
168+
path = path.Substring (top_dir.Length + 1);
169+
}
170+
return GetHashString (path);
171+
}
172+
173+
static byte [] GetHash (string inputString)
174+
{
175+
using (var algorithm = SHA256.Create ())
176+
return algorithm.ComputeHash (Encoding.UTF8.GetBytes (inputString));
177+
}
178+
179+
static string GetHashString (string inputString)
180+
{
181+
var sb = new StringBuilder ("S", 65);
182+
foreach (byte b in GetHash (inputString))
183+
sb.Append (b.ToString ("X2"));
184+
return sb.ToString ();
185+
}
186+
}
187+
}
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.Android.Workload"
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.Android.Workload">
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>
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
<Project Sdk="Microsoft.Build.NoTargets">
2+
<PropertyGroup>
3+
<TargetFramework>netstandard2.0</TargetFramework>
4+
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
5+
<OutputPath>..\..\bin\Build$(Configuration)</OutputPath>
6+
<_WixTemplate>Microsoft.Android.Workload.wix.in</_WixTemplate>
7+
</PropertyGroup>
8+
9+
<Import Project="..\..\Configuration.props" />
10+
<Import Project="..\scripts\XAVersionInfo.targets" />
11+
12+
<ItemGroup>
13+
<None Remove="**" />
14+
<None Include="$(_WixTemplate)" />
15+
</ItemGroup>
16+
17+
<UsingTask AssemblyFile="$(PrepTasksAssembly)" TaskName="Xamarin.Android.BuildTools.PrepTasks.DownloadUri" />
18+
<UsingTask AssemblyFile="$(BootstrapTasksAssembly)" TaskName="Xamarin.Android.Tools.BootstrapTasks.ConvertToRichText" />
19+
<UsingTask AssemblyFile="$(BootstrapTasksAssembly)" TaskName="Xamarin.Android.Tools.BootstrapTasks.GenerateWixFile" />
20+
<UsingTask AssemblyFile="$(BootstrapTasksAssembly)" TaskName="Xamarin.Android.Tools.BootstrapTasks.UnzipDirectoryChildren" />
21+
22+
<Target Name="_DownloadWix">
23+
<DownloadUri
24+
SourceUris="https://github.com/wixtoolset/wix3/releases/download/wix3112rtm/wix311-binaries.zip"
25+
DestinationFiles="$(AndroidToolchainCacheDirectory)\wix311-binaries.zip"
26+
/>
27+
</Target>
28+
29+
<Target Name="_UnzipWix"
30+
Inputs="$(AndroidToolchainCacheDirectory)\wix311-binaries.zip"
31+
Outputs="$(WixToolPath)candle.exe;$(WixToolPath)light.exe">
32+
<UnzipDirectoryChildren
33+
NoSubdirectory="True"
34+
SourceFiles="$(AndroidToolchainCacheDirectory)\wix311-binaries.zip"
35+
DestinationFolder="$(WixToolPath)"
36+
/>
37+
</Target>
38+
39+
<Target Name="_Properties">
40+
<PropertyGroup>
41+
<LicenseSrcEn Condition=" '$(LicenseSrcEn)' == '' ">$(XamarinAndroidSourcePath)\LICENSE</LicenseSrcEn>
42+
<_LicenseFile>$(IntermediateOutputPath)LICENSE.rtf</_LicenseFile>
43+
<_WixFile>$(IntermediateOutputPath)Microsoft.Android.Workload.wix</_WixFile>
44+
<_WixObj>$(IntermediateOutputPath)Microsoft.Android.Workload.wixobj</_WixObj>
45+
<_WixMsi>$(IntermediateOutputPath)Microsoft.Android.Workload.msi</_WixMsi>
46+
</PropertyGroup>
47+
</Target>
48+
49+
<Target Name="_ConvertLicense"
50+
Inputs="$(MSBuildThisFile);$(BootstrapTasksAssembly);$(LicenseSrcEn)"
51+
Outputs="$(_LicenseFile)">
52+
<ConvertToRichText
53+
SourceFile="$(LicenseSrcEn)"
54+
DestinationFile="$(_LicenseFile)"
55+
/>
56+
<ItemGroup>
57+
<FileWrites Include="$(_LicenseFile)" />
58+
<None Include="$(_LicenseFile)" />
59+
</ItemGroup>
60+
</Target>
61+
62+
<Target Name="_GenerateWix"
63+
DependsOnTargets="GetXAVersionInfo"
64+
Inputs="$(MSBuildThisFile);$(BootstrapTasksAssembly);@(None)"
65+
Outputs="$(_WixFile)">
66+
<GenerateWixFile
67+
Template="$(_WixTemplate)"
68+
DestinationFile="$(_WixFile)"
69+
DotNetPath="$(DotNetPreviewPath)"
70+
DotNetVersion="$(DotNetPreviewVersionFull)"
71+
MSIVersion="$(AndroidMSIVersion)"
72+
/>
73+
<ItemGroup>
74+
<FileWrites Include="$(_WixFile)" />
75+
</ItemGroup>
76+
</Target>
77+
78+
<Target Name="_Candle"
79+
DependsOnTargets="_DownloadWix;_Properties;_ConvertLicense;_GenerateWix"
80+
Inputs="$(_WixFile)"
81+
Outputs="$(_WixObj)">
82+
<Exec Command="&quot;$(WixToolPath)candle.exe&quot; -arch x64 Microsoft.Android.Workload.wix" WorkingDirectory="$(IntermediateOutputPath)" />
83+
<ItemGroup>
84+
<FileWrites Include="$(_WixObj)" />
85+
</ItemGroup>
86+
</Target>
87+
88+
<Target Name="_Light"
89+
BeforeTargets="AssignTargetPaths"
90+
DependsOnTargets="_Candle"
91+
Inputs="$(_WixObj)"
92+
Outputs="$(_WixMsi)">
93+
<Exec Command="&quot;$(WixToolPath)light.exe&quot; -ext WixUIExtension -cultures:en-US Microsoft.Android.Workload.wixobj" WorkingDirectory="$(IntermediateOutputPath)" />
94+
<ItemGroup>
95+
<FileWrites Include="$(_WixMsi)" />
96+
<None Include="$(_WixMsi)" CopyToOutputDirectory="PreserveNewest" Link="Microsoft.Android.Workload.$(AndroidMSIVersion).msi" />
97+
</ItemGroup>
98+
</Target>
99+
100+
</Project>

0 commit comments

Comments
 (0)