From dda1d33f7bea9e85e6abcd5dab2642f22e9a8093 Mon Sep 17 00:00:00 2001
From: Juan Hoyos <juan.hoyos@microsoft.com>
Date: Thu, 10 Sep 2020 10:59:37 -0700
Subject: [PATCH] Enable ARM64 installers build. (#25579)

Changes WiX toolset used to 3.14 to support ARM64
Generates targeting pack from the x86/x64 leg, as it gets produced using a zip that gets generated there.
The ARM64 leg now produces all the necessary msi's, exe, and wixlib needed for the installer to generate a bundle.
---
 .azure/pipelines/ci.yml                       | 61 +++++++++++++++----
 build.ps1                                     |  5 +-
 eng/Build.props                               | 14 ++++-
 eng/targets/Wix.Common.props                  |  4 +-
 .../Windows/SharedFramework/Product.wxs       |  4 +-
 .../SharedFramework/SharedFramework.wixproj   |  4 ++
 .../Windows/SharedFrameworkBundle/Bundle.wxs  |  2 +
 .../SharedFrameworkBundle.wixproj             | 43 ++++++++-----
 .../Windows/SharedFrameworkLib/Library.wxs    |  3 +
 .../Windows/TargetingPack/Product.wxs         |  4 +-
 src/Installers/Windows/Wix.targets            |  5 ++
 11 files changed, 113 insertions(+), 36 deletions(-)

diff --git a/.azure/pipelines/ci.yml b/.azure/pipelines/ci.yml
index a3cce2ea12e..a7dc48c1289 100644
--- a/.azure/pipelines/ci.yml
+++ b/.azure/pipelines/ci.yml
@@ -51,6 +51,8 @@ variables:
            /p:DotNetPublishUsingPipelines=$(_PublishUsingPipelines)
            /p:DotNetArtifactsCategory=$(_DotNetArtifactsCategory)
   # Do not log most Windows steps in official builds; this is the slowest job. Site extensions step always logs.
+  - name: WindowsArm64LogArgs
+    value: -ExcludeCIBinaryLog
   - name: Windows64LogArgs
     value: -ExcludeCIBinaryLog
   - name: Windows86LogArgs
@@ -59,12 +61,16 @@ variables:
     value: -ExcludeCIBinaryLog
   - name: WindowsInstallersLogArgs
     value: -ExcludeCIBinaryLog
+  - name: WindowsArm64InstallersLogArgs
+    value: -ExcludeCIBinaryLog
 - ${{ if or(eq(variables['System.TeamProject'], 'public'), in(variables['Build.Reason'], 'PullRequest')) }}:
   - name: _BuildArgs
     value: '/p:SkipTestBuild=true'
   - name: _PublishArgs
     value: ''
   # Write binary logs for all main Windows build steps except the x86 one in public and PR builds.
+  - name: WindowsArm64LogArgs
+    value: /bl:artifacts/log/Release/Build.arm64.binlog
   - name: Windows64LogArgs
     value: /bl:artifacts/log/Release/Build.x64.binlog
   - name: Windows86LogArgs
@@ -73,6 +79,8 @@ variables:
     value: /bl:artifacts/log/Release/Build.CodeSign.binlog
   - name: WindowsInstallersLogArgs
     value: /bl:artifacts/log/Release/Build.Installers.binlog
+  - name: WindowsArm64InstallersLogArgs
+    value: /bl:artifacts/log/Release/Build.Installers.Arm64.binlog
 - ${{ if ne(variables['System.TeamProject'], 'internal') }}:
   - name: _UseHelixOpenQueues
     value: 'true'
@@ -263,18 +271,6 @@ stages:
       jobName: Windows_arm64_build
       jobDisplayName: "Build: Windows ARM64"
       agentOs: Windows
-      buildArgs:
-        -arch arm64
-        -sign
-        -pack
-        -noBuildNodeJS
-        -noBuildJava
-        /p:DotNetSignType=$(_SignType)
-        /p:OnlyPackPlatformSpecificPackages=true
-        /p:AssetManifestFileName=aspnetcore-win-arm64.xml
-        $(_BuildArgs)
-        $(_PublishArgs)
-        $(_InternalRuntimeDownloadArgs)
       installNodeJs: false
       installJdk: false
       artifacts:
@@ -286,6 +282,47 @@ stages:
         path: artifacts/packages/
       - name: Windows_arm64_Installers
         path: artifacts/installers/
+      steps:
+      - script: ./build.cmd
+                -ci
+                -arch arm64
+                -sign
+                -pack
+                -noBuildJava
+                -noBuildNative
+                /p:DotNetSignType=$(_SignType)
+                /p:OnlyPackPlatformSpecificPackages=true
+                $(_BuildArgs)
+                $(_InternalRuntimeDownloadArgs)
+                $(WindowsArm64LogArgs)
+        displayName: Build ARM64
+
+      # Windows installers bundle for arm64
+      - script: ./build.cmd
+                -ci
+                -noBuildRepoTasks
+                -arch arm64
+                -sign
+                -buildInstallers
+                -noBuildNative
+                /p:DotNetSignType=$(_SignType)
+                /p:AssetManifestFileName=aspnetcore-win-arm64.xml
+                $(_BuildArgs)
+                $(_PublishArgs)
+                $(_InternalRuntimeDownloadArgs)
+                $(WindowsArm64InstallersLogArgs)
+        displayName: Build Arm64 Installers
+
+      # A few files must also go to the VS package feed.
+      - ${{ if and(ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}:
+        - task: NuGetCommand@2
+          displayName: Push Visual Studio packages
+          inputs:
+            command: push
+            packagesToPush: 'artifacts/packages/**/VS.Redist.Common.AspNetCore.*.nupkg'
+            nuGetFeedType: external
+            publishFeedCredentials: 'DevDiv - VS package feed'
+
 
   # Build MacOS
   - template: jobs/default-build.yml
diff --git a/build.ps1 b/build.ps1
index 96e5cb7bf07..0d82c3b2657 100644
--- a/build.ps1
+++ b/build.ps1
@@ -296,12 +296,11 @@ if ($BuildNodeJS) { $dotnetBuildArguments += "/p:BuildNodeJS=true" }
 # Don't bother with two builds if just one will build everything. Ignore super-weird cases like
 # "-Projects ... -NoBuildJava -NoBuildManaged -NoBuildNodeJS". An empty `./build.ps1` command will build both
 # managed and native projects.
-$performDesktopBuild = ($BuildInstallers -or $BuildNative) -and `
-    -not $Architecture.StartsWith("arm", [System.StringComparison]::OrdinalIgnoreCase)
+$performDesktopBuild = ($BuildInstallers -and $Architecture -ne "arm") -or `
+    ($BuildNative -and -not $Architecture.StartsWith("arm", [System.StringComparison]::OrdinalIgnoreCase))
 $performDotnetBuild = $BuildJava -or $BuildManaged -or $BuildNodeJS -or `
     ($All -and -not ($NoBuildJava -and $NoBuildManaged -and $NoBuildNodeJS)) -or `
     ($Projects -and -not ($BuildInstallers -or $specifiedBuildNative))
-
 $foundJdk = $false
 $javac = Get-Command javac -ErrorAction Ignore -CommandType Application
 $localJdkPath = "$PSScriptRoot\.tools\jdk\win-x64\"
diff --git a/eng/Build.props b/eng/Build.props
index f0cb8db0b8a..497ba8f4f80 100644
--- a/eng/Build.props
+++ b/eng/Build.props
@@ -50,7 +50,7 @@
       </ItemGroup>
     </When>
     <Otherwise>
-      <ItemGroup Condition=" '$(BuildInstallers)' == 'true' AND '$(TargetOsName)' == 'win' ">
+      <ItemGroup Condition=" '$(BuildInstallers)' == 'true' AND '$(TargetOsName)' == 'win' AND ('$(TargetArchitecture)' == 'x86' OR '$(TargetArchitecture)' == 'x64')  ">
         <!-- Build the ANCM custom action -->
         <ProjectToBuild Include="$(RepoRoot)src\Installers\Windows\AspNetCoreModule-Setup\CustomAction\aspnetcoreCA.vcxproj" AdditionalProperties="Platform=x64" />
         <ProjectToBuild Include="$(RepoRoot)src\Installers\Windows\AspNetCoreModule-Setup\CustomAction\aspnetcoreCA.vcxproj" AdditionalProperties="Platform=Win32" />
@@ -64,6 +64,10 @@
         <!-- Build the targeting pack installers -->
         <ProjectToBuild Include="$(RepoRoot)src\Installers\Windows\TargetingPack\TargetingPack.wixproj" AdditionalProperties="Platform=x64" />
         <ProjectToBuild Include="$(RepoRoot)src\Installers\Windows\TargetingPack\TargetingPack.wixproj" AdditionalProperties="Platform=x86" />
+        <!-- This really shouldn't be here, but instead of harvesting from the intermediate/output directories, the targetting pack installer logic
+        harvests from a zip of the reference assemblies. Producing it in each leg ends up with multiple targeting packs
+        getting produced and the BAR will reject the build. Centralize building the targeting pack in the x86/x64 leg. -->
+        <ProjectToBuild Include="$(RepoRoot)src\Installers\Windows\TargetingPack\TargetingPack.wixproj" AdditionalProperties="Platform=arm64" />
 
         <!-- Build the SharedFramework installers -->
         <ProjectToBuild Include="$(RepoRoot)src\Installers\Windows\SharedFrameworkBundle\SharedFrameworkBundle.wixproj" AdditionalProperties="Platform=x64" />
@@ -77,6 +81,14 @@
         <ProjectToBuild Include="$(RepoRoot)src\Installers\Windows\WindowsHostingBundle\WindowsHostingBundle.wixproj" AdditionalProperties="Platform=x86" />
       </ItemGroup>
 
+      <ItemGroup Condition=" '$(BuildInstallers)' == 'true' AND '$(TargetOsName)' == 'win' AND '$(TargetArchitecture)' == 'arm64' ">
+        <!-- We don't build the bundle here because we'd have to bundle the x86 installer which gets built in a different leg.
+        Instead we only provide the ARM64 MSI-->
+
+        <!-- Build the SharedFramework wixlib -->
+        <ProjectToBuild Include="$(RepoRoot)src\Installers\Windows\SharedFrameworkLib\SharedFrameworkLib.wixproj" AdditionalProperties="Platform=arm64" />
+      </ItemGroup>
+
       <ItemGroup Condition="'$(BuildInstallers)' == 'true' AND '$(TargetRuntimeIdentifier)' == 'linux-x64'">
         <ProjectToBuild Condition=" '$(LinuxInstallerType)' == 'deb' "
                         Include="$(RepoRoot)src\Installers\Debian\**\*.*proj" />
diff --git a/eng/targets/Wix.Common.props b/eng/targets/Wix.Common.props
index e6b1337fcba..9a29437eb2e 100644
--- a/eng/targets/Wix.Common.props
+++ b/eng/targets/Wix.Common.props
@@ -3,8 +3,8 @@
 
   <PropertyGroup>
     <SchemaVersion>2.0</SchemaVersion>
-    <ProductVersion>3.11</ProductVersion>
-    <WixVersion>3.11.1</WixVersion>
+    <ProductVersion>3.14</ProductVersion>
+    <WixVersion>3.14.0-dotnet</WixVersion>
   </PropertyGroup>
 
   <PropertyGroup>
diff --git a/src/Installers/Windows/SharedFramework/Product.wxs b/src/Installers/Windows/SharedFramework/Product.wxs
index 2a71da323a2..3375094918c 100644
--- a/src/Installers/Windows/SharedFramework/Product.wxs
+++ b/src/Installers/Windows/SharedFramework/Product.wxs
@@ -2,7 +2,7 @@
 <Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
     <Product Id="$(var.ProductCode)" Name="$(var.ProductName)" Language="1033" Version="$(var.Version)"
              Manufacturer="Microsoft Corporation" UpgradeCode="$(var.UpgradeCode)">
-        <Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine" />
+        <Package InstallerVersion="$(var.InstallerVersion)" Compressed="yes" InstallScope="perMachine" />
 
         <MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." Schedule="afterInstallFinalize" />
         <Media Id="1" Cabinet="$(var.Cabinet)" CompressionLevel="high" EmbedCab="$(var.EmbedCab)" />
@@ -21,7 +21,7 @@
 
     <?if $(var.Platform)=x86?>
     <?define PFilesFolder=ProgramFilesFolder?>
-    <?elseif $(var.Platform)=x64?>
+    <?elseif $(var.Platform)=x64 or $(var.Platform)=arm64?>
     <?define PFilesFolder=ProgramFiles64Folder?>
     <?else?>
     <?error Invalid Platform ($(var.Platform))?>
diff --git a/src/Installers/Windows/SharedFramework/SharedFramework.wixproj b/src/Installers/Windows/SharedFramework/SharedFramework.wixproj
index edbb9f5bef0..6c87d777470 100644
--- a/src/Installers/Windows/SharedFramework/SharedFramework.wixproj
+++ b/src/Installers/Windows/SharedFramework/SharedFramework.wixproj
@@ -61,12 +61,16 @@
   <!-- TODO: https://github.com/dotnet/aspnetcore/issues/6304. Harvest shared frameworks from a project reference -->
   <Target Name="ExtractIntermediateSharedFx" BeforeTargets="PrepareForBuild">
     <PropertyGroup>
+      <SharedFrameworkArm64HarvestRootPath Condition="'$(SharedFrameworkArm64HarvestRootPath)' == ''">$(InstallersOutputPath)</SharedFrameworkArm64HarvestRootPath>
       <SharedFrameworkX64HarvestRootPath Condition="'$(SharedFrameworkX64HarvestRootPath)' == ''">$(InstallersOutputPath)</SharedFrameworkX64HarvestRootPath>
       <SharedFrameworkX86HarvestRootPath Condition="'$(SharedFrameworkX86HarvestRootPath)' == ''">$(InstallersOutputPath)</SharedFrameworkX86HarvestRootPath>
+      <IntermediateArm64SharedFxZip>$(SharedFrameworkArm64HarvestRootPath)aspnetcore-runtime-internal-$(PackageVersion)-win-arm64.zip</IntermediateArm64SharedFxZip>
       <IntermediateX64SharedFxZip>$(SharedFrameworkX64HarvestRootPath)aspnetcore-runtime-internal-$(PackageVersion)-win-x64.zip</IntermediateX64SharedFxZip>
       <IntermediateX86SharedFxZip>$(SharedFrameworkX86HarvestRootPath)aspnetcore-runtime-internal-$(PackageVersion)-win-x86.zip</IntermediateX86SharedFxZip>
     </PropertyGroup>
 
+    <Unzip Condition="'$(Platform)' == 'arm64'"
+           SourceFiles="$(IntermediateArm64SharedFxZip)" DestinationFolder="$(HarvestSource)" />
     <Unzip Condition="'$(Platform)' == 'x64'"
            SourceFiles="$(IntermediateX64SharedFxZip)" DestinationFolder="$(HarvestSource)" />
     <Unzip Condition="'$(Platform)' == 'x86'"
diff --git a/src/Installers/Windows/SharedFrameworkBundle/Bundle.wxs b/src/Installers/Windows/SharedFrameworkBundle/Bundle.wxs
index 7f89a593091..f54d1446b40 100644
--- a/src/Installers/Windows/SharedFrameworkBundle/Bundle.wxs
+++ b/src/Installers/Windows/SharedFrameworkBundle/Bundle.wxs
@@ -43,6 +43,8 @@
             <PackageGroupRef Id="PG_AspNetCoreSharedFramework_x86"/>
             <?elseif $(var.Platform)=x64?>
             <PackageGroupRef Id="PG_AspNetCoreSharedFramework_x64"/>
+            <?elseif $(var.Platform)=arm64?>
+            <PackageGroupRef Id="PG_AspNetCoreSharedFramework_arm64"/>
             <?endif?>
         </Chain>
     </Bundle>
diff --git a/src/Installers/Windows/SharedFrameworkBundle/SharedFrameworkBundle.wixproj b/src/Installers/Windows/SharedFrameworkBundle/SharedFrameworkBundle.wixproj
index 7aa16e58ec0..f9f285c8739 100644
--- a/src/Installers/Windows/SharedFrameworkBundle/SharedFrameworkBundle.wixproj
+++ b/src/Installers/Windows/SharedFrameworkBundle/SharedFrameworkBundle.wixproj
@@ -29,20 +29,35 @@
     <Content Include="thm.xml" />
   </ItemGroup>
 
-  <ItemGroup>
-    <ProjectReference Include="..\SharedFrameworkLib\SharedFrameworkLib.wixproj" SetPlatform="Platform=x86">
-      <Name>SharedFrameworkLib</Name>
-      <Project>{5244BC49-2568-4701-80A6-EAB8950AB5FA}</Project>
-      <Private>True</Private>
-      <DoNotHarvest>True</DoNotHarvest>
-    </ProjectReference>
-    <ProjectReference Include="..\SharedFrameworkLib\SharedFrameworkLib.wixproj" SetPlatform="Platform=x64">
-      <Name>SharedFrameworkLib</Name>
-      <Project>{5244BC49-2568-4701-80A6-EAB8950AB5FA}</Project>
-      <Private>True</Private>
-      <DoNotHarvest>True</DoNotHarvest>
-    </ProjectReference>
-  </ItemGroup>
+  <Choose>
+    <When Condition="'$(Platform)' == 'arm64'">
+      <ItemGroup>
+        <ProjectReference Include="..\SharedFrameworkLib\SharedFrameworkLib.wixproj" SetPlatform="Platform=arm64">
+          <Name>SharedFrameworkLib</Name>
+          <Project>{5244BC49-2568-4701-80A6-EAB8950AB5FA}</Project>
+          <Private>True</Private>
+          <DoNotHarvest>True</DoNotHarvest>
+        </ProjectReference>
+      </ItemGroup>
+    </When>
+    <Otherwise>
+      <ItemGroup>
+        <ProjectReference Include="..\SharedFrameworkLib\SharedFrameworkLib.wixproj" SetPlatform="Platform=x86">
+          <Name>SharedFrameworkLib</Name>
+          <Project>{5244BC49-2568-4701-80A6-EAB8950AB5FA}</Project>
+          <Private>True</Private>
+          <DoNotHarvest>True</DoNotHarvest>
+        </ProjectReference>
+        <ProjectReference Include="..\SharedFrameworkLib\SharedFrameworkLib.wixproj" SetPlatform="Platform=x64">
+          <Name>SharedFrameworkLib</Name>
+          <Project>{5244BC49-2568-4701-80A6-EAB8950AB5FA}</Project>
+          <Private>True</Private>
+          <DoNotHarvest>True</DoNotHarvest>
+        </ProjectReference>
+      </ItemGroup>
+    </Otherwise>
+  </Choose>
+
 
   <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), Directory.Build.targets))\Directory.Build.targets" />
 
diff --git a/src/Installers/Windows/SharedFrameworkLib/Library.wxs b/src/Installers/Windows/SharedFrameworkLib/Library.wxs
index dde16acbd7f..c30f743d0bb 100644
--- a/src/Installers/Windows/SharedFrameworkLib/Library.wxs
+++ b/src/Installers/Windows/SharedFrameworkLib/Library.wxs
@@ -16,6 +16,9 @@
             <?elseif $(var.Platform)=x64?>
             <?define SharedFrameworkInstallCondition=VersionNT64 AND (NOT OPT_NO_SHAREDFX)?>
             <?define DotNetHome=DOTNETHOME_X64?>
+            <?elseif $(var.Platform)=arm64?>
+            <?define SharedFrameworkInstallCondition=VersionNT64 AND (NOT OPT_NO_SHAREDFX)?>
+            <?define DotNetHome=DOTNETHOME_ARM64?>
             <?endif?>
 
             <MsiPackage Id="AspNetCoreSharedFramework_$(var.Platform)"
diff --git a/src/Installers/Windows/TargetingPack/Product.wxs b/src/Installers/Windows/TargetingPack/Product.wxs
index ce7bda6695e..a43f6ab4e17 100644
--- a/src/Installers/Windows/TargetingPack/Product.wxs
+++ b/src/Installers/Windows/TargetingPack/Product.wxs
@@ -2,7 +2,7 @@
 <Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
     <Product Id="$(var.ProductCode)" Name="$(var.ProductName)" Language="1033" Version="$(var.Version)"
              Manufacturer="Microsoft Corporation" UpgradeCode="$(var.UpgradeCode)">
-        <Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine" />
+        <Package InstallerVersion="$(var.InstallerVersion)" Compressed="yes" InstallScope="perMachine" />
 
         <MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." Schedule="afterInstallFinalize" />
         <Media Id="1" Cabinet="$(var.Cabinet)" CompressionLevel="high" EmbedCab="$(var.EmbedCab)" />
@@ -21,7 +21,7 @@
 
     <?if $(var.Platform)=x86?>
     <?define PFilesFolder=ProgramFilesFolder?>
-    <?elseif $(var.Platform)=x64?>
+    <?elseif $(var.Platform)=x64 or $(var.Platform)=arm64?>
     <?define PFilesFolder=ProgramFiles64Folder?>
     <?else?>
     <?error Invalid Platform ($(var.Platform))?>
diff --git a/src/Installers/Windows/Wix.targets b/src/Installers/Windows/Wix.targets
index ec321d4c41a..4112082782a 100644
--- a/src/Installers/Windows/Wix.targets
+++ b/src/Installers/Windows/Wix.targets
@@ -8,6 +8,11 @@
     <_FileRevisionVersion Condition=" '$(_FileRevisionVersion)' == '' ">42424</_FileRevisionVersion>
     <BundleVersion>$(AspNetCoreMajorVersion).$(AspNetCoreMinorVersion).$(AspNetCorePatchVersion).$(_FileRevisionVersion)</BundleVersion>
 
+    <!-- ARM64 MSIs require the installer version to be at least 500. -->
+    <!-- See: https://docs.microsoft.com/en-us/windows/win32/msi/64-bit-windows-installer-packages -->
+    <DefineConstants Condition=" '$(Platform)' == 'arm64' ">$(DefineConstants);InstallerVersion=500</DefineConstants>
+    <DefineConstants Condition=" '$(Platform)' != 'arm64' ">$(DefineConstants);InstallerVersion=200</DefineConstants>
+
     <DefineConstants>$(DefineConstants);MajorVersion=$(AspNetCoreMajorVersion)</DefineConstants>
     <DefineConstants>$(DefineConstants);MinorVersion=$(AspNetCoreMinorVersion)</DefineConstants>
     <DefineConstants>$(DefineConstants);Version=$(Version)</DefineConstants>
-- 
GitLab