mirror of
https://github.com/aphrody-labs/PowerShell.git
synced 2026-10-14 21:59:20 +00:00
`Test-Bom` was only used in `New-MSIPackage`, which was removed in https://github.com/PowerShell/PowerShell/pull/27213 as we are not building MSI packages for v7.7 and onward. `Test-Bom` is no longer used anywhere in PowerShell repo, so removing it and its helpers, as well as the boms JSON files, in this PR.
4884 lines
184 KiB
PowerShell
4884 lines
184 KiB
PowerShell
# Copyright (c) Microsoft Corporation.
|
|
# Licensed under the MIT License.
|
|
|
|
. "$PSScriptRoot\..\buildCommon\startNativeExecution.ps1"
|
|
|
|
$Environment = Get-EnvironmentInformation
|
|
$RepoRoot = (Resolve-Path -Path "$PSScriptRoot/../..").Path
|
|
|
|
$packagingStrings = Import-PowerShellDataFile "$PSScriptRoot\packaging.strings.psd1"
|
|
Import-Module "$PSScriptRoot\..\Xml" -ErrorAction Stop -Force
|
|
$DebianDistributions = @("deb")
|
|
$RedhatFullDistributions = @("rh")
|
|
$RedhatFddDistributions = @("cm")
|
|
$RedhatDistributions = @()
|
|
$RedhatDistributions += $RedhatFullDistributions
|
|
$RedhatDistributions += $RedhatFddDistributions
|
|
$AllDistributions = @()
|
|
$AllDistributions += $DebianDistributions
|
|
$AllDistributions += $RedhatDistributions
|
|
$AllDistributions += 'macOs'
|
|
$script:netCoreRuntime = 'net11.0'
|
|
$script:iconFileName = "Powershell_black_64.png"
|
|
$script:iconPath = Join-Path -path $PSScriptRoot -ChildPath "../../assets/$iconFileName" -Resolve
|
|
|
|
class R2RVerification {
|
|
[ValidateSet('NoR2R','R2R','SdkOnly')]
|
|
[string]
|
|
$R2RState = 'R2R'
|
|
|
|
[System.Reflection.PortableExecutable.Machine]
|
|
$Architecture = [System.Reflection.PortableExecutable.Machine]::Amd64
|
|
|
|
[ValidateSet('Linux','Apple','Windows')]
|
|
[string]
|
|
$OperatingSystem = 'Windows'
|
|
}
|
|
|
|
function Start-PSPackage {
|
|
[CmdletBinding(DefaultParameterSetName='Version',SupportsShouldProcess=$true)]
|
|
param(
|
|
# PowerShell packages use Semantic Versioning https://semver.org/
|
|
[Parameter(ParameterSetName = "Version")]
|
|
[string]$Version,
|
|
|
|
[Parameter(ParameterSetName = "ReleaseTag")]
|
|
[ValidatePattern("^v\d+\.\d+\.\d+(-\w+(\.\d{1,2})?)?$")]
|
|
[ValidateNotNullOrEmpty()]
|
|
[string]$ReleaseTag,
|
|
|
|
# Package name
|
|
[ValidatePattern("^powershell")]
|
|
[string]$Name = "powershell",
|
|
|
|
# Ubuntu, CentOS, Fedora, macOS, and Windows packages are supported
|
|
[ValidateSet("msix", "deb", "deb-arm64", "osxpkg", "rpm", "rpm-fxdependent", "rpm-fxdependent-arm64", "zip", "zip-pdb", "tar", "tar-arm", "tar-arm64", "tar-alpine", "fxdependent", "fxdependent-win-desktop", "min-size-x64", "min-size-arm64", "tar-alpine-fxdependent")]
|
|
[string[]]$Type,
|
|
|
|
# Generate windows downlevel package
|
|
[ValidateSet("win7-x86", "win7-x64", "win-arm", "win-arm64")]
|
|
[string] $WindowsRuntime,
|
|
|
|
[ValidateSet('osx-x64', 'osx-arm64')]
|
|
[ValidateScript({$Environment.IsMacOS})]
|
|
[string] $MacOSRuntime,
|
|
|
|
[string] $PackageBinPath,
|
|
|
|
[switch] $Private,
|
|
|
|
[Switch] $Force,
|
|
|
|
[Switch] $SkipReleaseChecks,
|
|
|
|
[switch] $NoSudo,
|
|
|
|
[switch] $LTS
|
|
)
|
|
|
|
DynamicParam {
|
|
if ($Type -contains 'zip' -or $Type -like 'min-size*' -or $Type -like 'fxdependent*') {
|
|
# Add a dynamic parameter '-IncludeSymbols' when the specified package type is essentially a 'zip' package.
|
|
# The '-IncludeSymbols' parameter can be used to indicate that the package should only contain powershell binaries and symbols.
|
|
$ParameterAttr = New-Object "System.Management.Automation.ParameterAttribute"
|
|
$Attributes = New-Object "System.Collections.ObjectModel.Collection``1[System.Attribute]"
|
|
$Attributes.Add($ParameterAttr) > $null
|
|
|
|
$Parameter = New-Object "System.Management.Automation.RuntimeDefinedParameter" -ArgumentList ("IncludeSymbols", [switch], $Attributes)
|
|
$Dict = New-Object "System.Management.Automation.RuntimeDefinedParameterDictionary"
|
|
$Dict.Add("IncludeSymbols", $Parameter) > $null
|
|
return $Dict
|
|
}
|
|
}
|
|
|
|
End {
|
|
$IncludeSymbols = $null
|
|
if ($PSBoundParameters.ContainsKey('IncludeSymbols')) {
|
|
Write-Log 'setting IncludeSymbols'
|
|
$IncludeSymbols = $PSBoundParameters['IncludeSymbols']
|
|
}
|
|
|
|
# Runtime and Configuration settings required by the package
|
|
($Runtime, $Configuration) = if ($WindowsRuntime) {
|
|
$WindowsRuntime, "Release"
|
|
} elseif ($MacOSRuntime) {
|
|
$MacOSRuntime, "Release"
|
|
} elseif ($Type.Count -eq 1 -and $Type[0] -eq "tar-alpine") {
|
|
New-PSOptions -Configuration "Release" -Runtime "linux-musl-x64" -WarningAction SilentlyContinue | ForEach-Object { $_.Runtime, $_.Configuration }
|
|
} elseif ($Type.Count -eq 1 -and $Type[0] -eq "tar-arm") {
|
|
New-PSOptions -Configuration "Release" -Runtime "linux-arm" -WarningAction SilentlyContinue | ForEach-Object { $_.Runtime, $_.Configuration }
|
|
} elseif ($Type.Count -eq 1 -and $Type[0] -eq "tar-arm64") {
|
|
if ($IsMacOS) {
|
|
New-PSOptions -Configuration "Release" -Runtime "osx-arm64" -WarningAction SilentlyContinue | ForEach-Object { $_.Runtime, $_.Configuration }
|
|
} else {
|
|
New-PSOptions -Configuration "Release" -Runtime "linux-arm64" -WarningAction SilentlyContinue | ForEach-Object { $_.Runtime, $_.Configuration }
|
|
}
|
|
} elseif ($Type.Count -eq 1 -and $Type[0] -eq "rpm-fxdependent") {
|
|
New-PSOptions -Configuration "Release" -Runtime 'fxdependent-linux-x64' -WarningAction SilentlyContinue | ForEach-Object { $_.Runtime, $_.Configuration }
|
|
} elseif ($Type.Count -eq 1 -and $Type[0] -eq "rpm-fxdependent-arm64") {
|
|
New-PSOptions -Configuration "Release" -Runtime 'fxdependent-linux-arm64' -WarningAction SilentlyContinue | ForEach-Object { $_.Runtime, $_.Configuration }
|
|
}
|
|
elseif ($Type.Count -eq 1 -and $Type[0] -eq "tar-alpine-fxdependent") {
|
|
New-PSOptions -Configuration "Release" -Runtime 'fxdependent-noopt-linux-musl-x64' -WarningAction SilentlyContinue | ForEach-Object { $_.Runtime, $_.Configuration }
|
|
}
|
|
elseif ($Type.Count -eq 1 -and $Type[0] -eq "deb-arm64") {
|
|
New-PSOptions -Configuration "Release" -Runtime 'linux-arm64' -WarningAction SilentlyContinue | ForEach-Object { $_.Runtime, $_.Configuration }
|
|
}
|
|
elseif ($Type.Count -eq 1 -and $Type[0] -eq "min-size-arm64") {
|
|
$runtimeToUse = if ($IsMacOS) { "osx-arm64" } elseif ($IsLinux) { "linux-arm64" } else { "win-arm64" }
|
|
New-PSOptions -Configuration "Release" -Runtime $runtimeToUse -WarningAction SilentlyContinue | ForEach-Object { $_.Runtime, $_.Configuration }
|
|
}
|
|
else {
|
|
New-PSOptions -Configuration "Release" -WarningAction SilentlyContinue | ForEach-Object { $_.Runtime, $_.Configuration }
|
|
}
|
|
|
|
if ($Environment.IsWindows) {
|
|
# Runtime will be one of win7-x64, win7-x86, "win-arm" and "win-arm64" on Windows.
|
|
# Build the name suffix for universal win-plat packages.
|
|
switch ($Runtime) {
|
|
"win-arm64" { $NameSuffix = "win-arm64" }
|
|
default { $NameSuffix = $_ -replace 'win\d+', 'win' }
|
|
}
|
|
}
|
|
|
|
if ($Type -eq 'fxdependent') {
|
|
$NameSuffix = "win-fxdependent"
|
|
Write-Log "Packaging : '$Type'; Packaging Configuration: '$Configuration', Runtime: '$Runtime'"
|
|
} elseif ($Type -eq 'fxdependent-win-desktop') {
|
|
$NameSuffix = "win-fxdependentWinDesktop"
|
|
Write-Log "Packaging : '$Type'; Packaging Configuration: '$Configuration', Runtime: '$Runtime'"
|
|
} elseif ($MacOSRuntime) {
|
|
$NameSuffix = $MacOSRuntime
|
|
Write-Log "Packaging : '$Type'; Packaging Configuration: '$Configuration', Runtime: '$Runtime'"
|
|
} else {
|
|
Write-Log "Packaging RID: '$Runtime'; Packaging Configuration: '$Configuration'"
|
|
}
|
|
|
|
$Script:Options = Get-PSOptions
|
|
$actualParams = @()
|
|
|
|
$PSModuleRestoreCorrect = $false
|
|
|
|
# Require PSModuleRestore for packaging without symbols
|
|
# But Disallow it when packaging with symbols
|
|
if (!$IncludeSymbols.IsPresent -and $Script:Options.PSModuleRestore) {
|
|
$actualParams += '-PSModuleRestore'
|
|
$PSModuleRestoreCorrect = $true
|
|
}
|
|
elseif ($IncludeSymbols.IsPresent -and !$Script:Options.PSModuleRestore) {
|
|
$PSModuleRestoreCorrect = $true
|
|
}
|
|
else {
|
|
$actualParams += '-PSModuleRestore'
|
|
}
|
|
|
|
$precheckFailed = if ($Type -like 'fxdependent*' -or $Type -eq 'tar-alpine') {
|
|
## We do not check on runtime for framework dependent package.
|
|
-not $Script:Options -or ## Start-PSBuild hasn't been executed yet
|
|
-not $PSModuleRestoreCorrect -or ## Last build didn't specify '-PSModuleRestore' correctly
|
|
$Script:Options.Configuration -ne $Configuration -or ## Last build was with configuration other than 'Release'
|
|
$Script:Options.Framework -ne $script:netCoreRuntime ## Last build wasn't for CoreCLR
|
|
} else {
|
|
-not $Script:Options -or ## Start-PSBuild hasn't been executed yet
|
|
-not $PSModuleRestoreCorrect -or ## Last build didn't specify '-PSModuleRestore' correctly
|
|
$Script:Options.Runtime -ne $Runtime -or ## Last build wasn't for the required RID
|
|
$Script:Options.Configuration -ne $Configuration -or ## Last build was with configuration other than 'Release'
|
|
$Script:Options.Framework -ne $script:netCoreRuntime ## Last build wasn't for CoreCLR
|
|
}
|
|
|
|
# Make sure the most recent build satisfies the package requirement
|
|
if ($precheckFailed) {
|
|
# It's possible that the most recent build doesn't satisfy the package requirement but
|
|
# an earlier build does.
|
|
# It's also possible that the last build actually satisfies the package requirement but
|
|
# then `Start-PSPackage` runs from a new PS session or `build.psm1` was reloaded.
|
|
#
|
|
# In these cases, the user will be asked to build again even though it's technically not
|
|
# necessary. However, we want it that way -- being very explict when generating packages.
|
|
# This check serves as a simple gate to ensure that the user knows what he is doing, and
|
|
# also ensure `Start-PSPackage` does what the user asks/expects, because once packages
|
|
# are generated, it'll be hard to verify if they were built from the correct content.
|
|
|
|
Write-Warning -Message "Start-PSPackage: The build PreCheck has failed."
|
|
if (-not $Script:Options) {
|
|
Write-Warning -Message "Start-PSPackage: builid options variable is null indicating Start-PSBuild hasn't been run yet."
|
|
}
|
|
if (-not $PSModuleRestoreCorrect) {
|
|
Write-Warning -Message "Start-PSPackage: PSModuleRestoreCorrect variable is null indicating build -PSModuleRestore was not performed."
|
|
}
|
|
if ($Script:Options.Configuration -ne $Configuration) {
|
|
Write-Warning -Message "Start-PSPackage: Build configuration is incorrect: Expected: $Configuration Actual: $($Script:Options.Configuration)"
|
|
}
|
|
if ($Script:Options.Framework -ne $script:netCoreRuntime) {
|
|
Write-Warning -Message "Start-PSPackage: Build .NET version is incorrect: Expected: $($script:netCoreRuntime) Actual: $($Script:Options.Framework)"
|
|
}
|
|
if (($Type -notlike 'fxdependent*' -and $Type -ne 'tar-alpine') -and ($Script:Options.Runtime -ne $Runtime)) {
|
|
Write-Warning -Message "Start-PSPackage: Build RID does not match expected RID: Expected: $Runtime Actual: $($Script:Options.Runtime)"
|
|
}
|
|
|
|
$params = @('-Clean')
|
|
|
|
if (!$IncludeSymbols.IsPresent) {
|
|
$params += '-PSModuleRestore'
|
|
}
|
|
|
|
$actualParams += '-Runtime ' + $Script:Options.Runtime
|
|
|
|
if ($Type -eq 'fxdependent') {
|
|
$params += '-Runtime', 'fxdependent'
|
|
} elseif ($Type -eq 'fxdependent-win-desktop') {
|
|
$params += '-Runtime', 'fxdependent-win-desktop'
|
|
} else {
|
|
$params += '-Runtime', $Runtime
|
|
}
|
|
|
|
$params += '-Configuration', $Configuration
|
|
$actualParams += '-Configuration ' + $Script:Options.Configuration
|
|
|
|
Write-Warning "Build started with unexpected parameters 'Start-PSBuild $actualParams"
|
|
throw "Please ensure you have run 'Start-PSBuild $params'!"
|
|
}
|
|
|
|
if ($SkipReleaseChecks.IsPresent) {
|
|
Write-Warning "Skipping release checks."
|
|
}
|
|
elseif (!$Script:Options.RootInfo.IsValid){
|
|
throw $Script:Options.RootInfo.Warning
|
|
}
|
|
|
|
# If ReleaseTag is specified, use the given tag to calculate Version
|
|
if ($PSCmdlet.ParameterSetName -eq "ReleaseTag") {
|
|
$Version = $ReleaseTag -Replace '^v'
|
|
}
|
|
|
|
# Use Git tag if not given a version
|
|
if (-not $Version) {
|
|
$Version = (git --git-dir="$RepoRoot/.git" describe) -Replace '^v'
|
|
}
|
|
|
|
$Source = if ($PackageBinPath) {
|
|
$PackageBinPath
|
|
}
|
|
else {
|
|
Split-Path -Path $Script:Options.Output -Parent
|
|
}
|
|
|
|
Write-Verbose -Verbose "Source: $Source"
|
|
|
|
# Copy the ThirdPartyNotices.txt so it's part of the package
|
|
Copy-Item "$RepoRoot/ThirdPartyNotices.txt" -Destination $Source -Force
|
|
|
|
# Copy the default.help.txt so it's part of the package
|
|
Copy-Item "$RepoRoot/assets/default.help.txt" -Destination "$Source/en-US" -Force
|
|
|
|
if (-not $SkipGenerateReleaseFiles -and -not $env:TF_BUILD) {
|
|
# Make sure psoptions.json file exists so appropriate files.wsx is generated
|
|
$psOptionsPath = (Join-Path -Path $Source "psoptions.json")
|
|
if (-not (Test-Path -Path $psOptionsPath)) {
|
|
$createdOptionsFile = New-Item -Path $psOptionsPath -Force
|
|
Write-Verbose -Verbose "Created psoptions file: $createdOptionsFile"
|
|
}
|
|
|
|
# Make sure _manifest\spdx_2.2\manifest.spdx.json file exists so appropriate files.wxs is generated
|
|
$manifestSpdxPath = (Join-Path -Path $Source "_manifest\spdx_2.2\manifest.spdx.json")
|
|
if (-not (Test-Path -Path $manifestSpdxPath)) {
|
|
$createdSpdxPath = New-Item -Path $manifestSpdxPath -Force
|
|
Write-Verbose -Verbose "Created manifest.spdx.json file: $createdSpdxPath"
|
|
}
|
|
|
|
$manifestSpdxPathSha = (Join-Path -Path $Source "_manifest\spdx_2.2\manifest.spdx.json.sha256")
|
|
if (-not (Test-Path -Path $manifestSpdxPathSha)) {
|
|
$createdSpdxPathSha = New-Item -Path $manifestSpdxPathSha -Force
|
|
Write-Verbose -Verbose "Created manifest.spdx.json.sha256 file: $createdSpdxPathSha"
|
|
}
|
|
|
|
$bsiJsonPath = (Join-Path -Path $Source "_manifest\spdx_2.2\bsi.json")
|
|
if (-not (Test-Path -Path $bsiJsonPath)) {
|
|
$createdBsiJsonPath = New-Item -Path $bsiJsonPath -Force
|
|
Write-Verbose -Verbose "Created bsi.json file: $createdBsiJsonPath"
|
|
}
|
|
|
|
$manifestCatPath = (Join-Path -Path $Source "_manifest\spdx_2.2\manifest.cat")
|
|
if (-not (Test-Path -Path $manifestCatPath)) {
|
|
$createdCatPath = New-Item -Path $manifestCatPath -Force
|
|
Write-Verbose -Verbose "Created manifest.cat file: $createdCatPath"
|
|
}
|
|
}
|
|
|
|
# If building a symbols package, we add a zip of the parent to publish
|
|
if ($IncludeSymbols.IsPresent)
|
|
{
|
|
$publishSource = $Source
|
|
$buildSource = Split-Path -Path $Source -Parent
|
|
$Source = New-TempFolder
|
|
$symbolsSource = New-TempFolder
|
|
|
|
try
|
|
{
|
|
# Copy files which go into the root package
|
|
Get-ChildItem -Path $publishSource | Copy-Item -Destination $Source -Recurse
|
|
|
|
$signingXml = [xml] (Get-Content (Join-Path $PSScriptRoot "..\releaseBuild\signing.xml" -Resolve))
|
|
# Only include the files we sign for compliance scanning, those are the files we build.
|
|
$filesToInclude = $signingXml.SignConfigXML.job.file.src | Where-Object { -not $_.endswith('pwsh.exe') -and ($_.endswith(".dll") -or $_.endswith(".exe")) } | ForEach-Object { ($_ -split '\\')[-1] }
|
|
$filesToInclude += $filesToInclude | ForEach-Object { $_ -replace '.dll', '.pdb' }
|
|
Get-ChildItem -Path $buildSource | Where-Object { $_.Name -in $filesToInclude } | Copy-Item -Destination $symbolsSource -Recurse
|
|
|
|
# Zip symbols.zip to the root package
|
|
$zipSource = Join-Path $symbolsSource -ChildPath '*'
|
|
$zipPath = Join-Path -Path $Source -ChildPath 'symbols.zip'
|
|
Save-PSOptions -PSOptionsPath (Join-Path -Path $source -ChildPath 'psoptions.json') -Options $Script:Options
|
|
Compress-Archive -Path $zipSource -DestinationPath $zipPath
|
|
}
|
|
finally
|
|
{
|
|
Remove-Item -Path $symbolsSource -Recurse -Force -ErrorAction SilentlyContinue
|
|
}
|
|
}
|
|
|
|
Write-Log "Packaging Source: '$Source'"
|
|
|
|
# Decide package output type
|
|
if (-not $Type) {
|
|
$Type = if ($Environment.IsLinux) {
|
|
if ($Environment.LinuxInfo.ID -match "ubuntu") {
|
|
"deb", "tar"
|
|
} elseif ($Environment.IsRedHatFamily) {
|
|
"rpm"
|
|
} elseif ($Environment.IsSUSEFamily) {
|
|
"rpm"
|
|
} else {
|
|
throw "Building packages for $($Environment.LinuxInfo.PRETTY_NAME) is unsupported!"
|
|
}
|
|
} elseif ($Environment.IsMacOS) {
|
|
"osxpkg", "tar"
|
|
} elseif ($Environment.IsWindows) {
|
|
"zip", "msix"
|
|
}
|
|
Write-Warning "-Type was not specified, continuing with $Type!"
|
|
}
|
|
Write-Log "Packaging Type: $Type"
|
|
|
|
# Add the symbols to the suffix
|
|
# if symbols are specified to be included
|
|
if ($IncludeSymbols.IsPresent -and $NameSuffix) {
|
|
$NameSuffix = "symbols-$NameSuffix"
|
|
}
|
|
elseif ($IncludeSymbols.IsPresent) {
|
|
$NameSuffix = "symbols"
|
|
}
|
|
|
|
switch ($Type) {
|
|
"zip" {
|
|
$os, $architecture = ($Script:Options.Runtime -split '-')
|
|
$peOS = ConvertTo-PEOperatingSystem -OperatingSystem $os
|
|
$peArch = ConvertTo-PEArchitecture -Architecture $architecture
|
|
|
|
$Arguments = @{
|
|
PackageNameSuffix = $NameSuffix
|
|
PackageSourcePath = $Source
|
|
PackageVersion = $Version
|
|
Force = $Force
|
|
}
|
|
|
|
if ($architecture -in 'x86', 'x64', 'arm', 'arm64') {
|
|
$Arguments += @{ R2RVerification = [R2RVerification]@{
|
|
R2RState = 'R2R'
|
|
OperatingSystem = $peOS
|
|
Architecture = $peArch
|
|
}
|
|
}
|
|
} else {
|
|
$Arguments += @{ R2RVerification = [R2RVerification]@{
|
|
R2RState = 'SdkOnly'
|
|
OperatingSystem = $peOS
|
|
Architecture = $peArch
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($PSCmdlet.ShouldProcess("Create Zip Package")) {
|
|
New-ZipPackage @Arguments
|
|
}
|
|
}
|
|
"zip-pdb" {
|
|
$Arguments = @{
|
|
PackageNameSuffix = $NameSuffix
|
|
PackageSourcePath = $Source
|
|
PackageVersion = $Version
|
|
Force = $Force
|
|
}
|
|
|
|
if ($PSCmdlet.ShouldProcess("Create Symbols Zip Package")) {
|
|
New-PdbZipPackage @Arguments
|
|
}
|
|
}
|
|
{ $_ -like "min-size*" } {
|
|
# Add suffix '-gc' because this package is for the Guest Config team.
|
|
if ($Environment.IsWindows) {
|
|
$Arguments = @{
|
|
PackageNameSuffix = "$NameSuffix-gc"
|
|
PackageSourcePath = $Source
|
|
PackageVersion = $Version
|
|
Force = $Force
|
|
R2RVerification = [R2RVerification]@{
|
|
R2RState = 'SdkOnly'
|
|
}
|
|
}
|
|
|
|
if ($PSCmdlet.ShouldProcess("Create min-size Zip Package")) {
|
|
New-ZipPackage @Arguments
|
|
}
|
|
}
|
|
elseif ($Environment.IsLinux) {
|
|
$Arguments = @{
|
|
PackageSourcePath = $Source
|
|
Name = $Name
|
|
PackageNameSuffix = 'gc'
|
|
Architecture = $Runtime.Split('-')[1]
|
|
Version = $Version
|
|
Force = $Force
|
|
R2RVerification = [R2RVerification]@{
|
|
R2RState = 'SdkOnly'
|
|
}
|
|
}
|
|
|
|
if ($PSCmdlet.ShouldProcess("Create min-size tar.gz Package")) {
|
|
New-TarballPackage @Arguments
|
|
}
|
|
}
|
|
else {
|
|
throw "The 'min-size*' package types are supported only on Windows and Linux."
|
|
}
|
|
}
|
|
{ $_ -like "fxdependent*" } {
|
|
if ($Environment.IsWindows) {
|
|
$Arguments = @{
|
|
PackageNameSuffix = $NameSuffix
|
|
PackageSourcePath = $Source
|
|
PackageVersion = $Version
|
|
Force = $Force
|
|
R2RVerification = [R2RVerification]@{
|
|
R2RState = 'NoR2R'
|
|
}
|
|
}
|
|
|
|
if ($PSCmdlet.ShouldProcess("Create Zip Package")) {
|
|
New-ZipPackage @Arguments
|
|
}
|
|
} elseif ($Environment.IsLinux) {
|
|
$Arguments = @{
|
|
PackageSourcePath = $Source
|
|
Name = $Name
|
|
PackageNameSuffix = 'fxdependent'
|
|
Version = $Version
|
|
Force = $Force
|
|
R2RVerification = [R2RVerification]@{
|
|
R2RState = 'NoR2R'
|
|
}
|
|
}
|
|
|
|
if ($PSCmdlet.ShouldProcess("Create tar.gz Package")) {
|
|
New-TarballPackage @Arguments
|
|
}
|
|
}
|
|
}
|
|
"tar-alpine-fxdependent" {
|
|
if ($Environment.IsLinux) {
|
|
$Arguments = @{
|
|
PackageSourcePath = $Source
|
|
Name = $Name
|
|
PackageNameSuffix = 'musl-noopt-fxdependent'
|
|
Version = $Version
|
|
Force = $Force
|
|
R2RVerification = [R2RVerification]@{
|
|
R2RState = 'NoR2R'
|
|
OperatingSystem = "Linux"
|
|
}
|
|
}
|
|
|
|
if ($PSCmdlet.ShouldProcess("Create tar.gz Package")) {
|
|
New-TarballPackage @Arguments
|
|
}
|
|
}
|
|
}
|
|
"msix" {
|
|
$Arguments = @{
|
|
ProductNameSuffix = $NameSuffix
|
|
ProductSourcePath = $Source
|
|
ProductVersion = $Version
|
|
Architecture = $WindowsRuntime.Split('-')[1]
|
|
Force = $Force
|
|
Private = $Private
|
|
LTS = $LTS
|
|
}
|
|
|
|
if ($PSCmdlet.ShouldProcess("Create MSIX Package")) {
|
|
New-MSIXPackage @Arguments
|
|
}
|
|
}
|
|
"tar" {
|
|
$Arguments = @{
|
|
PackageSourcePath = $Source
|
|
Name = $Name
|
|
Version = $Version
|
|
Force = $Force
|
|
}
|
|
|
|
if ($MacOSRuntime) {
|
|
$architecture = $MacOSRuntime.Split('-')[1]
|
|
$Arguments['Architecture'] = $architecture
|
|
}
|
|
|
|
if ($Script:Options.Runtime -match '(linux|osx).*') {
|
|
$os, $architecture = ($Script:Options.Runtime -split '-')
|
|
$peOS = ConvertTo-PEOperatingSystem -OperatingSystem $os
|
|
$peArch = ConvertTo-PEArchitecture -Architecture $architecture
|
|
|
|
$Arguments['R2RVerification'] = [R2RVerification]@{
|
|
R2RState = "R2R"
|
|
OperatingSystem = $peOS
|
|
Architecture = $peArch
|
|
}
|
|
}
|
|
|
|
if ($PSCmdlet.ShouldProcess("Create tar.gz Package")) {
|
|
New-TarballPackage @Arguments
|
|
}
|
|
}
|
|
"tar-arm" {
|
|
$peArch = ConvertTo-PEArchitecture -Architecture 'arm'
|
|
$Arguments = @{
|
|
PackageSourcePath = $Source
|
|
Name = $Name
|
|
Version = $Version
|
|
Force = $Force
|
|
Architecture = "arm32"
|
|
ExcludeSymbolicLinks = $true
|
|
R2RVerification = [R2RVerification]@{
|
|
R2RState = 'R2R'
|
|
OperatingSystem = "Linux"
|
|
Architecture = $peArch
|
|
}
|
|
}
|
|
|
|
if ($PSCmdlet.ShouldProcess("Create tar.gz Package")) {
|
|
New-TarballPackage @Arguments
|
|
}
|
|
}
|
|
"tar-arm64" {
|
|
$Arguments = @{
|
|
PackageSourcePath = $Source
|
|
Name = $Name
|
|
Version = $Version
|
|
Force = $Force
|
|
Architecture = "arm64"
|
|
ExcludeSymbolicLinks = $true
|
|
R2RVerification = [R2RVerification]@{
|
|
R2RState = 'R2R'
|
|
OperatingSystem = "Linux"
|
|
Architecture = "arm64"
|
|
}
|
|
}
|
|
|
|
if ($PSCmdlet.ShouldProcess("Create tar.gz Package")) {
|
|
New-TarballPackage @Arguments
|
|
}
|
|
}
|
|
"tar-alpine" {
|
|
$Arguments = @{
|
|
PackageSourcePath = $Source
|
|
Name = $Name
|
|
Version = $Version
|
|
Force = $Force
|
|
Architecture = "musl-x64"
|
|
ExcludeSymbolicLinks = $true
|
|
R2RVerification = [R2RVerification]@{
|
|
R2RState = 'R2R'
|
|
OperatingSystem = "Linux"
|
|
Architecture = "amd64"
|
|
}
|
|
}
|
|
|
|
if ($PSCmdlet.ShouldProcess("Create tar.gz Package")) {
|
|
New-TarballPackage @Arguments
|
|
}
|
|
}
|
|
'deb' {
|
|
$Arguments = @{
|
|
Type = 'deb'
|
|
PackageSourcePath = $Source
|
|
Name = $Name
|
|
Version = $Version
|
|
Force = $Force
|
|
NoSudo = $NoSudo
|
|
LTS = $LTS
|
|
HostArchitecture = "amd64"
|
|
}
|
|
foreach ($Distro in $Script:DebianDistributions) {
|
|
$Arguments["Distribution"] = $Distro
|
|
if ($PSCmdlet.ShouldProcess("Create DEB Package for $Distro")) {
|
|
New-UnixPackage @Arguments
|
|
}
|
|
}
|
|
}
|
|
'deb-arm64' {
|
|
$Arguments = @{
|
|
Type = 'deb'
|
|
PackageSourcePath = $Source
|
|
Name = $Name
|
|
Version = $Version
|
|
Force = $Force
|
|
NoSudo = $NoSudo
|
|
LTS = $LTS
|
|
HostArchitecture = "arm64"
|
|
}
|
|
foreach ($Distro in $Script:DebianDistributions) {
|
|
$Arguments["Distribution"] = $Distro
|
|
if ($PSCmdlet.ShouldProcess("Create DEB Package for $Distro")) {
|
|
New-UnixPackage @Arguments
|
|
}
|
|
}
|
|
}
|
|
'rpm' {
|
|
$Arguments = @{
|
|
Type = 'rpm'
|
|
PackageSourcePath = $Source
|
|
Name = $Name
|
|
Version = $Version
|
|
Force = $Force
|
|
NoSudo = $NoSudo
|
|
LTS = $LTS
|
|
HostArchitecture = "x86_64"
|
|
}
|
|
foreach ($Distro in $Script:RedhatFullDistributions) {
|
|
$Arguments["Distribution"] = $Distro
|
|
if ($PSCmdlet.ShouldProcess("Create RPM Package for $Distro")) {
|
|
Write-Verbose -Verbose "Creating RPM Package for $Distro"
|
|
New-UnixPackage @Arguments
|
|
}
|
|
}
|
|
}
|
|
'rpm-fxdependent' {
|
|
$Arguments = @{
|
|
Type = 'rpm'
|
|
PackageSourcePath = $Source
|
|
Name = $Name
|
|
Version = $Version
|
|
Force = $Force
|
|
NoSudo = $NoSudo
|
|
LTS = $LTS
|
|
HostArchitecture = "x86_64"
|
|
}
|
|
foreach ($Distro in $Script:RedhatFddDistributions) {
|
|
$Arguments["Distribution"] = $Distro
|
|
if ($PSCmdlet.ShouldProcess("Create RPM Package for $Distro")) {
|
|
Write-Verbose -Verbose "Creating RPM Package for $Distro"
|
|
New-UnixPackage @Arguments
|
|
}
|
|
}
|
|
}
|
|
'rpm-fxdependent-arm64' {
|
|
$Arguments = @{
|
|
Type = 'rpm'
|
|
PackageSourcePath = $Source
|
|
Name = $Name
|
|
Version = $Version
|
|
Force = $Force
|
|
NoSudo = $NoSudo
|
|
LTS = $LTS
|
|
HostArchitecture = "aarch64"
|
|
}
|
|
foreach ($Distro in $Script:RedhatFddDistributions) {
|
|
$Arguments["Distribution"] = $Distro
|
|
if ($PSCmdlet.ShouldProcess("Create RPM Package for $Distro")) {
|
|
Write-Verbose -Verbose "Creating RPM Package for $Distro"
|
|
New-UnixPackage @Arguments
|
|
}
|
|
}
|
|
}
|
|
'osxpkg' {
|
|
$HostArchitecture = "x86_64"
|
|
if ($MacOSRuntime -match "-arm64") {
|
|
$HostArchitecture = "arm64"
|
|
}
|
|
Write-Verbose "HostArchitecture = $HostArchitecture" -Verbose
|
|
|
|
$Arguments = @{
|
|
Type = 'osxpkg'
|
|
PackageSourcePath = $Source
|
|
Name = $Name
|
|
Version = $Version
|
|
Force = $Force
|
|
NoSudo = $NoSudo
|
|
LTS = $LTS
|
|
HostArchitecture = $HostArchitecture
|
|
}
|
|
|
|
|
|
if ($PSCmdlet.ShouldProcess("Create macOS Package")) {
|
|
New-UnixPackage @Arguments
|
|
}
|
|
}
|
|
default {
|
|
$Arguments = @{
|
|
Type = $_
|
|
PackageSourcePath = $Source
|
|
Name = $Name
|
|
Version = $Version
|
|
Force = $Force
|
|
NoSudo = $NoSudo
|
|
LTS = $LTS
|
|
HostArchitecture = "all"
|
|
}
|
|
|
|
if ($PSCmdlet.ShouldProcess("Create $_ Package")) {
|
|
New-UnixPackage @Arguments
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($IncludeSymbols.IsPresent)
|
|
{
|
|
# Source is a temporary folder when -IncludeSymbols is present. So, we should remove it.
|
|
Remove-Item -Path $Source -Recurse -Force -ErrorAction SilentlyContinue
|
|
}
|
|
}
|
|
}
|
|
|
|
function New-TarballPackage {
|
|
[CmdletBinding(SupportsShouldProcess=$true)]
|
|
param (
|
|
[Parameter(Mandatory)]
|
|
[string] $PackageSourcePath,
|
|
|
|
# Must start with 'powershell' but may have any suffix
|
|
[Parameter(Mandatory)]
|
|
[ValidatePattern("^powershell")]
|
|
[string] $Name,
|
|
|
|
# Suffix of the Name
|
|
[string] $PackageNameSuffix,
|
|
|
|
[Parameter(Mandatory)]
|
|
[string] $Version,
|
|
|
|
[Parameter()]
|
|
[string] $Architecture = "x64",
|
|
|
|
[switch] $Force,
|
|
|
|
[switch] $ExcludeSymbolicLinks,
|
|
|
|
[string] $CurrentLocation = (Get-Location),
|
|
|
|
[R2RVerification] $R2RVerification
|
|
)
|
|
|
|
if ($PackageNameSuffix) {
|
|
$packageName = "$Name-$Version-{0}-$Architecture-$PackageNameSuffix.tar.gz"
|
|
} else {
|
|
$packageName = "$Name-$Version-{0}-$Architecture.tar.gz"
|
|
}
|
|
|
|
if ($Environment.IsWindows) {
|
|
throw "Must be on Linux or macOS to build 'tar.gz' packages!"
|
|
} elseif ($Environment.IsLinux) {
|
|
$packageName = $packageName -f "linux"
|
|
} elseif ($Environment.IsMacOS) {
|
|
$packageName = $packageName -f "osx"
|
|
}
|
|
|
|
$packagePath = Join-Path -Path $CurrentLocation -ChildPath $packageName
|
|
Write-Verbose "Create package $packageName"
|
|
Write-Verbose "Package destination path: $packagePath"
|
|
|
|
if (Test-Path -Path $packagePath) {
|
|
if ($Force -or $PSCmdlet.ShouldProcess("Overwrite existing package file")) {
|
|
Write-Verbose "Overwrite existing package file at $packagePath" -Verbose
|
|
Remove-Item -Path $packagePath -Force -ErrorAction Stop -Confirm:$false
|
|
}
|
|
}
|
|
|
|
$Staging = "$PSScriptRoot/staging"
|
|
New-StagingFolder -StagingPath $Staging -PackageSourcePath $PackageSourcePath -R2RVerification $R2RVerification
|
|
|
|
# Ensure PowerShell executable has correct permissions in tarball
|
|
$pwshInStaging = Join-Path $Staging 'pwsh'
|
|
if (Test-Path -LiteralPath $pwshInStaging) {
|
|
Start-NativeExecution { chmod 755 $pwshInStaging }
|
|
}
|
|
|
|
# Included .NET executable for producing crash dumps
|
|
$createdumpInStaging = Join-Path $Staging 'createdump'
|
|
if (Test-Path -LiteralPath $createdumpInStaging) {
|
|
Start-NativeExecution { chmod 755 $createdumpInStaging }
|
|
}
|
|
|
|
if (Get-Command -Name tar -CommandType Application -ErrorAction Ignore) {
|
|
if ($Force -or $PSCmdlet.ShouldProcess("Create tarball package")) {
|
|
$options = "-czf"
|
|
if ($PSBoundParameters.ContainsKey('Verbose') -and $PSBoundParameters['Verbose'].IsPresent) {
|
|
# Use the verbose mode '-v' if '-Verbose' is specified
|
|
$options = "-czvf"
|
|
}
|
|
|
|
try {
|
|
Push-Location -Path $Staging
|
|
tar $options $packagePath *
|
|
} finally {
|
|
Pop-Location
|
|
}
|
|
|
|
if (Test-Path -Path $packagePath) {
|
|
Write-Log "You can find the tarball package at $packagePath"
|
|
return (Get-Item $packagePath)
|
|
} else {
|
|
throw "Failed to create $packageName"
|
|
}
|
|
}
|
|
} else {
|
|
throw "Failed to create the package because the application 'tar' cannot be found"
|
|
}
|
|
}
|
|
|
|
function New-TempFolder
|
|
{
|
|
$tempPath = [System.IO.Path]::GetTempPath()
|
|
|
|
$tempFolder = Join-Path -Path $tempPath -ChildPath ([System.IO.Path]::GetRandomFileName())
|
|
if (!(Test-Path -Path $tempFolder))
|
|
{
|
|
$null = New-Item -Path $tempFolder -ItemType Directory
|
|
}
|
|
|
|
return $tempFolder
|
|
}
|
|
|
|
function New-PSSignedBuildZip
|
|
{
|
|
param(
|
|
[Parameter(Mandatory)]
|
|
[string]$BuildPath,
|
|
[Parameter(Mandatory)]
|
|
[string]$SignedFilesPath,
|
|
[Parameter(Mandatory)]
|
|
[string]$DestinationFolder,
|
|
[parameter(HelpMessage='VSTS variable to set for path to zip')]
|
|
[string]$VstsVariableName
|
|
)
|
|
|
|
Update-PSSignedBuildFolder -BuildPath $BuildPath -SignedFilesPath $SignedFilesPath
|
|
|
|
# Remove '$signedFilesPath' now that signed binaries are copied
|
|
if (Test-Path $signedFilesPath)
|
|
{
|
|
Remove-Item -Recurse -Force -Path $signedFilesPath
|
|
}
|
|
|
|
New-PSBuildZip -BuildPath $BuildPath -DestinationFolder $DestinationFolder -VstsVariableName $VstsVariableName
|
|
}
|
|
|
|
function New-PSBuildZip
|
|
{
|
|
param(
|
|
[Parameter(Mandatory)]
|
|
[string]$BuildPath,
|
|
[Parameter(Mandatory)]
|
|
[string]$DestinationFolder,
|
|
[parameter(HelpMessage='VSTS variable to set for path to zip')]
|
|
[string]$VstsVariableName
|
|
)
|
|
|
|
$name = Split-Path -Path $BuildPath -Leaf
|
|
$zipLocationPath = Join-Path -Path $DestinationFolder -ChildPath "$name-signed.zip"
|
|
Compress-Archive -Path $BuildPath\* -DestinationPath $zipLocationPath
|
|
if ($VstsVariableName)
|
|
{
|
|
# set VSTS variable with path to package files
|
|
Write-Log "Setting $VstsVariableName to $zipLocationPath"
|
|
Write-Host "##vso[task.setvariable variable=$VstsVariableName]$zipLocationPath"
|
|
}
|
|
else
|
|
{
|
|
return $zipLocationPath
|
|
}
|
|
}
|
|
|
|
|
|
function Update-PSSignedBuildFolder
|
|
{
|
|
param(
|
|
[Parameter(Mandatory)]
|
|
[string]$BuildPath,
|
|
[Parameter(Mandatory)]
|
|
[string]$SignedFilesPath,
|
|
[string[]] $RemoveFilter = ('*.pdb', '*.zip', '*.r2rmap'),
|
|
[bool]$OfficialBuild = $true
|
|
)
|
|
|
|
$BuildPathNormalized = (Get-Item $BuildPath).FullName
|
|
$SignedFilesPathNormalized = (Get-Item $SignedFilesPath).FullName
|
|
|
|
Write-Verbose -Verbose "BuildPath = $BuildPathNormalized"
|
|
Write-Verbose -Verbose "SignedFilesPath = $signedFilesPath"
|
|
|
|
# Replace unsigned binaries with signed
|
|
$signedFilesFilter = Join-Path -Path $SignedFilesPathNormalized -ChildPath '*'
|
|
Write-Verbose -Verbose "signedFilesFilter = $signedFilesFilter"
|
|
|
|
$signedFilesList = Get-ChildItem -Path $signedFilesFilter -Recurse -File
|
|
foreach ($signedFileObject in $signedFilesList) {
|
|
# completely skip replacing pwsh on non-windows systems (there is no .exe extension here)
|
|
# and it may not be signed correctly
|
|
|
|
# The Shim will not be signed in CI.
|
|
|
|
if ($signedFileObject.Name -eq "pwsh" -or ($signedFileObject.Name -eq "Microsoft.PowerShell.GlobalTool.Shim.exe" -and $env:BUILD_REASON -eq 'PullRequest')) {
|
|
Write-Verbose -Verbose "Skipping $signedFileObject"
|
|
continue
|
|
}
|
|
|
|
$signedFilePath = $signedFileObject.FullName
|
|
Write-Verbose -Verbose "Processing $signedFilePath"
|
|
|
|
# Agents seems to be on a case sensitive file system
|
|
if ($IsLinux) {
|
|
$relativePath = $signedFilePath.Replace($SignedFilesPathNormalized, '')
|
|
} else {
|
|
$relativePath = $signedFilePath.ToLowerInvariant().Replace($SignedFilesPathNormalized.ToLowerInvariant(), '')
|
|
}
|
|
|
|
Write-Verbose -Verbose "relativePath = $relativePath"
|
|
$destination = (Get-Item (Join-Path -Path $BuildPathNormalized -ChildPath $relativePath)).FullName
|
|
Write-Verbose -Verbose "destination = $destination"
|
|
Write-Log "replacing $destination with $signedFilePath"
|
|
|
|
if (-not (Test-Path $destination)) {
|
|
$parent = Split-Path -Path $destination -Parent
|
|
$exists = Test-Path -Path $parent
|
|
|
|
if ($exists) {
|
|
Write-Verbose -Verbose "Parent:"
|
|
Get-ChildItem -Path $parent | Select-Object -ExpandProperty FullName | Write-Verbose -Verbose
|
|
}
|
|
|
|
Write-Error "File not found: $destination, parent - $parent exists: $exists"
|
|
}
|
|
|
|
# Get-AuthenticodeSignature will only work on Windows
|
|
if ($IsWindows)
|
|
{
|
|
$signature = Get-AuthenticodeSignature -FilePath $signedFilePath
|
|
|
|
if ($signature.Status -ne 'Valid' -and $OfficialBuild) {
|
|
Write-Host "Certificate Issuer: $($signature.SignerCertificate.Issuer)"
|
|
Write-Host "Certificate Subject: $($signature.SignerCertificate.Subject)"
|
|
Write-Error "Invalid signature for $signedFilePath"
|
|
} elseif ($OfficialBuild -eq $false) {
|
|
if ($signature.Status -eq 'NotSigned') {
|
|
Write-Warning "File is not signed: $signedFilePath"
|
|
} elseif ($signature.SignerCertificate.Issuer -notmatch '^CN=(Microsoft|TestAzureEngBuildCodeSign|Windows Internal Build Tools).*') {
|
|
Write-Warning "File signed with test certificate: $signedFilePath"
|
|
Write-Host "Certificate Issuer: $($signature.SignerCertificate.Issuer)"
|
|
Write-Host "Certificate Subject: $($signature.SignerCertificate.Subject)"
|
|
} else {
|
|
Write-Verbose -Verbose "File properly signed: $signedFilePath"
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Write-Verbose -Verbose "Skipping certificate check of $signedFilePath on non-Windows"
|
|
}
|
|
|
|
Copy-Item -Path $signedFilePath -Destination $destination -Force
|
|
|
|
}
|
|
|
|
foreach($filter in $RemoveFilter) {
|
|
$removePath = Join-Path -Path $BuildPathNormalized -ChildPath $filter
|
|
Remove-Item -Path $removePath -Recurse -Force
|
|
}
|
|
}
|
|
|
|
|
|
function Expand-PSSignedBuild
|
|
{
|
|
param(
|
|
[Parameter(Mandatory)]
|
|
[string]$BuildZip,
|
|
|
|
[Switch]$SkipPwshExeCheck
|
|
)
|
|
|
|
$psModulePath = Split-Path -Path $PSScriptRoot
|
|
# Expand signed build
|
|
$buildPath = Join-Path -Path $psModulePath -ChildPath 'ExpandedBuild'
|
|
$null = New-Item -Path $buildPath -ItemType Directory -Force
|
|
Expand-Archive -Path $BuildZip -DestinationPath $buildPath -Force
|
|
# Remove the zip file that contains only those files from the parent folder of 'publish'.
|
|
# That zip file is used for compliance scan.
|
|
Remove-Item -Path (Join-Path -Path $buildPath -ChildPath '*.zip') -Recurse
|
|
|
|
if ($SkipPwshExeCheck) {
|
|
$executablePath = (Join-Path $buildPath -ChildPath 'pwsh.dll')
|
|
} else {
|
|
if ($IsMacOS -or $IsLinux) {
|
|
$executablePath = (Join-Path $buildPath -ChildPath 'pwsh')
|
|
} else {
|
|
$executablePath = (Join-Path $buildPath -ChildPath 'pwsh.exe')
|
|
}
|
|
}
|
|
|
|
Restore-PSModuleToBuild -PublishPath $buildPath
|
|
|
|
$psOptionsPath = Join-Path $buildPath -ChildPath 'psoptions.json'
|
|
Restore-PSOptions -PSOptionsPath $psOptionsPath
|
|
|
|
$options = Get-PSOptions
|
|
|
|
$options.PSModuleRestore = $true
|
|
|
|
if (Test-Path -Path $executablePath) {
|
|
$options.Output = $executablePath
|
|
} else {
|
|
throw 'Could not find pwsh'
|
|
}
|
|
|
|
Set-PSOptions -Options $options
|
|
}
|
|
|
|
function New-UnixPackage {
|
|
[CmdletBinding(SupportsShouldProcess=$true)]
|
|
param(
|
|
[Parameter(Mandatory)]
|
|
[ValidateSet("deb", "osxpkg", "rpm")]
|
|
[string]$Type,
|
|
|
|
[Parameter(Mandatory)]
|
|
[string]$PackageSourcePath,
|
|
|
|
# Must start with 'powershell' but may have any suffix
|
|
[Parameter(Mandatory)]
|
|
[ValidatePattern("^powershell")]
|
|
[string]$Name,
|
|
|
|
[Parameter(Mandatory)]
|
|
[string]$Version,
|
|
|
|
# Package iteration version (rarely changed)
|
|
# This is a string because strings are appended to it
|
|
[string]$Iteration = "1",
|
|
|
|
# Host architecture values allowed for deb type packages: amd64, arm64
|
|
# Host architecture values allowed for rpm type packages include: x86_64, aarch64, native, all, noarch, any
|
|
# Host architecture values allowed for osxpkg type packages include: x86_64, arm64
|
|
[string]
|
|
[ValidateSet("x86_64", "amd64", "aarch64", "arm64", "native", "all", "noarch", "any")]
|
|
$HostArchitecture,
|
|
|
|
[Switch]
|
|
$Force,
|
|
|
|
[switch]
|
|
$NoSudo,
|
|
|
|
[switch]
|
|
$LTS,
|
|
|
|
[string]
|
|
$CurrentLocation = (Get-Location)
|
|
)
|
|
|
|
DynamicParam {
|
|
if ($Type -eq "deb" -or $Type -like 'rpm*') {
|
|
# Add a dynamic parameter '-Distribution' when the specified package type is 'deb'.
|
|
# The '-Distribution' parameter can be used to indicate which Debian distro this package is targeting.
|
|
$ParameterAttr = New-Object "System.Management.Automation.ParameterAttribute"
|
|
if($type -eq 'deb')
|
|
{
|
|
$ValidateSetAttr = New-Object "System.Management.Automation.ValidateSetAttribute" -ArgumentList $Script:DebianDistributions
|
|
}
|
|
else
|
|
{
|
|
$ValidateSetAttr = New-Object "System.Management.Automation.ValidateSetAttribute" -ArgumentList $Script:RedHatDistributions
|
|
}
|
|
$Attributes = New-Object "System.Collections.ObjectModel.Collection``1[System.Attribute]"
|
|
$Attributes.Add($ParameterAttr) > $null
|
|
$Attributes.Add($ValidateSetAttr) > $null
|
|
$Dict = New-Object "System.Management.Automation.RuntimeDefinedParameterDictionary"
|
|
$Parameter = New-Object "System.Management.Automation.RuntimeDefinedParameter" -ArgumentList ("Distribution", [string], $Attributes)
|
|
|
|
$Dict.Add("Distribution", $Parameter) > $null
|
|
return $Dict
|
|
}
|
|
}
|
|
|
|
End {
|
|
# This allows sudo install to be optional; needed when running in containers / as root
|
|
# Note that when it is null, Invoke-Expression (but not &) must be used to interpolate properly
|
|
$sudo = if (!$NoSudo) { "sudo" }
|
|
|
|
# Validate platform
|
|
$ErrorMessage = "Must be on {0} to build '$Type' packages!"
|
|
switch ($Type) {
|
|
"deb" {
|
|
$packageVersion = Get-LinuxPackageSemanticVersion -Version $Version
|
|
if (!$Environment.IsUbuntu -and !$Environment.IsDebian -and !$Environment.IsMariner) {
|
|
throw ($ErrorMessage -f "Ubuntu or Debian")
|
|
}
|
|
|
|
if ($PSBoundParameters.ContainsKey('Distribution')) {
|
|
$DebDistro = $PSBoundParameters['Distribution']
|
|
} elseif ($Environment.IsUbuntu16) {
|
|
$DebDistro = "ubuntu.16.04"
|
|
} elseif ($Environment.IsUbuntu18) {
|
|
$DebDistro = "ubuntu.18.04"
|
|
} elseif ($Environment.IsUbuntu20) {
|
|
$DebDistro = "ubuntu.20.04"
|
|
} elseif ($Environment.IsDebian9) {
|
|
$DebDistro = "debian.9"
|
|
} else {
|
|
throw "The current Debian distribution is not supported."
|
|
}
|
|
|
|
# iteration is "debian_revision"
|
|
# usage of this to differentiate distributions is allowed by non-standard
|
|
$Iteration += ".$DebDistro"
|
|
}
|
|
"rpm" {
|
|
if ($PSBoundParameters.ContainsKey('Distribution')) {
|
|
$DebDistro = $PSBoundParameters['Distribution']
|
|
|
|
} elseif ($Environment.IsRedHatFamily) {
|
|
$DebDistro = "rhel.7"
|
|
} else {
|
|
throw "The current distribution is not supported."
|
|
}
|
|
|
|
$packageVersion = Get-LinuxPackageSemanticVersion -Version $Version
|
|
}
|
|
"osxpkg" {
|
|
$packageVersion = $Version
|
|
if (!$Environment.IsMacOS) {
|
|
throw ($ErrorMessage -f "macOS")
|
|
}
|
|
|
|
$DebDistro = 'macOS'
|
|
}
|
|
}
|
|
|
|
# Determine if the version is a preview version
|
|
$IsPreview = Test-IsPreview -Version $Version -IsLTS:$LTS
|
|
|
|
# For deb/rpm packages, use the '-lts' and '-preview' channel suffix variants to match existing names on packages.microsoft.com.
|
|
# For osxpkg package, only LTS packages get a channel suffix in the name.
|
|
$Name = if($LTS) {
|
|
"powershell-lts"
|
|
}
|
|
elseif ($IsPreview -and $Type -ne "osxpkg") {
|
|
"powershell-preview"
|
|
}
|
|
else {
|
|
"powershell"
|
|
}
|
|
|
|
# Verify dependencies are installed and in the path
|
|
Test-Dependencies
|
|
|
|
$Description = $packagingStrings.Description
|
|
|
|
# Break the version down into its components, we are interested in the major version
|
|
$VersionMatch = [regex]::Match($Version, '(\d+)(?:.(\d+)(?:.(\d+)(?:-preview(?:.(\d+))?)?)?)?')
|
|
$MajorVersion = $VersionMatch.Groups[1].Value
|
|
|
|
# Suffix is used for side-by-side preview/release package installation
|
|
$Suffix = if ($IsPreview) { $MajorVersion + "-preview" } elseif ($LTS) { $MajorVersion + "-lts" } else { $MajorVersion }
|
|
|
|
# Setup staging directory so we don't change the original source directory
|
|
$Staging = "$PSScriptRoot/staging"
|
|
if ($PSCmdlet.ShouldProcess("Create staging folder")) {
|
|
New-StagingFolder -StagingPath $Staging -PackageSourcePath $PackageSourcePath
|
|
}
|
|
|
|
# Follow the Filesystem Hierarchy Standard for Linux and macOS
|
|
$Destination = if ($Environment.IsLinux) {
|
|
"/opt/microsoft/powershell/$Suffix"
|
|
} elseif ($Environment.IsMacOS) {
|
|
"/usr/local/microsoft/powershell/$Suffix"
|
|
}
|
|
|
|
# Destination for symlink to powershell executable
|
|
$Link = Get-PwshExecutablePath -IsPreview:$IsPreview
|
|
|
|
$links = @(New-LinkInfo -LinkDestination $Link -LinkTarget "$Destination/pwsh")
|
|
|
|
if($LTS) {
|
|
$links += New-LinkInfo -LinkDestination (Get-PwshExecutablePath -IsLTS:$LTS) -LinkTarget "$Destination/pwsh"
|
|
}
|
|
|
|
if ($PSCmdlet.ShouldProcess("Create package file system"))
|
|
{
|
|
# Generate After Install and After Remove scripts
|
|
$AfterScriptInfo = New-AfterScripts -Link $Link -Distribution $DebDistro -Destination $Destination
|
|
|
|
# Generate gzip of man file
|
|
$ManGzipInfo = New-ManGzip -IsPreview:$IsPreview -IsLTS:$LTS
|
|
|
|
# Change permissions for packaging
|
|
Write-Log "Setting permissions..."
|
|
Start-NativeExecution {
|
|
find $Staging -type d | xargs chmod 755
|
|
find $Staging -type f | xargs chmod 644
|
|
chmod 644 $ManGzipInfo.GzipFile
|
|
# refers to executable, does not vary by channel
|
|
chmod 755 "$Staging/pwsh" # only the executable file should be granted the execution permission
|
|
# Included .NET executable for producing crash dumps
|
|
if (Test-Path "$Staging/createdump") {
|
|
chmod 755 "$Staging/createdump"
|
|
}
|
|
}
|
|
}
|
|
|
|
# Add macOS powershell launcher
|
|
if ($Type -eq "osxpkg")
|
|
{
|
|
Write-Log "Adding macOS launch application..."
|
|
if ($PSCmdlet.ShouldProcess("Add macOS launch application"))
|
|
{
|
|
# Generate launcher app folder
|
|
$AppsFolder = New-MacOSLauncher -Version $Version
|
|
}
|
|
}
|
|
|
|
$packageDependenciesParams = @{}
|
|
if ($DebDistro)
|
|
{
|
|
$packageDependenciesParams['Distribution']=$DebDistro
|
|
}
|
|
|
|
# Setup package dependencies
|
|
$Dependencies = @(Get-PackageDependencies @packageDependenciesParams)
|
|
|
|
# Build package
|
|
try {
|
|
if ($Type -eq 'rpm') {
|
|
# Use rpmbuild directly for RPM packages
|
|
if ($PSCmdlet.ShouldProcess("Create RPM package with rpmbuild")) {
|
|
Write-Log "Creating RPM package with rpmbuild..."
|
|
|
|
# Create rpmbuild directory structure
|
|
$rpmBuildRoot = Join-Path $env:HOME "rpmbuild"
|
|
$specsDir = Join-Path $rpmBuildRoot "SPECS"
|
|
$rpmsDir = Join-Path $rpmBuildRoot "RPMS"
|
|
|
|
New-Item -ItemType Directory -Path $specsDir -Force | Out-Null
|
|
New-Item -ItemType Directory -Path $rpmsDir -Force | Out-Null
|
|
|
|
# Generate RPM spec file
|
|
$specContent = New-RpmSpec `
|
|
-Name $Name `
|
|
-Version $packageVersion `
|
|
-Iteration $Iteration `
|
|
-Description $Description `
|
|
-Dependencies $Dependencies `
|
|
-AfterInstallScript $AfterScriptInfo.AfterInstallScript `
|
|
-AfterRemoveScript $AfterScriptInfo.AfterRemoveScript `
|
|
-Staging $Staging `
|
|
-Destination $Destination `
|
|
-ManGzipFile $ManGzipInfo.GzipFile `
|
|
-ManDestination $ManGzipInfo.ManFile `
|
|
-LinkInfo $Links `
|
|
-Distribution $DebDistro `
|
|
-HostArchitecture $HostArchitecture
|
|
|
|
$specFile = Join-Path $specsDir "$Name.spec"
|
|
$specContent | Out-File -FilePath $specFile -Encoding ascii
|
|
Write-Verbose "Generated spec file: $specFile" -Verbose
|
|
|
|
# Log the spec file content
|
|
if ($env:GITHUB_ACTIONS -eq 'true') {
|
|
Write-Host "::group::RPM Spec File Content"
|
|
Write-Host $specContent
|
|
Write-Host "::endgroup::"
|
|
} else {
|
|
Write-Verbose "RPM Spec File Content:`n$specContent" -Verbose
|
|
}
|
|
|
|
# Build RPM package
|
|
try {
|
|
# Use bash to properly handle rpmbuild arguments
|
|
# Add --target for cross-architecture builds
|
|
$targetArch = ""
|
|
if ($HostArchitecture -ne "x86_64" -and $HostArchitecture -ne "noarch") {
|
|
$targetArch = "--target $HostArchitecture"
|
|
}
|
|
$buildCmd = "rpmbuild -bb --quiet $targetArch --define '_topdir $rpmBuildRoot' --buildroot '$rpmBuildRoot/BUILDROOT' '$specFile'"
|
|
Write-Verbose "Running: $buildCmd" -Verbose
|
|
$Output = bash -c $buildCmd 2>&1
|
|
$exitCode = $LASTEXITCODE
|
|
|
|
if ($exitCode -ne 0) {
|
|
throw "rpmbuild failed with exit code $exitCode"
|
|
}
|
|
|
|
# Find the generated RPM
|
|
$rpmFile = Get-ChildItem -Path (Join-Path $rpmsDir $HostArchitecture) -Filter "*.rpm" -ErrorAction Stop |
|
|
Sort-Object -Property LastWriteTime -Descending |
|
|
Select-Object -First 1
|
|
|
|
if ($rpmFile) {
|
|
# Copy RPM to current location
|
|
Copy-Item -Path $rpmFile.FullName -Destination $CurrentLocation -Force
|
|
$Output = @("Created package {:path=>""$($rpmFile.Name)""}")
|
|
} else {
|
|
throw "RPM file not found after build"
|
|
}
|
|
}
|
|
catch {
|
|
Write-Verbose -Message "!!!Handling error in rpmbuild!!!" -Verbose -ErrorAction SilentlyContinue
|
|
if ($Output) {
|
|
Write-Verbose -Message "$Output" -Verbose -ErrorAction SilentlyContinue
|
|
}
|
|
Get-Error -InputObject $_
|
|
throw
|
|
}
|
|
}
|
|
} elseif ($Type -eq 'deb') {
|
|
# Use native DEB package builder
|
|
if ($PSCmdlet.ShouldProcess("Create DEB package natively")) {
|
|
Write-Log "Creating DEB package natively..."
|
|
try {
|
|
$result = New-NativeDeb `
|
|
-Name $Name `
|
|
-Version $packageVersion `
|
|
-Iteration $Iteration `
|
|
-Description $Description `
|
|
-Staging $Staging `
|
|
-Destination $Destination `
|
|
-ManGzipFile $ManGzipInfo.GzipFile `
|
|
-ManDestination $ManGzipInfo.ManFile `
|
|
-LinkInfo $Links `
|
|
-Dependencies $Dependencies `
|
|
-AfterInstallScript $AfterScriptInfo.AfterInstallScript `
|
|
-AfterRemoveScript $AfterScriptInfo.AfterRemoveScript `
|
|
-HostArchitecture $HostArchitecture `
|
|
-CurrentLocation $CurrentLocation
|
|
|
|
$Output = @("Created package {:path=>""$($result.PackageName)""}")
|
|
}
|
|
catch {
|
|
Write-Verbose -Message "!!!Handling error in native DEB creation!!!" -Verbose -ErrorAction SilentlyContinue
|
|
}
|
|
}
|
|
} elseif ($Type -eq 'osxpkg') {
|
|
# Use native macOS packaging tools
|
|
if ($PSCmdlet.ShouldProcess("Create macOS package with pkgbuild/productbuild")) {
|
|
Write-Log "Creating macOS package with native tools..."
|
|
|
|
$macPkgArgs = @{
|
|
Name = $Name
|
|
Version = $packageVersion
|
|
Iteration = $Iteration
|
|
Staging = $Staging
|
|
Destination = $Destination
|
|
ManGzipFile = $ManGzipInfo.GzipFile
|
|
ManDestination = $ManGzipInfo.ManFile
|
|
LinkInfo = $Links
|
|
AfterInstallScript = $AfterScriptInfo.AfterInstallScript
|
|
AppsFolder = $AppsFolder
|
|
HostArchitecture = $HostArchitecture
|
|
CurrentLocation = $CurrentLocation
|
|
LTS = $LTS
|
|
}
|
|
|
|
try {
|
|
$packageFile = New-MacOSPackage @macPkgArgs
|
|
$Output = @("Created package {:path=>""$($packageFile.Name)""}")
|
|
}
|
|
catch {
|
|
Write-Verbose -Message "!!!Handling error in macOS packaging!!!" -Verbose -ErrorAction SilentlyContinue
|
|
Get-Error -InputObject $_
|
|
throw
|
|
}
|
|
}
|
|
} else {
|
|
# Nothing should reach here
|
|
throw "Unknown package type: $Type"
|
|
}
|
|
} finally {
|
|
if ($Environment.IsMacOS) {
|
|
Write-Log "Starting Cleanup for mac packaging..."
|
|
if ($PSCmdlet.ShouldProcess("Cleanup macOS launcher"))
|
|
{
|
|
Clear-MacOSLauncher
|
|
}
|
|
}
|
|
|
|
# Clean up rpmbuild directory if it was created
|
|
if ($Type -eq 'rpm') {
|
|
$rpmBuildRoot = Join-Path $env:HOME "rpmbuild"
|
|
if (Test-Path $rpmBuildRoot) {
|
|
Write-Verbose "Cleaning up rpmbuild directory: $rpmBuildRoot" -Verbose
|
|
Remove-Item -Path $rpmBuildRoot -Recurse -Force -ErrorAction SilentlyContinue
|
|
}
|
|
}
|
|
|
|
if ($AfterScriptInfo.AfterInstallScript) {
|
|
Remove-Item -ErrorAction 'silentlycontinue' $AfterScriptInfo.AfterInstallScript -Force
|
|
}
|
|
if ($AfterScriptInfo.AfterRemoveScript) {
|
|
Remove-Item -ErrorAction 'silentlycontinue' $AfterScriptInfo.AfterRemoveScript -Force
|
|
}
|
|
Remove-Item -Path $ManGzipInfo.GzipFile -Force -ErrorAction SilentlyContinue
|
|
}
|
|
|
|
# Magic to get path output
|
|
$createdPackage = Get-Item (Join-Path $CurrentLocation (($Output[-1] -split ":path=>")[-1] -replace '["{}]'))
|
|
|
|
# For macOS with native tools, the package is already in the correct format
|
|
# For other platforms, the package name from dpkg-deb/rpmbuild is sufficient
|
|
|
|
if (Test-Path $createdPackage)
|
|
{
|
|
Write-Verbose "Created package: $createdPackage" -Verbose
|
|
return $createdPackage
|
|
}
|
|
else
|
|
{
|
|
throw "Failed to create $createdPackage"
|
|
}
|
|
}
|
|
}
|
|
|
|
Function New-LinkInfo
|
|
{
|
|
[CmdletBinding(SupportsShouldProcess=$true)]
|
|
param(
|
|
[Parameter(Mandatory)]
|
|
[string]
|
|
$LinkDestination,
|
|
[Parameter(Mandatory)]
|
|
[string]
|
|
$linkTarget
|
|
)
|
|
|
|
$linkDir = Join-Path -Path '/tmp' -ChildPath ([System.IO.Path]::GetRandomFileName())
|
|
$null = New-Item -ItemType Directory -Path $linkDir
|
|
$linkSource = Join-Path -Path $linkDir -ChildPath 'pwsh'
|
|
|
|
Write-Log "Creating link to target '$LinkTarget', with a temp source of '$LinkSource' and a Package Destination of '$LinkDestination'"
|
|
if ($PSCmdlet.ShouldProcess("Create package symbolic from $linkDestination to $linkTarget"))
|
|
{
|
|
# refers to executable, does not vary by channel
|
|
New-Item -Force -ItemType SymbolicLink -Path $linkSource -Target $LinkTarget > $null
|
|
}
|
|
|
|
[LinkInfo] @{
|
|
Source = $linkSource
|
|
Destination = $LinkDestination
|
|
}
|
|
}
|
|
|
|
function New-MacOsDistributionPackage
|
|
{
|
|
[CmdletBinding(SupportsShouldProcess=$true)]
|
|
param(
|
|
[Parameter(Mandatory,HelpMessage='The FileInfo of the component package')]
|
|
[System.IO.FileInfo]$ComponentPackage,
|
|
|
|
[Parameter(Mandatory,HelpMessage='Package name for the output file')]
|
|
[string]$PackageName,
|
|
|
|
[Parameter(Mandatory,HelpMessage='Package version')]
|
|
[string]$Version,
|
|
|
|
[Parameter(Mandatory,HelpMessage='Output directory for the final package')]
|
|
[string]$OutputDirectory,
|
|
|
|
[Parameter(HelpMessage='x86_64 for Intel or arm64 for Apple Silicon')]
|
|
[ValidateSet("x86_64", "arm64")]
|
|
[string] $HostArchitecture = "x86_64",
|
|
|
|
[Parameter(HelpMessage='Package identifier')]
|
|
[string]$PackageIdentifier,
|
|
|
|
[Switch] $IsPreview
|
|
)
|
|
|
|
if (!$Environment.IsMacOS)
|
|
{
|
|
throw 'New-MacOsDistributionPackage is only supported on macOS!'
|
|
}
|
|
|
|
# Create a temp directory to store the needed files
|
|
$tempDir = Join-Path ([System.IO.Path]::GetTempPath()) ([System.IO.Path]::GetRandomFileName())
|
|
New-Item -ItemType Directory -Path $tempDir -Force > $null
|
|
|
|
$resourcesDir = Join-Path -Path $tempDir -ChildPath 'resources'
|
|
New-Item -ItemType Directory -Path $resourcesDir -Force > $null
|
|
|
|
# Copy background file to temp directory
|
|
$backgroundFile = "$RepoRoot/assets/macDialog.png"
|
|
if (Test-Path $backgroundFile) {
|
|
Copy-Item -Path $backgroundFile -Destination $resourcesDir -Force
|
|
}
|
|
|
|
# Copy the component package to temp directory
|
|
$componentFileName = Split-Path -Leaf -Path $ComponentPackage
|
|
$tempComponentPath = Join-Path -Path $tempDir -ChildPath $componentFileName
|
|
Copy-Item -Path $ComponentPackage -Destination $tempComponentPath -Force
|
|
|
|
# Create the distribution xml
|
|
$distributionXmlPath = Join-Path -Path $tempDir -ChildPath 'powershellDistribution.xml'
|
|
|
|
# Get package ID if not provided
|
|
if (-not $PackageIdentifier) {
|
|
if ($IsPreview.IsPresent) {
|
|
$PackageIdentifier = 'com.microsoft.powershell-preview'
|
|
}
|
|
else {
|
|
$PackageIdentifier = 'com.microsoft.powershell'
|
|
}
|
|
}
|
|
|
|
# Minimum OS version
|
|
$minOSVersion = "11.0" # macOS Big Sur minimum
|
|
|
|
# format distribution template with:
|
|
# 0 - title
|
|
# 1 - version
|
|
# 2 - package path (component package filename)
|
|
# 3 - minimum os version
|
|
# 4 - Package Identifier
|
|
# 5 - host architecture (x86_64 for Intel or arm64 for Apple Silicon)
|
|
$PackagingStrings.OsxDistributionTemplate -f $PackageName, $Version, $componentFileName, $minOSVersion, $PackageIdentifier, $HostArchitecture | Out-File -Encoding utf8 -FilePath $distributionXmlPath -Force
|
|
|
|
# Build final package path
|
|
# Rename x86_64 to x64 for compatibility
|
|
$packageArchName = if ($HostArchitecture -eq "x86_64") { "x64" } else { $HostArchitecture }
|
|
$finalPackagePath = Join-Path $OutputDirectory "$PackageName-$Version-osx-$packageArchName.pkg"
|
|
|
|
# Remove existing package if it exists
|
|
if (Test-Path $finalPackagePath) {
|
|
Write-Warning "Removing existing package: $finalPackagePath"
|
|
Remove-Item $finalPackagePath -Force
|
|
}
|
|
|
|
if ($PSCmdlet.ShouldProcess("Build product package with productbuild")) {
|
|
Write-Log "Applying distribution.xml to package..."
|
|
Push-Location $tempDir
|
|
try
|
|
{
|
|
# productbuild is an xcode command line tool
|
|
Start-NativeExecution -VerboseOutputOnError {
|
|
productbuild --distribution $distributionXmlPath `
|
|
--package-path $tempDir `
|
|
--resources $resourcesDir `
|
|
$finalPackagePath
|
|
}
|
|
|
|
if (Test-Path $finalPackagePath) {
|
|
Write-Log "Successfully created macOS package: $finalPackagePath"
|
|
}
|
|
else {
|
|
throw "Package was not created at expected location: $finalPackagePath"
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
Pop-Location
|
|
Remove-Item -Path $tempDir -Recurse -Force -ErrorAction SilentlyContinue
|
|
}
|
|
}
|
|
|
|
return (Get-Item $finalPackagePath)
|
|
}
|
|
|
|
Class LinkInfo
|
|
{
|
|
[string] $Source
|
|
[string] $Destination
|
|
}
|
|
|
|
function New-RpmSpec
|
|
{
|
|
param(
|
|
[Parameter(Mandatory,HelpMessage='Package Name')]
|
|
[String]$Name,
|
|
|
|
[Parameter(Mandatory,HelpMessage='Package Version')]
|
|
[String]$Version,
|
|
|
|
[Parameter(Mandatory)]
|
|
[String]$Iteration,
|
|
|
|
[Parameter(Mandatory,HelpMessage='Package description')]
|
|
[String]$Description,
|
|
|
|
[Parameter(Mandatory,HelpMessage='Staging folder for installation files')]
|
|
[String]$Staging,
|
|
|
|
[Parameter(Mandatory,HelpMessage='Install path on target machine')]
|
|
[String]$Destination,
|
|
|
|
[Parameter(Mandatory,HelpMessage='The built and gzipped man file.')]
|
|
[String]$ManGzipFile,
|
|
|
|
[Parameter(Mandatory,HelpMessage='The destination of the man file')]
|
|
[String]$ManDestination,
|
|
|
|
[Parameter(Mandatory,HelpMessage='Symlink to powershell executable')]
|
|
[LinkInfo[]]$LinkInfo,
|
|
|
|
[Parameter(Mandatory,HelpMessage='Packages required to install this package')]
|
|
[String[]]$Dependencies,
|
|
|
|
[Parameter(Mandatory,HelpMessage='Script to run after the package installation.')]
|
|
[String]$AfterInstallScript,
|
|
|
|
[Parameter(Mandatory,HelpMessage='Script to run after the package removal.')]
|
|
[String]$AfterRemoveScript,
|
|
|
|
[String]$Distribution = 'rhel.7',
|
|
[string]$HostArchitecture
|
|
)
|
|
|
|
# RPM doesn't allow hyphens in version, so convert them to underscores
|
|
# e.g., "7.6.0-preview.6" becomes Version: 7.6.0_preview.6
|
|
$rpmVersion = $Version -replace '-', '_'
|
|
|
|
# Build Release field with distribution suffix (e.g., "1.cm" or "1.rh")
|
|
# Don't use RPM macros - build the full release string in PowerShell
|
|
$rpmRelease = "$Iteration.$Distribution"
|
|
|
|
$specContent = @"
|
|
# RPM spec file for PowerShell
|
|
# Generated by PowerShell build system
|
|
|
|
Name: $Name
|
|
Version: $rpmVersion
|
|
Release: $rpmRelease
|
|
Summary: PowerShell - Cross-platform automation and configuration tool/framework
|
|
License: MIT
|
|
URL: https://microsoft.com/powershell
|
|
AutoReq: no
|
|
|
|
"@
|
|
|
|
# Only add BuildArch if not doing cross-architecture build
|
|
# For cross-arch builds, we'll rely on --target option
|
|
if ($HostArchitecture -eq "x86_64" -or $HostArchitecture -eq "noarch") {
|
|
$specContent += "BuildArch: $HostArchitecture`n`n"
|
|
} else {
|
|
# For cross-architecture builds, don't specify BuildArch in spec
|
|
# The --target option will handle the architecture
|
|
|
|
# Disable automatic binary stripping for cross-arch builds
|
|
# The native /bin/strip on x86_64 cannot process ARM64 binaries and would fail with:
|
|
# "Unable to recognise the format of the input file"
|
|
# See: https://rpm-software-management.github.io/rpm/manual/macros.html
|
|
# __strip: This macro controls the command used for stripping binaries during the build process.
|
|
# /bin/true: A command that does nothing and always exits successfully, effectively bypassing the stripping process.
|
|
$specContent += "%define __strip /bin/true`n"
|
|
|
|
# Disable debug package generation to prevent strip-related errors
|
|
# Debug packages require binary stripping which fails for cross-arch builds
|
|
# See: https://rpm-packaging-guide.github.io/#debugging
|
|
# See: https://docs.fedoraproject.org/en-US/packaging-guidelines/Debuginfo/#_useless_or_incomplete_debuginfo_packages_due_to_other_reasons
|
|
$specContent += "%global debug_package %{nil}`n`n"
|
|
}
|
|
|
|
# Add dependencies
|
|
foreach ($dep in $Dependencies) {
|
|
$specContent += "Requires: $dep`n"
|
|
}
|
|
|
|
$specContent += @"
|
|
|
|
%description
|
|
$Description
|
|
|
|
%prep
|
|
# No prep needed - files are already staged
|
|
|
|
%build
|
|
# No build needed - binaries are pre-built
|
|
|
|
%install
|
|
rm -rf `$RPM_BUILD_ROOT
|
|
mkdir -p `$RPM_BUILD_ROOT$Destination
|
|
mkdir -p `$RPM_BUILD_ROOT$(Split-Path -Parent $ManDestination)
|
|
|
|
# Copy all files from staging to destination
|
|
cp -r $Staging/* `$RPM_BUILD_ROOT$Destination/
|
|
|
|
# Copy man page
|
|
cp $ManGzipFile `$RPM_BUILD_ROOT$ManDestination
|
|
|
|
"@
|
|
|
|
# Add symlinks - we need to get the target of the temp symlink
|
|
foreach ($link in $LinkInfo) {
|
|
$linkDir = Split-Path -Parent $link.Destination
|
|
$specContent += "mkdir -p `$RPM_BUILD_ROOT$linkDir`n"
|
|
# For RPM, we copy the symlink itself.
|
|
# The symlink at $link.Source points to the actual target, so we'll copy it.
|
|
# The -P flag preserves symlinks rather than copying their targets, which is critical for this operation.
|
|
$specContent += "cp -P $($link.Source) `$RPM_BUILD_ROOT$($link.Destination)`n"
|
|
}
|
|
|
|
# Post-install script
|
|
$postInstallContent = Get-Content -Path $AfterInstallScript -Raw
|
|
$specContent += "`n%post`n"
|
|
$specContent += $postInstallContent
|
|
$specContent += "`n"
|
|
|
|
# Post-uninstall script
|
|
$postUninstallContent = Get-Content -Path $AfterRemoveScript -Raw
|
|
$specContent += "%postun`n"
|
|
$specContent += $postUninstallContent
|
|
$specContent += "`n"
|
|
|
|
# Files section
|
|
$specContent += "%files`n"
|
|
$specContent += "%defattr(-,root,root,-)`n"
|
|
$specContent += "$Destination/*`n"
|
|
$specContent += "$ManDestination`n"
|
|
|
|
# Add symlinks to files
|
|
foreach ($link in $LinkInfo) {
|
|
$specContent += "$($link.Destination)`n"
|
|
}
|
|
|
|
# Changelog with correct date format for RPM
|
|
$changelogDate = Get-Date -Format "ddd MMM dd yyyy"
|
|
$specContent += "`n%changelog`n"
|
|
$specContent += "* $changelogDate PowerShell Team <PowerShellTeam@hotmail.com> - $rpmVersion-$rpmRelease`n"
|
|
$specContent += "- Automated build`n"
|
|
|
|
return $specContent
|
|
}
|
|
|
|
function New-NativeDeb
|
|
{
|
|
param(
|
|
[Parameter(Mandatory, HelpMessage='Package Name')]
|
|
[String]$Name,
|
|
|
|
[Parameter(Mandatory, HelpMessage='Package Version')]
|
|
[String]$Version,
|
|
|
|
[Parameter(Mandatory)]
|
|
[String]$Iteration,
|
|
|
|
[Parameter(Mandatory, HelpMessage='Package description')]
|
|
[String]$Description,
|
|
|
|
[Parameter(Mandatory, HelpMessage='Staging folder for installation files')]
|
|
[String]$Staging,
|
|
|
|
[Parameter(Mandatory, HelpMessage='Install path on target machine')]
|
|
[String]$Destination,
|
|
|
|
[Parameter(Mandatory, HelpMessage='The built and gzipped man file.')]
|
|
[String]$ManGzipFile,
|
|
|
|
[Parameter(Mandatory, HelpMessage='The destination of the man file')]
|
|
[String]$ManDestination,
|
|
|
|
[Parameter(Mandatory, HelpMessage='Symlink to powershell executable')]
|
|
[LinkInfo[]]$LinkInfo,
|
|
|
|
[Parameter(HelpMessage='Packages required to install this package.')]
|
|
[String[]]$Dependencies,
|
|
|
|
[Parameter(HelpMessage='Script to run after the package installation.')]
|
|
[String]$AfterInstallScript,
|
|
|
|
[Parameter(HelpMessage='Script to run after the package removal.')]
|
|
[String]$AfterRemoveScript,
|
|
|
|
[string]$HostArchitecture,
|
|
|
|
[string]$CurrentLocation
|
|
)
|
|
|
|
Write-Log "Creating native DEB package..."
|
|
|
|
# Create temporary build directory
|
|
$debBuildRoot = Join-Path $env:HOME "debbuild-$(Get-Random)"
|
|
$debianDir = Join-Path $debBuildRoot "DEBIAN"
|
|
$dataDir = Join-Path $debBuildRoot "data"
|
|
|
|
try {
|
|
New-Item -ItemType Directory -Path $debianDir -Force | Out-Null
|
|
New-Item -ItemType Directory -Path $dataDir -Force | Out-Null
|
|
|
|
# Calculate installed size (in KB)
|
|
$installedSize = 0
|
|
Get-ChildItem -Path $Staging -Recurse -File | ForEach-Object { $installedSize += $_.Length }
|
|
$installedSize += (Get-Item $ManGzipFile).Length
|
|
$installedSizeKB = [Math]::Ceiling($installedSize / 1024)
|
|
|
|
# Create control file with all fields in proper order
|
|
# Description must be single line (first line) followed by extended description with leading space
|
|
$descriptionLines = $Description -split "`n"
|
|
$shortDescription = $descriptionLines[0]
|
|
$extendedDescription = if ($descriptionLines.Count -gt 1) {
|
|
($descriptionLines[1..($descriptionLines.Count-1)] | ForEach-Object { " $_" }) -join "`n"
|
|
}
|
|
|
|
$controlContent = @"
|
|
Package: $Name
|
|
Version: $Version-$Iteration
|
|
Architecture: $HostArchitecture
|
|
Maintainer: PowerShell Team <PowerShellTeam@hotmail.com>
|
|
Installed-Size: $installedSizeKB
|
|
Priority: optional
|
|
Section: shells
|
|
Homepage: https://microsoft.com/powershell
|
|
Depends: $(if ($Dependencies) { $Dependencies -join ', ' })
|
|
Description: $shortDescription
|
|
$(if ($extendedDescription) { $extendedDescription + "`n" })
|
|
"@
|
|
|
|
$controlFile = Join-Path $debianDir "control"
|
|
$controlContent | Out-File -FilePath $controlFile -Encoding ascii -NoNewline
|
|
|
|
Write-Verbose "Control file created: $controlFile" -Verbose
|
|
Write-LogGroup -Title "DEB Control File Content" -Message $controlContent
|
|
|
|
# Copy postinst script if provided
|
|
if ($AfterInstallScript -and (Test-Path $AfterInstallScript)) {
|
|
$postinstFile = Join-Path $debianDir "postinst"
|
|
Copy-Item -Path $AfterInstallScript -Destination $postinstFile -Force
|
|
Start-NativeExecution { chmod 755 $postinstFile }
|
|
Write-Verbose "Postinst script copied to: $postinstFile" -Verbose
|
|
}
|
|
|
|
# Copy postrm script if provided
|
|
if ($AfterRemoveScript -and (Test-Path $AfterRemoveScript)) {
|
|
$postrmFile = Join-Path $debianDir "postrm"
|
|
Copy-Item -Path $AfterRemoveScript -Destination $postrmFile -Force
|
|
Start-NativeExecution { chmod 755 $postrmFile }
|
|
Write-Verbose "Postrm script copied to: $postrmFile" -Verbose
|
|
}
|
|
|
|
# Copy staging files to data directory
|
|
$targetPath = Join-Path $dataDir $Destination.TrimStart('/')
|
|
New-Item -ItemType Directory -Path $targetPath -Force | Out-Null
|
|
Copy-Item -Path "$Staging/*" -Destination $targetPath -Recurse -Force
|
|
Write-Verbose "Copied staging files to: $targetPath" -Verbose
|
|
|
|
# Copy man page
|
|
$manDestPath = Join-Path $dataDir $ManDestination.TrimStart('/')
|
|
$manDestDir = Split-Path $manDestPath -Parent
|
|
New-Item -ItemType Directory -Path $manDestDir -Force | Out-Null
|
|
Copy-Item -Path $ManGzipFile -Destination $manDestPath -Force
|
|
Write-Verbose "Copied man page to: $manDestPath" -Verbose
|
|
|
|
# Copy symlinks from temporary locations
|
|
foreach ($link in $LinkInfo) {
|
|
$linkPath = Join-Path $dataDir $link.Destination.TrimStart('/')
|
|
$linkDir = Split-Path $linkPath -Parent
|
|
New-Item -ItemType Directory -Path $linkDir -Force | Out-Null
|
|
|
|
# Copy the temporary symlink file that was created by New-LinkInfo
|
|
# The Source contains a temporary symlink that points to the correct target
|
|
if (Test-Path $link.Source) {
|
|
# Use cp to preserve the symlink
|
|
Start-NativeExecution { cp -P $link.Source $linkPath }
|
|
Write-Verbose "Copied symlink: $linkPath (from $($link.Source))" -Verbose
|
|
} else {
|
|
Write-Warning "Symlink source not found: $($link.Source)"
|
|
}
|
|
}
|
|
|
|
# Set proper permissions
|
|
Write-Verbose "Setting file permissions..." -Verbose
|
|
# 755 = rwxr-xr-x (owner can read/write/execute, group and others can read/execute)
|
|
Get-ChildItem $dataDir -Directory -Recurse | ForEach-Object {
|
|
Start-NativeExecution { chmod 755 $_.FullName }
|
|
}
|
|
# 644 = rw-r--r-- (owner can read/write, group and others can read only)
|
|
# Exclude symlinks to avoid "cannot operate on dangling symlink" error
|
|
Get-ChildItem $dataDir -File -Recurse |
|
|
Where-Object { -not $_.Target } |
|
|
ForEach-Object {
|
|
Start-NativeExecution { chmod 644 $_.FullName }
|
|
}
|
|
|
|
# Set executable permission for pwsh if it exists
|
|
# 755 = rwxr-xr-x (executable permission)
|
|
$pwshPath = "$targetPath/pwsh"
|
|
if (Test-Path $pwshPath) {
|
|
Start-NativeExecution { chmod 755 $pwshPath }
|
|
}
|
|
|
|
# Included .NET executable for producing crash dumps
|
|
$createdumpPath = "$targetPath/createdump"
|
|
if (Test-Path $createdumpPath) {
|
|
Start-NativeExecution { chmod 755 $createdumpPath }
|
|
}
|
|
|
|
# Calculate md5sums for all files in data directory (excluding symlinks)
|
|
$md5sumsFile = Join-Path $debianDir "md5sums"
|
|
$md5Content = ""
|
|
Get-ChildItem -Path $dataDir -Recurse -File |
|
|
Where-Object { -not $_.Target } |
|
|
ForEach-Object {
|
|
$relativePath = $_.FullName.Substring($dataDir.Length + 1)
|
|
$md5Hash = (Get-FileHash -Path $_.FullName -Algorithm MD5).Hash.ToLower()
|
|
$md5Content += "$md5Hash $relativePath`n"
|
|
}
|
|
$md5Content | Out-File -FilePath $md5sumsFile -Encoding ascii -NoNewline
|
|
Write-Verbose "MD5 sums file created: $md5sumsFile" -Verbose
|
|
|
|
# Build the package using dpkg-deb
|
|
$debFileName = "${Name}_${Version}-${Iteration}_${HostArchitecture}.deb"
|
|
$debFilePath = Join-Path $CurrentLocation $debFileName
|
|
|
|
Write-Verbose "Building DEB package: $debFileName" -Verbose
|
|
|
|
# Copy DEBIAN directory and data files to build root
|
|
$buildDir = Join-Path $debBuildRoot "build"
|
|
New-Item -ItemType Directory -Path $buildDir -Force | Out-Null
|
|
|
|
Write-Verbose "debianDir: $debianDir" -Verbose
|
|
Write-Verbose "dataDir: $dataDir" -Verbose
|
|
Write-Verbose "buildDir: $buildDir" -Verbose
|
|
|
|
# Use cp to preserve symlinks
|
|
Start-NativeExecution { cp -a $debianDir "$buildDir/DEBIAN" }
|
|
Start-NativeExecution { cp -a $dataDir/* $buildDir }
|
|
|
|
# Build package with dpkg-deb
|
|
Start-NativeExecution -VerboseOutputOnError {
|
|
dpkg-deb --build $buildDir $debFilePath
|
|
}
|
|
|
|
if (Test-Path $debFilePath) {
|
|
Write-Log "Successfully created DEB package: $debFileName"
|
|
return @{
|
|
PackagePath = $debFilePath
|
|
PackageName = $debFileName
|
|
}
|
|
} else {
|
|
throw "DEB package file not found after build: $debFilePath"
|
|
}
|
|
}
|
|
finally {
|
|
# Cleanup temporary directory
|
|
if (Test-Path $debBuildRoot) {
|
|
Write-Verbose "Cleaning up temporary build directory: $debBuildRoot" -Verbose
|
|
Remove-Item -Path $debBuildRoot -Recurse -Force -ErrorAction SilentlyContinue
|
|
}
|
|
}
|
|
}
|
|
|
|
function New-MacOSPackage
|
|
{
|
|
[CmdletBinding(SupportsShouldProcess=$true)]
|
|
param(
|
|
[Parameter(Mandatory)]
|
|
[string]$Name,
|
|
|
|
[Parameter(Mandatory)]
|
|
[string]$Version,
|
|
|
|
[Parameter(Mandatory)]
|
|
[string]$Iteration,
|
|
|
|
[Parameter(Mandatory)]
|
|
[string]$Staging,
|
|
|
|
[Parameter(Mandatory)]
|
|
[string]$Destination,
|
|
|
|
[Parameter(Mandatory)]
|
|
[string]$ManGzipFile,
|
|
|
|
[Parameter(Mandatory)]
|
|
[string]$ManDestination,
|
|
|
|
[Parameter(Mandatory)]
|
|
[LinkInfo[]]$LinkInfo,
|
|
|
|
[Parameter(Mandatory)]
|
|
[string]$AfterInstallScript,
|
|
|
|
[Parameter(Mandatory)]
|
|
[string]$AppsFolder,
|
|
|
|
[Parameter(Mandatory)]
|
|
[string]$HostArchitecture,
|
|
|
|
[string]$CurrentLocation = (Get-Location),
|
|
|
|
[switch]$LTS
|
|
)
|
|
|
|
Write-Log "Creating macOS package using pkgbuild and productbuild..."
|
|
|
|
# Create a temporary directory for package building
|
|
$tempRoot = New-TempFolder
|
|
$componentPkgPath = Join-Path $tempRoot "component.pkg"
|
|
$scriptsDir = Join-Path $tempRoot "scripts"
|
|
$resourcesDir = Join-Path $tempRoot "resources"
|
|
$distributionFile = Join-Path $tempRoot "distribution.xml"
|
|
|
|
try {
|
|
# Create scripts directory
|
|
New-Item -ItemType Directory -Path $scriptsDir -Force | Out-Null
|
|
|
|
# Copy and prepare the postinstall script
|
|
$postInstallPath = Join-Path $scriptsDir "postinstall"
|
|
Copy-Item -Path $AfterInstallScript -Destination $postInstallPath -Force
|
|
Start-NativeExecution {
|
|
chmod 755 $postInstallPath
|
|
}
|
|
|
|
# Create a temporary directory for the package root
|
|
$pkgRoot = Join-Path $tempRoot "pkgroot"
|
|
New-Item -ItemType Directory -Path $pkgRoot -Force | Out-Null
|
|
|
|
# Copy staging files to destination path in package root
|
|
$destInPkg = Join-Path $pkgRoot $Destination
|
|
New-Item -ItemType Directory -Path $destInPkg -Force | Out-Null
|
|
Write-Verbose "Copying staging files from $Staging to $destInPkg" -Verbose
|
|
Copy-Item -Path "$Staging/*" -Destination $destInPkg -Recurse -Force
|
|
|
|
# Create man page directory structure
|
|
$manDir = Join-Path $pkgRoot (Split-Path $ManDestination -Parent)
|
|
New-Item -ItemType Directory -Path $manDir -Force | Out-Null
|
|
Copy-Item -Path $ManGzipFile -Destination (Join-Path $pkgRoot $ManDestination) -Force
|
|
|
|
# Create symlinks in package root
|
|
# The LinkInfo contains Source (a temp file that IS a symlink) and Destination (where to install it)
|
|
foreach ($link in $LinkInfo) {
|
|
$linkDestDir = Join-Path $pkgRoot (Split-Path $link.Destination -Parent)
|
|
New-Item -ItemType Directory -Path $linkDestDir -Force | Out-Null
|
|
$finalLinkPath = Join-Path $pkgRoot $link.Destination
|
|
|
|
Write-Verbose "Creating symlink at $finalLinkPath" -Verbose
|
|
|
|
# Remove if exists
|
|
if (Test-Path $finalLinkPath) {
|
|
Remove-Item $finalLinkPath -Force
|
|
}
|
|
|
|
# Get the target of the original symlink and recreate it in the package root
|
|
if (Test-Path $link.Source) {
|
|
$linkTarget = (Get-Item $link.Source).Target
|
|
if ($linkTarget) {
|
|
Write-Verbose "Creating symlink to target: $linkTarget" -Verbose
|
|
New-Item -ItemType SymbolicLink -Path $finalLinkPath -Target $linkTarget -Force | Out-Null
|
|
} else {
|
|
Write-Warning "Could not determine target for symlink at $($link.Source), copying file instead"
|
|
Copy-Item -Path $link.Source -Destination $finalLinkPath -Force
|
|
}
|
|
} else {
|
|
Write-Warning "Source symlink $($link.Source) does not exist"
|
|
}
|
|
}
|
|
|
|
# Copy launcher app folder if provided
|
|
if ($AppsFolder) {
|
|
$appsInPkg = Join-Path $pkgRoot "Applications"
|
|
New-Item -ItemType Directory -Path $appsInPkg -Force | Out-Null
|
|
Write-Verbose "Copying launcher app from $AppsFolder to $appsInPkg" -Verbose
|
|
Copy-Item -Path "$AppsFolder/*" -Destination $appsInPkg -Recurse -Force
|
|
}
|
|
|
|
# Get package identifier info based on version and LTS flag
|
|
$packageInfo = Get-MacOSPackageIdentifierInfo -Version $Version -LTS:$LTS
|
|
$IsPreview = $packageInfo.IsPreview
|
|
$pkgIdentifier = $packageInfo.PackageIdentifier
|
|
|
|
if ($PSCmdlet.ShouldProcess("Build component package with pkgbuild")) {
|
|
Write-Log "Running pkgbuild to create component package..."
|
|
|
|
Start-NativeExecution -VerboseOutputOnError {
|
|
pkgbuild --root $pkgRoot `
|
|
--identifier $pkgIdentifier `
|
|
--version $Version `
|
|
--scripts $scriptsDir `
|
|
--install-location "/" `
|
|
$componentPkgPath
|
|
}
|
|
|
|
Write-Verbose "Component package created: $componentPkgPath" -Verbose
|
|
}
|
|
|
|
# Create the final distribution package using the refactored function
|
|
$distributionPackage = New-MacOsDistributionPackage `
|
|
-ComponentPackage (Get-Item $componentPkgPath) `
|
|
-PackageName $Name `
|
|
-Version $Version `
|
|
-OutputDirectory $CurrentLocation `
|
|
-HostArchitecture $HostArchitecture `
|
|
-PackageIdentifier $pkgIdentifier `
|
|
-IsPreview:$IsPreview
|
|
|
|
return $distributionPackage
|
|
}
|
|
finally {
|
|
# Clean up temporary directory
|
|
if (Test-Path $tempRoot) {
|
|
Write-Verbose "Cleaning up temporary directory: $tempRoot" -Verbose
|
|
Remove-Item -Path $tempRoot -Recurse -Force -ErrorAction SilentlyContinue
|
|
}
|
|
}
|
|
}
|
|
|
|
function Get-PackageDependencies
|
|
{
|
|
[CmdletBinding()]
|
|
param()
|
|
DynamicParam {
|
|
# Add a dynamic parameter '-Distribution' when the specified package type is 'deb'.
|
|
# The '-Distribution' parameter can be used to indicate which Debian distro this package is targeting.
|
|
$ParameterAttr = New-Object "System.Management.Automation.ParameterAttribute"
|
|
$ParameterAttr.Mandatory = $true
|
|
$ValidateSetAttr = New-Object "System.Management.Automation.ValidateSetAttribute" -ArgumentList $Script:AllDistributions
|
|
$Attributes = New-Object "System.Collections.ObjectModel.Collection``1[System.Attribute]"
|
|
$Attributes.Add($ParameterAttr) > $null
|
|
$Attributes.Add($ValidateSetAttr) > $null
|
|
|
|
$Parameter = New-Object "System.Management.Automation.RuntimeDefinedParameter" -ArgumentList ("Distribution", [string], $Attributes)
|
|
$Dict = New-Object "System.Management.Automation.RuntimeDefinedParameterDictionary"
|
|
$Dict.Add("Distribution", $Parameter) > $null
|
|
return $Dict
|
|
}
|
|
|
|
End {
|
|
if ($PSBoundParameters.ContainsKey('Distribution')) {
|
|
$Distribution = $PSBoundParameters['Distribution']
|
|
}
|
|
|
|
# These should match those in the Dockerfiles, but exclude tools like Git, which, and curl
|
|
$Dependencies = @()
|
|
|
|
# ICU version range follows .NET runtime policy.
|
|
# See: https://github.com/dotnet/runtime/blob/3fe8518d51bbcaa179bbe275b2597fbe1b88bc5a/src/native/libs/System.Globalization.Native/pal_icushim.c#L235-L243
|
|
#
|
|
# Version range rationale:
|
|
# - The runtime supports ICU versions >= the version it was built against
|
|
# and <= that version + 30, to allow sufficient headroom for future releases.
|
|
# - ICU typically releases about twice per year, so +30 provides roughly
|
|
# 15 years of forward compatibility.
|
|
# - On some platforms, the minimum supported version may be lower
|
|
# than the build version and we know that older versions just works.
|
|
#
|
|
$MinICUVersion = 60 # runtime minimum supported
|
|
$BuildICUVersion = 76 # current build version
|
|
$MaxICUVersion = $BuildICUVersion + 30 # headroom
|
|
|
|
if ($Distribution -eq 'deb') {
|
|
$Dependencies = @(
|
|
"libc6",
|
|
"libgcc1",
|
|
"libgssapi-krb5-2",
|
|
"libstdc++6",
|
|
"zlib1g",
|
|
(($MaxICUVersion..$MinICUVersion).ForEach{ "libicu$_" } -join '|'),
|
|
"libssl3|libssl1.1|libssl1.0.2|libssl1.0.0"
|
|
)
|
|
} elseif ($Distribution -eq 'rh') {
|
|
$Dependencies = @(
|
|
"openssl-libs",
|
|
"libicu"
|
|
)
|
|
} elseif ($Distribution -eq 'cm') {
|
|
# Taken from the list here:
|
|
# https://github.com/dotnet/dotnet-docker/blob/d451d6e9427f58c8508f1297c862663a27eb609f/src/runtime-deps/6.0/cbl-mariner1.0/amd64/Dockerfile#L6
|
|
$Dependencies = @(
|
|
"glibc"
|
|
"libgcc"
|
|
"krb5"
|
|
"libstdc++"
|
|
"zlib"
|
|
"icu"
|
|
"openssl-libs"
|
|
)
|
|
if($Script:Options.Runtime -like 'fx*') {
|
|
$Dependencies += @(
|
|
"dotnet-runtime-10.0"
|
|
)
|
|
}
|
|
} elseif ($Distribution -eq 'macOS') {
|
|
# do nothing
|
|
} else {
|
|
throw "Unknown distribution $Distribution"
|
|
}
|
|
|
|
return $Dependencies
|
|
}
|
|
}
|
|
|
|
function Test-Dependencies
|
|
{
|
|
# RPM packages use rpmbuild directly.
|
|
# DEB packages use dpkg-deb directly.
|
|
# macOS packages use pkgbuild and productbuild from Xcode Command Line Tools.
|
|
$Dependencies = @()
|
|
|
|
# Check for 'rpmbuild' and 'dpkg-deb' on Azure Linux.
|
|
if ($Environment.IsMariner) {
|
|
$Dependencies += "dpkg-deb"
|
|
$Dependencies += "rpmbuild"
|
|
}
|
|
|
|
# Check for macOS packaging tools
|
|
if ($Environment.IsMacOS) {
|
|
$Dependencies += "pkgbuild"
|
|
$Dependencies += "productbuild"
|
|
}
|
|
|
|
foreach ($Dependency in $Dependencies) {
|
|
if (!(precheck $Dependency "Package dependency '$Dependency' not found. Run Start-PSBootstrap -Scenario Package")) {
|
|
throw "Dependency precheck failed!"
|
|
}
|
|
}
|
|
}
|
|
|
|
function New-AfterScripts
|
|
{
|
|
param(
|
|
[Parameter(Mandatory)]
|
|
[string]
|
|
$Link,
|
|
|
|
[Parameter(Mandatory)]
|
|
[string]
|
|
$Distribution,
|
|
|
|
[Parameter(Mandatory)]
|
|
[string]
|
|
$Destination
|
|
)
|
|
|
|
Write-Verbose -Message "AfterScript Distribution: $Distribution" -Verbose
|
|
|
|
if ($Distribution -in $script:RedHatDistributions) {
|
|
$AfterInstallScript = (Join-Path $env:HOME $([System.IO.Path]::GetRandomFileName()))
|
|
$AfterRemoveScript = (Join-Path $env:HOME $([System.IO.Path]::GetRandomFileName()))
|
|
$packagingStrings.RedHatAfterInstallScript -f "$Link", $Destination | Out-File -FilePath $AfterInstallScript -Encoding ascii
|
|
$packagingStrings.RedHatAfterRemoveScript -f "$Link", $Destination | Out-File -FilePath $AfterRemoveScript -Encoding ascii
|
|
}
|
|
elseif ($Environment.IsDebianFamily -or $Environment.IsSUSEFamily -or $Distribution -in $script:DebianDistributions) {
|
|
$AfterInstallScript = (Join-Path $env:HOME $([System.IO.Path]::GetRandomFileName()))
|
|
$AfterRemoveScript = (Join-Path $env:HOME $([System.IO.Path]::GetRandomFileName()))
|
|
$packagingStrings.UbuntuAfterInstallScript -f "$Link", $Destination | Out-File -FilePath $AfterInstallScript -Encoding ascii
|
|
$packagingStrings.UbuntuAfterRemoveScript -f "$Link", $Destination | Out-File -FilePath $AfterRemoveScript -Encoding ascii
|
|
}
|
|
elseif ($Environment.IsMacOS) {
|
|
# NOTE: The macos pkgutil doesn't support uninstall actions so we did not implement it.
|
|
# Handling uninstall can be done in Homebrew so we'll take advantage of that in the brew formula.
|
|
$AfterInstallScript = (Join-Path $env:HOME $([System.IO.Path]::GetRandomFileName()))
|
|
$packagingStrings.MacOSAfterInstallScript -f "$Link" | Out-File -FilePath $AfterInstallScript -Encoding ascii
|
|
}
|
|
|
|
return [PSCustomObject] @{
|
|
AfterInstallScript = $AfterInstallScript
|
|
AfterRemoveScript = $AfterRemoveScript
|
|
}
|
|
}
|
|
|
|
function New-ManGzip
|
|
{
|
|
param(
|
|
[switch]
|
|
$IsPreview,
|
|
|
|
[switch]
|
|
$IsLTS
|
|
)
|
|
|
|
Write-Log "Creating man gz..."
|
|
|
|
# run roff to convert man page to roff
|
|
$RoffFile = "$RepoRoot/assets/manpage/pwsh.1"
|
|
|
|
if ($IsPreview.IsPresent -or $IsLTS.IsPresent) {
|
|
$prodName = if ($IsLTS) { 'pwsh-lts' } else { 'pwsh-preview' }
|
|
$newRoffFile = $RoffFile -replace 'pwsh', $prodName
|
|
Copy-Item -Path $RoffFile -Destination $newRoffFile -Force -Verbose
|
|
$RoffFile = $newRoffFile
|
|
}
|
|
|
|
# gzip in assets directory
|
|
$GzipFile = "$RoffFile.gz"
|
|
Write-Log "Creating man gz - running gzip..."
|
|
Start-NativeExecution { gzip -kf $RoffFile } -VerboseOutputOnError
|
|
|
|
if($Environment.IsMacOS) {
|
|
$ManFile = Join-Path "/usr/local/share/man/man1" (Split-Path -Leaf $GzipFile)
|
|
}
|
|
else {
|
|
$ManFile = Join-Path "/usr/share/man/man1" (Split-Path -Leaf $GzipFile)
|
|
}
|
|
|
|
return [PSCustomObject ] @{
|
|
GZipFile = $GzipFile
|
|
ManFile = $ManFile
|
|
}
|
|
}
|
|
|
|
<#
|
|
.SYNOPSIS
|
|
Determines the package identifier and preview status for macOS packages.
|
|
.DESCRIPTION
|
|
This function determines if a package is a preview build based on the version string
|
|
and LTS flag, then returns the appropriate package identifier.
|
|
.PARAMETER Version
|
|
The version string (e.g., "7.6.0-preview.6" or "7.6.0")
|
|
.PARAMETER LTS
|
|
Whether this is an LTS build
|
|
.OUTPUTS
|
|
Hashtable with IsPreview (boolean) and PackageIdentifier (string) properties
|
|
.EXAMPLE
|
|
Get-MacOSPackageIdentifierInfo -Version "7.6.0-preview.6" -LTS:$false
|
|
Returns @{ IsPreview = $true; PackageIdentifier = "com.microsoft.powershell-preview" }
|
|
#>
|
|
function Get-MacOSPackageIdentifierInfo
|
|
{
|
|
param(
|
|
[Parameter(Mandatory)]
|
|
[string]$Version,
|
|
|
|
[switch]$LTS
|
|
)
|
|
|
|
$IsPreview = Test-IsPreview -Version $Version -IsLTS:$LTS
|
|
|
|
# Determine package identifier based on preview status
|
|
if ($IsPreview) {
|
|
$PackageIdentifier = 'com.microsoft.powershell-preview'
|
|
}
|
|
else {
|
|
$PackageIdentifier = 'com.microsoft.powershell'
|
|
}
|
|
|
|
return @{
|
|
IsPreview = $IsPreview
|
|
PackageIdentifier = $PackageIdentifier
|
|
}
|
|
}
|
|
|
|
# Dynamically build macOS launcher application.
|
|
function New-MacOSLauncher
|
|
{
|
|
param(
|
|
[Parameter(Mandatory)]
|
|
[String]$Version,
|
|
|
|
[switch]$LTS
|
|
)
|
|
|
|
$packageInfo = Get-MacOSPackageIdentifierInfo -Version $Version -LTS:$LTS
|
|
$IsPreview = $packageInfo.IsPreview
|
|
$packageId = $packageInfo.PackageIdentifier
|
|
|
|
# Define folder for launcher application.
|
|
$suffix = if ($IsPreview) { "-preview" } elseif ($LTS) { "-lts" }
|
|
$macosapp = "$PSScriptRoot/macos/launcher/ROOT/Applications/PowerShell$suffix.app"
|
|
|
|
# Create folder structure for launcher application.
|
|
New-Item -Force -ItemType Directory -Path "$macosapp/Contents/MacOS" | Out-Null
|
|
New-Item -Force -ItemType Directory -Path "$macosapp/Contents/Resources" | Out-Null
|
|
|
|
# Define icns file information.
|
|
if ($IsPreview)
|
|
{
|
|
$iconfile = "$RepoRoot/assets/Powershell-preview.icns"
|
|
}
|
|
else
|
|
{
|
|
$iconfile = "$RepoRoot/assets/Powershell.icns"
|
|
}
|
|
$iconfilebase = (Get-Item -Path $iconfile).BaseName
|
|
|
|
# Copy icns file.
|
|
Copy-Item -Force -Path $iconfile -Destination "$macosapp/Contents/Resources"
|
|
|
|
# Create plist file.
|
|
$plist = "$macosapp/Contents/Info.plist"
|
|
$plistcontent = $packagingStrings.MacOSLauncherPlistTemplate -f $packageId, $Version, $iconfilebase
|
|
$plistcontent | Out-File -Force -Path $plist -Encoding utf8
|
|
|
|
# Create shell script.
|
|
$executablepath = Get-PwshExecutablePath -IsPreview:$IsPreview -IsLTS:$LTS
|
|
$shellscript = "$macosapp/Contents/MacOS/PowerShell.sh"
|
|
$shellscriptcontent = $packagingStrings.MacOSLauncherScript -f $executablepath
|
|
$shellscriptcontent | Out-File -Force -Path $shellscript -Encoding utf8
|
|
|
|
# Set permissions for plist and shell script.
|
|
Start-NativeExecution {
|
|
chmod 644 $plist
|
|
}
|
|
Start-NativeExecution {
|
|
chmod 755 $shellscript
|
|
}
|
|
|
|
# Return the app folder path for packaging
|
|
$appsfolder = (Resolve-Path -Path "$macosapp/..").Path
|
|
|
|
return $appsfolder
|
|
}
|
|
|
|
function Get-PwshExecutablePath
|
|
{
|
|
param(
|
|
[switch] $IsPreview,
|
|
[switch] $IsLTS
|
|
)
|
|
|
|
if ($IsPreview -and $IsLTS)
|
|
{
|
|
throw "Cannot be LTS and Preview"
|
|
}
|
|
|
|
$executableName = if ($IsPreview) {
|
|
"pwsh-preview"
|
|
} elseif ($IsLTS) {
|
|
"pwsh-lts"
|
|
} else {
|
|
"pwsh"
|
|
}
|
|
|
|
if ($Environment.IsLinux) {
|
|
"/usr/bin/$executableName"
|
|
} elseif ($Environment.IsMacOS) {
|
|
"/usr/local/bin/$executableName"
|
|
}
|
|
}
|
|
|
|
function Clear-MacOSLauncher
|
|
{
|
|
# This is needed to prevent installer from picking up
|
|
# the launcher app in the build structure and updating
|
|
# it which locks out subsequent package builds due to
|
|
# increase permissions.
|
|
|
|
# Remove launcher application.
|
|
$macosfolder = "$PSScriptRoot/macos"
|
|
Remove-Item -Force -Recurse -Path $macosfolder
|
|
}
|
|
|
|
function New-StagingFolder
|
|
{
|
|
param(
|
|
[Parameter(Mandatory)]
|
|
[string]
|
|
$StagingPath,
|
|
|
|
[Parameter(Mandatory)]
|
|
[string]
|
|
$PackageSourcePath,
|
|
|
|
[string]
|
|
$Filter = '*',
|
|
|
|
[R2RVerification]
|
|
$R2RVerification
|
|
)
|
|
|
|
Remove-Item -Recurse -Force -ErrorAction SilentlyContinue $StagingPath
|
|
Copy-Item -Recurse $PackageSourcePath $StagingPath -Filter $Filter
|
|
|
|
$smaPath = Join-Path $StagingPath 'System.Management.Automation.dll'
|
|
if ($R2RVerification) {
|
|
$smaInfo = Get-PEInfo -File $smaPath
|
|
switch ($R2RVerification.R2RState) {
|
|
$null {
|
|
Write-Verbose "Skipping R2R verification" -Verbose
|
|
}
|
|
'R2R' {
|
|
Write-Verbose "Verifying R2R was done..." -Verbose
|
|
if (!$smaInfo.CrossGen -or $smaInfo.Architecture -ne $R2RVerification.Architecture -or $smaInfo.OS -ne $R2RVerification.OperatingSystem) {
|
|
throw "System.Management.Automation.dll is not ReadyToRun for $($R2RVerification.OperatingSystem) $($R2RVerification.Architecture). Actually ($($smaInfo.CrossGen) $($smaInfo.OS) $($smaInfo.Architecture) )"
|
|
}
|
|
$mismatchedCrossGenedFiles = @(Get-ChildItem -Path $StagingPath -Filter '*.dll' -Recurse |
|
|
Get-PEInfo |
|
|
Where-Object { $_.CrossGen -and $_.OS -ne $R2RVerification.OperatingSystem -and $_.Architecture -ne $R2RVerification.Architecture })
|
|
if ($mismatchedCrossGenedFiles.Count -gt 0) {
|
|
foreach ($file in $mismatchedCrossGenedFiles) {
|
|
Write-Warning "Misconfigured ReadyToRun file found. Expected $($R2RVerification.OperatingSystem) $($R2RVerification.Architecture). Actual ($($file.OS) $($file.Architecture) ) "
|
|
}
|
|
throw "Unexpected ReadyToRun files found."
|
|
}
|
|
}
|
|
'NoR2R' {
|
|
Write-Verbose "Verifying no R2R was done..." -Verbose
|
|
$crossGenedFiles = @(Get-ChildItem -Path $StagingPath -Filter '*.dll' -Recurse |
|
|
Get-PEInfo |
|
|
Where-Object { $_.CrossGen })
|
|
if ($crossGenedFiles.Count -gt 0) {
|
|
throw "Unexpected ReadyToRun files found: $($crossGenedFiles | ForEach-Object { $_.Path })"
|
|
}
|
|
}
|
|
'SdkOnly' {
|
|
Write-Verbose "Verifying no R2R was done on SMA..." -Verbose
|
|
if ($smaInfo.CrossGen) {
|
|
throw "System.Management.Automation.dll should not be ReadyToRun"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
# Function to create a zip file for Nano Server and xcopy deployment
|
|
function New-ZipPackage
|
|
{
|
|
[CmdletBinding(SupportsShouldProcess=$true)]
|
|
param (
|
|
|
|
# Name of the Product
|
|
[ValidateNotNullOrEmpty()]
|
|
[string] $PackageName = 'PowerShell',
|
|
|
|
# Suffix of the Name
|
|
[string] $PackageNameSuffix,
|
|
|
|
# Version of the Product
|
|
[Parameter(Mandatory = $true)]
|
|
[ValidateNotNullOrEmpty()]
|
|
[string] $PackageVersion,
|
|
|
|
# Source Path to the Product Files - required to package the contents into an Zip
|
|
[Parameter(Mandatory = $true)]
|
|
[ValidateNotNullOrEmpty()]
|
|
[string] $PackageSourcePath,
|
|
|
|
[switch] $Force,
|
|
|
|
[string] $CurrentLocation = (Get-Location),
|
|
|
|
[R2RVerification] $R2RVerification = [R2RVerification]::new()
|
|
)
|
|
|
|
$ProductSemanticVersion = Get-PackageSemanticVersion -Version $PackageVersion
|
|
|
|
$zipPackageName = $PackageName + "-" + $ProductSemanticVersion
|
|
if ($PackageNameSuffix) {
|
|
$zipPackageName = $zipPackageName, $PackageNameSuffix -join "-"
|
|
}
|
|
|
|
Write-Verbose "Create Zip for Product $zipPackageName"
|
|
|
|
$zipLocationPath = Join-Path $CurrentLocation "$zipPackageName.zip"
|
|
|
|
if ($Force.IsPresent)
|
|
{
|
|
if (Test-Path $zipLocationPath)
|
|
{
|
|
Remove-Item $zipLocationPath
|
|
}
|
|
}
|
|
|
|
if (Get-Command Compress-Archive -ErrorAction Ignore)
|
|
{
|
|
if ($PSCmdlet.ShouldProcess("Create zip package"))
|
|
{
|
|
$staging = "$PSScriptRoot/staging"
|
|
New-StagingFolder -StagingPath $staging -PackageSourcePath $PackageSourcePath -R2RVerification $R2RVerification
|
|
|
|
Compress-Archive -Path $staging\* -DestinationPath $zipLocationPath
|
|
}
|
|
|
|
if (Test-Path $zipLocationPath)
|
|
{
|
|
Write-Log "You can find the Zip @ $zipLocationPath"
|
|
$zipLocationPath
|
|
}
|
|
else
|
|
{
|
|
throw "Failed to create $zipLocationPath"
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Write-Error -Message "Compress-Archive cmdlet is missing in this PowerShell version"
|
|
}
|
|
}
|
|
|
|
# Function to create a zip file of PDB
|
|
function New-PdbZipPackage
|
|
{
|
|
[CmdletBinding(SupportsShouldProcess=$true)]
|
|
param (
|
|
|
|
# Name of the Product
|
|
[ValidateNotNullOrEmpty()]
|
|
[string] $PackageName = 'PowerShell-Symbols',
|
|
|
|
# Suffix of the Name
|
|
[string] $PackageNameSuffix,
|
|
|
|
# Version of the Product
|
|
[Parameter(Mandatory = $true)]
|
|
[string] $PackageVersion,
|
|
|
|
# Source Path to the Product Files - required to package the contents into an Zip
|
|
[Parameter(Mandatory = $true)]
|
|
[string] $PackageSourcePath,
|
|
|
|
[switch] $Force,
|
|
|
|
[string] $CurrentLocation = (Get-Location)
|
|
)
|
|
|
|
$ProductSemanticVersion = Get-PackageSemanticVersion -Version $PackageVersion
|
|
|
|
$zipPackageName = $PackageName + "-" + $ProductSemanticVersion
|
|
if ($PackageNameSuffix) {
|
|
$zipPackageName = $zipPackageName, $PackageNameSuffix -join "-"
|
|
}
|
|
|
|
Write-Verbose "Create Symbols Zip for Product $zipPackageName"
|
|
|
|
$zipLocationPath = Join-Path $CurrentLocation "$zipPackageName.zip"
|
|
|
|
if ($Force.IsPresent)
|
|
{
|
|
if (Test-Path $zipLocationPath)
|
|
{
|
|
Remove-Item $zipLocationPath
|
|
}
|
|
}
|
|
|
|
if (Get-Command Compress-Archive -ErrorAction Ignore)
|
|
{
|
|
if ($PSCmdlet.ShouldProcess("Create zip package"))
|
|
{
|
|
$staging = "$PSScriptRoot/staging"
|
|
|
|
# We should NOT R2R verify the PDB zip
|
|
New-StagingFolder -StagingPath $staging -PackageSourcePath $PackageSourcePath -Filter *.pdb
|
|
|
|
Compress-Archive -Path $staging\* -DestinationPath $zipLocationPath
|
|
}
|
|
|
|
if (Test-Path $zipLocationPath)
|
|
{
|
|
Write-Log "You can find the Zip @ $zipLocationPath"
|
|
$zipLocationPath
|
|
}
|
|
else
|
|
{
|
|
throw "Failed to create $zipLocationPath"
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Write-Error -Message "Compress-Archive cmdlet is missing in this PowerShell version"
|
|
}
|
|
}
|
|
|
|
|
|
function CreateNugetPlatformFolder
|
|
{
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string] $FileName,
|
|
|
|
[Parameter(Mandatory = $true)]
|
|
[string] $Platform,
|
|
|
|
[Parameter(Mandatory = $true)]
|
|
[string] $PackageRuntimesFolder,
|
|
|
|
[Parameter(Mandatory = $true)]
|
|
[string] $PlatformBinPath
|
|
)
|
|
|
|
$destPath = New-Item -ItemType Directory -Path (Join-Path $PackageRuntimesFolder "$Platform/lib/$script:netCoreRuntime")
|
|
$fullPath = Join-Path $PlatformBinPath $FileName
|
|
|
|
if (-not(Test-Path $fullPath)) {
|
|
throw "File not found: $fullPath"
|
|
}
|
|
|
|
Copy-Item -Path $fullPath -Destination $destPath
|
|
Write-Log "Copied $FileName to $Platform at path: $destPath"
|
|
}
|
|
|
|
<#
|
|
.SYNOPSIS
|
|
Creates a CGManifest file containing package dependencies for specified file.
|
|
|
|
.PARAMETER FilePath
|
|
File path name of CGManifest file to be created.
|
|
|
|
.PARAMETER Dependencies
|
|
Array list of dependency tuples:
|
|
[tuple[ [tuple[string, string]], [tuple[string, string]] ] []]
|
|
["Id", "Microsoft.PowerShell.SecretStore"], ["Version", "1.1.1.0"]
|
|
#>
|
|
function New-CGManifest
|
|
{
|
|
param (
|
|
[parameter(Mandatory = $true)]
|
|
[string] $FilePath,
|
|
|
|
[parameter(Mandatory = $false)]
|
|
[tuple[ [tuple[string, string]], [tuple[string, string]] ] []] $Dependencies
|
|
)
|
|
|
|
Write-Verbose -Verbose -Message "Creating CGManifest for SBOM: $Filepath"
|
|
|
|
$Registrations = @()
|
|
|
|
foreach ($dependency in $Dependencies) {
|
|
$component = @{
|
|
Component = @{
|
|
Type = "nuget";
|
|
NuGet = @{
|
|
Name = ($dependency.Item1.Item2); Version = ($dependency.Item2.Item2)
|
|
}
|
|
};
|
|
DevelopmentDependency = "true"
|
|
}
|
|
|
|
$Registrations += $component
|
|
}
|
|
|
|
$manifest = @{ Registrations = $Registrations }
|
|
$jsonManifest = $manifest | ConvertTo-Json -Depth 10
|
|
|
|
$jsonManifest | Out-File -FilePath $FilePath
|
|
}
|
|
|
|
function New-FileDependencies
|
|
{
|
|
param (
|
|
[parameter(Mandatory = $true)]
|
|
[string] $FileBaseName,
|
|
|
|
[parameter(Mandatory = $true)]
|
|
[string] $PackageVersion
|
|
)
|
|
|
|
# Filed a tracking bug for automating generation of dependecy list: https://github.com/PowerShell/PowerShell/issues/6247
|
|
$deps = [System.Collections.ArrayList]::new()
|
|
|
|
switch ($FileBaseName) {
|
|
'Microsoft.Management.Infrastructure.CimCmdlets' {
|
|
$deps.Add([tuple]::Create([tuple]::Create('id', 'System.Management.Automation'), [tuple]::Create('version', $PackageVersion))) > $null
|
|
}
|
|
|
|
'Microsoft.PowerShell.Commands.Diagnostics' {
|
|
$deps.Add([tuple]::Create([tuple]::Create('id', 'System.Management.Automation'), [tuple]::Create('version', $PackageVersion))) > $null
|
|
}
|
|
|
|
'Microsoft.PowerShell.Commands.Management' {
|
|
$deps.Add([tuple]::Create([tuple]::Create('id', 'Microsoft.PowerShell.Security'), [tuple]::Create('version', $PackageVersion))) > $null
|
|
foreach($packageInfo in (Get-ProjectPackageInformation -ProjectName $FileBaseName))
|
|
{
|
|
$deps.Add([tuple]::Create([tuple]::Create('id', $packageInfo.Name), [tuple]::Create('version', $packageInfo.Version))) > $null
|
|
}
|
|
}
|
|
|
|
'Microsoft.PowerShell.Commands.Utility' {
|
|
$deps.Add([tuple]::Create([tuple]::Create('id', 'System.Management.Automation'), [tuple]::Create('version', $PackageVersion))) > $null
|
|
|
|
foreach($packageInfo in (Get-ProjectPackageInformation -ProjectName $FileBaseName))
|
|
{
|
|
$deps.Add([tuple]::Create([tuple]::Create('id', $packageInfo.Name), [tuple]::Create('version', $packageInfo.Version))) > $null
|
|
}
|
|
}
|
|
|
|
'Microsoft.PowerShell.ConsoleHost' {
|
|
$deps.Add([tuple]::Create( [tuple]::Create('id', 'System.Management.Automation'), [tuple]::Create('version', $PackageVersion))) > $null
|
|
foreach($packageInfo in (Get-ProjectPackageInformation -ProjectName $FileBaseName))
|
|
{
|
|
$deps.Add([tuple]::Create([tuple]::Create('id', $packageInfo.Name), [tuple]::Create('version', $packageInfo.Version))) > $null
|
|
}
|
|
}
|
|
|
|
'Microsoft.PowerShell.CoreCLR.Eventing' {
|
|
foreach($packageInfo in (Get-ProjectPackageInformation -ProjectName $FileBaseName))
|
|
{
|
|
$deps.Add([tuple]::Create([tuple]::Create('id', $packageInfo.Name), [tuple]::Create('version', $packageInfo.Version))) > $null
|
|
}
|
|
}
|
|
|
|
'Microsoft.PowerShell.SDK' {
|
|
$deps.Add([tuple]::Create([tuple]::Create('id', 'Microsoft.PowerShell.Commands.Management'), [tuple]::Create('version', $PackageVersion))) > $null
|
|
$deps.Add([tuple]::Create([tuple]::Create('id', 'Microsoft.PowerShell.Commands.Utility'), [tuple]::Create('version', $PackageVersion))) > $null
|
|
$deps.Add([tuple]::Create([tuple]::Create('id', 'Microsoft.PowerShell.ConsoleHost'), [tuple]::Create('version', $PackageVersion))) > $null
|
|
$deps.Add([tuple]::Create([tuple]::Create('id', 'Microsoft.PowerShell.Security'), [tuple]::Create('version', $PackageVersion))) > $null
|
|
$deps.Add([tuple]::Create([tuple]::Create('id', 'System.Management.Automation'), [tuple]::Create('version', $PackageVersion))) > $null
|
|
foreach($packageInfo in (Get-ProjectPackageInformation -ProjectName $FileBaseName))
|
|
{
|
|
$deps.Add([tuple]::Create([tuple]::Create('id', $packageInfo.Name), [tuple]::Create('version', $packageInfo.Version))) > $null
|
|
}
|
|
$deps.Add([tuple]::Create([tuple]::Create('id', 'Microsoft.WSMan.Management'), [tuple]::Create('version', $PackageVersion))) > $null
|
|
$deps.Add([tuple]::Create([tuple]::Create('id', 'Microsoft.PowerShell.Commands.Diagnostics'), [tuple]::Create('version', $PackageVersion))) > $null
|
|
$deps.Add([tuple]::Create([tuple]::Create('id', 'Microsoft.Management.Infrastructure.CimCmdlets'), [tuple]::Create('version', $PackageVersion))) > $null
|
|
}
|
|
|
|
'Microsoft.PowerShell.Security' {
|
|
$deps.Add([tuple]::Create([tuple]::Create('id', 'System.Management.Automation'), [tuple]::Create('version', $PackageVersion))) > $null
|
|
}
|
|
|
|
'Microsoft.WSMan.Management' {
|
|
$deps.Add([tuple]::Create([tuple]::Create('id', 'System.Management.Automation'), [tuple]::Create('version', $PackageVersion))) > $null
|
|
$deps.Add([tuple]::Create([tuple]::Create('id', 'Microsoft.WSMan.Runtime'), [tuple]::Create('version', $PackageVersion))) > $null
|
|
foreach($packageInfo in (Get-ProjectPackageInformation -ProjectName $FileBaseName))
|
|
{
|
|
$deps.Add([tuple]::Create([tuple]::Create('id', $packageInfo.Name), [tuple]::Create('version', $packageInfo.Version))) > $null
|
|
}
|
|
}
|
|
|
|
'Microsoft.WSMan.Runtime' {
|
|
## No dependencies
|
|
}
|
|
|
|
'System.Management.Automation' {
|
|
$deps.Add([tuple]::Create([tuple]::Create('id', 'Microsoft.PowerShell.CoreCLR.Eventing'), [tuple]::Create('version', $PackageVersion))) > $null
|
|
foreach($packageInfo in (Get-ProjectPackageInformation -ProjectName $FileBaseName))
|
|
{
|
|
$deps.Add([tuple]::Create([tuple]::Create('id', $packageInfo.Name), [tuple]::Create('version', $packageInfo.Version))) > $null
|
|
}
|
|
}
|
|
}
|
|
|
|
Write-Output $deps
|
|
}
|
|
|
|
<#
|
|
.SYNOPSIS
|
|
Creates nuget package sources for a single provided binary file.
|
|
|
|
.DESCRIPTION
|
|
Creates IL assemblies, for a single binary file, to be packaged in a NuGet file.
|
|
Includes runtime assemblies for linux and Windows runtime assemblies.
|
|
|
|
.PARAMETER FileName
|
|
File name of binary to create nuget sources for.
|
|
|
|
.PARAMETER PackagePath
|
|
Path where the package source files will be created.
|
|
|
|
.PARAMETER PackageVersion
|
|
Version of the created package.
|
|
|
|
.PARAMETER WinFxdBinPath
|
|
Path to source folder containing Windows framework dependent assemblies.
|
|
|
|
.PARAMETER LinuxFxdBinPath
|
|
Path to source folder containing Linux framework dependent assemblies.
|
|
|
|
.PARAMETER RefAssemblyPath
|
|
Path to the reference assemblies.
|
|
|
|
.PARAMETER CGManifestPath
|
|
Path to the CGManifest.json file.
|
|
#>
|
|
function New-ILNugetPackageSource
|
|
{
|
|
[CmdletBinding(SupportsShouldProcess = $true)]
|
|
param (
|
|
[Parameter(Mandatory = $true)]
|
|
[string] $FileName,
|
|
|
|
[Parameter(Mandatory = $true)]
|
|
[string] $PackagePath,
|
|
|
|
[Parameter(Mandatory = $true)]
|
|
[string] $PackageVersion,
|
|
|
|
[Parameter(Mandatory = $true)]
|
|
[string] $WinFxdBinPath,
|
|
|
|
[Parameter(Mandatory = $true)]
|
|
[string] $LinuxFxdBinPath,
|
|
|
|
[Parameter(Mandatory = $true)]
|
|
[string] $RefAssemblyPath,
|
|
|
|
[string] $CGManifestPath
|
|
|
|
)
|
|
|
|
if (! $Environment.IsWindows)
|
|
{
|
|
throw "New-ILNugetPackageSource can be only executed on Windows platform."
|
|
}
|
|
|
|
if (! $PSCmdlet.ShouldProcess("Create nuget packages at: $PackagePath"))
|
|
{
|
|
return
|
|
}
|
|
|
|
$fileList = @(
|
|
"Microsoft.Management.Infrastructure.CimCmdlets.dll",
|
|
"Microsoft.PowerShell.Commands.Diagnostics.dll",
|
|
"Microsoft.PowerShell.Commands.Management.dll",
|
|
"Microsoft.PowerShell.Commands.Utility.dll",
|
|
"Microsoft.PowerShell.ConsoleHost.dll",
|
|
"Microsoft.PowerShell.CoreCLR.Eventing.dll",
|
|
"Microsoft.PowerShell.Security.dll",
|
|
"Microsoft.PowerShell.SDK.dll",
|
|
"Microsoft.WSMan.Management.dll",
|
|
"Microsoft.WSMan.Runtime.dll",
|
|
"System.Management.Automation.dll")
|
|
|
|
$linuxExceptionList = @(
|
|
"Microsoft.Management.Infrastructure.CimCmdlets.dll",
|
|
"Microsoft.PowerShell.Commands.Diagnostics.dll",
|
|
"Microsoft.PowerShell.CoreCLR.Eventing.dll",
|
|
"Microsoft.WSMan.Management.dll",
|
|
"Microsoft.WSMan.Runtime.dll")
|
|
|
|
$SnkFilePath = "$RepoRoot\src\signing\visualstudiopublic.snk"
|
|
|
|
if (! (Test-Path $PackagePath)) {
|
|
$null = New-Item -Path $PackagePath -ItemType Directory
|
|
}
|
|
|
|
# Remove '.dll' at the end
|
|
$fileBaseName = [System.IO.Path]::GetFileNameWithoutExtension($FileName)
|
|
$filePackageFolder = New-Item (Join-Path $PackagePath $fileBaseName) -ItemType Directory -Force
|
|
$packageRuntimesFolder = New-Item (Join-Path $filePackageFolder.FullName 'runtimes') -ItemType Directory
|
|
|
|
Write-Verbose -Verbose "New-ILNugetPackageSource: Creating package source folder for file: $FileName at: $filePackageFolder"
|
|
|
|
#region ref
|
|
$refFolder = New-Item (Join-Path $filePackageFolder.FullName "ref/$script:netCoreRuntime") -ItemType Directory -Force
|
|
CopyReferenceAssemblies -assemblyName $fileBaseName -refBinPath $RefAssemblyPath -refNugetPath $refFolder -assemblyFileList $fileList -winBinPath $WinFxdBinPath
|
|
#endregion ref
|
|
|
|
$packageRuntimesFolderPath = $packageRuntimesFolder.FullName
|
|
|
|
CreateNugetPlatformFolder -FileName $FileName -Platform 'win' -PackageRuntimesFolder $packageRuntimesFolderPath -PlatformBinPath $WinFxdBinPath
|
|
|
|
Write-Verbose -Verbose "Done creating Windows runtime assemblies for $FileName"
|
|
|
|
if ($linuxExceptionList -notcontains $FileName )
|
|
{
|
|
CreateNugetPlatformFolder -FileName $FileName -Platform 'unix' -PackageRuntimesFolder $packageRuntimesFolderPath -PlatformBinPath $LinuxFxdBinPath
|
|
Write-Verbose -Verbose "Done creating Linux runtime assemblies for $FileName"
|
|
}
|
|
else {
|
|
Write-Verbose -Verbose "Skipping creating Linux runtime assemblies for $FileName"
|
|
}
|
|
|
|
if ($FileName -eq "Microsoft.PowerShell.SDK.dll")
|
|
{
|
|
# Copy the '$PSHOME\ref' folder to the NuGet package, so 'dotnet publish' can deploy the 'ref' folder to the publish folder.
|
|
# This is to make 'Add-Type' work in application that hosts PowerShell.
|
|
|
|
$contentFolder = New-Item (Join-Path $filePackageFolder "contentFiles\any\any") -ItemType Directory -Force
|
|
$dotnetRefAsmFolder = Join-Path -Path $WinFxdBinPath -ChildPath "ref"
|
|
Copy-Item -Path $dotnetRefAsmFolder -Destination $contentFolder -Recurse -Force
|
|
Write-Log "Copied the reference assembly folder to contentFiles for the SDK package"
|
|
|
|
# Copy the built-in module folders to the NuGet package, so 'dotnet publish' can deploy those modules to the $pshome module path.
|
|
# This is for enabling applications that hosts PowerShell to ship the built-in modules.
|
|
|
|
$winBuiltInModules = @(
|
|
"CimCmdlets",
|
|
"Microsoft.PowerShell.Diagnostics",
|
|
"Microsoft.PowerShell.Host",
|
|
"Microsoft.PowerShell.Management",
|
|
"Microsoft.PowerShell.Security",
|
|
"Microsoft.PowerShell.Utility",
|
|
"Microsoft.WSMan.Management",
|
|
"PSDiagnostics"
|
|
)
|
|
|
|
$unixBuiltInModules = @(
|
|
"Microsoft.PowerShell.Host",
|
|
"Microsoft.PowerShell.Management",
|
|
"Microsoft.PowerShell.Security",
|
|
"Microsoft.PowerShell.Utility"
|
|
)
|
|
|
|
$winModuleFolder = New-Item (Join-Path $contentFolder "runtimes\win\lib\$script:netCoreRuntime\Modules") -ItemType Directory -Force
|
|
$unixModuleFolder = New-Item (Join-Path $contentFolder "runtimes\unix\lib\$script:netCoreRuntime\Modules") -ItemType Directory -Force
|
|
|
|
foreach ($module in $winBuiltInModules) {
|
|
$source = Join-Path $WinFxdBinPath "Modules\$module"
|
|
Copy-Item -Path $source -Destination $winModuleFolder -Recurse -Force
|
|
}
|
|
|
|
foreach ($module in $unixBuiltInModules) {
|
|
$source = Join-Path $LinuxFxdBinPath "Modules\$module"
|
|
Copy-Item -Path $source -Destination $unixModuleFolder -Recurse -Force
|
|
}
|
|
|
|
Write-Log "Copied the built-in modules to contentFiles for the SDK package"
|
|
}
|
|
else {
|
|
Write-Verbose -Verbose "Skipping copying the built-in modules and reference assemblies for $FileName"
|
|
}
|
|
|
|
if (-not $PSBoundParameters.ContainsKey("CGManifestPath")) {
|
|
Write-Verbose -Verbose "CGManifestPath is not provided. Skipping CGManifest creation."
|
|
return
|
|
}
|
|
|
|
# Create a CGManifest file that lists all dependencies for this package, which is used when creating the SBOM.
|
|
if (! (Test-Path -Path $CGManifestPath)) {
|
|
$null = New-Item -Path $CGManifestPath -ItemType Directory
|
|
}
|
|
$deps = New-FileDependencies -FileBaseName $fileBaseName -PackageVersion $PackageVersion
|
|
New-CGManifest -FilePath (Join-Path -Path $CGManifestPath -ChildPath "CGManifest.json") -Dependencies $deps
|
|
}
|
|
|
|
<#
|
|
.SYNOPSIS
|
|
Creates a nuget package file from the provided source path.
|
|
|
|
.PARAMETER FileName
|
|
File name of binary to create nuget package for.
|
|
|
|
.PARAMETER PackagePath
|
|
Path for the source files and the created NuGet package file.
|
|
#>
|
|
function New-ILNugetPackageFromSource
|
|
{
|
|
[CmdletBinding(SupportsShouldProcess = $true)]
|
|
param (
|
|
[Parameter(Mandatory = $true)]
|
|
[string] $FileName,
|
|
|
|
[Parameter(Mandatory = $true)]
|
|
[string] $PackageVersion,
|
|
|
|
[Parameter(Mandatory = $true)]
|
|
[string] $PackagePath
|
|
)
|
|
|
|
if (! $Environment.IsWindows)
|
|
{
|
|
throw "New-ILNugetPackageFromSource can be only executed on Windows platform."
|
|
}
|
|
|
|
if (! $PSCmdlet.ShouldProcess("Create nuget package for file $FileName at: $PackagePath"))
|
|
{
|
|
return
|
|
}
|
|
|
|
$fileBaseName = [System.IO.Path]::GetFileNameWithoutExtension($FileName)
|
|
|
|
$deps = New-FileDependencies -FileBaseName $fileBaseName -PackageVersion $PackageVersion
|
|
|
|
$srcFilePackagePath = Join-Path $PackagePath $fileBaseName
|
|
|
|
Write-Verbose -Verbose "New-ILNugetPackageFromSource: Creating nuget package for file: $FileName from source path: $srcFilePackagePath"
|
|
|
|
if (! (Test-Path $srcFilePackagePath)) {
|
|
$msg = "Expected nuget source path $srcFilePackagePath for file $fileBaseName does not exist."
|
|
Write-Verbose -Verbose -Message $msg
|
|
throw $msg
|
|
}
|
|
|
|
# Remove the CGManifest file used to create the SBOM.
|
|
$cgManifestPath = Join-Path -Path $PackagePath -ChildPath 'CGManifest'
|
|
$cgManifestFilePath = Join-Path -Path $cgManifestPath -ChildPath 'CGManifest.json'
|
|
if (Test-Path -Path $cgManifestFilePath)
|
|
{
|
|
Write-Verbose -Verbose "Removing CGManifest file: $cgManifestFilePath"
|
|
Remove-Item -Path $cgManifestFilePath -Force -ErrorAction Continue
|
|
}
|
|
|
|
New-NuSpec -PackageId $fileBaseName -PackageVersion $PackageVersion -Dependency $deps -FilePath (Join-Path $srcFilePackagePath "$fileBaseName.nuspec")
|
|
|
|
# Copy icon file to package
|
|
Copy-Item -Path $iconPath -Destination "$srcFilePackagePath/$iconFileName" -Verbose
|
|
|
|
New-NugetPackage -NuSpecPath $srcFilePackagePath -PackageDestinationPath $PackagePath
|
|
|
|
# Remove file nuget package source directory
|
|
Remove-Item $srcFilePackagePath -Recurse -Force -ErrorAction SilentlyContinue
|
|
}
|
|
|
|
<#
|
|
Copy the generated reference assemblies to the 'ref/net8.0' folder properly.
|
|
This is a helper function used by 'New-ILNugetPackageSource'.
|
|
#>
|
|
function CopyReferenceAssemblies
|
|
{
|
|
param(
|
|
[string] $assemblyName,
|
|
[string] $refBinPath,
|
|
[string] $refNugetPath,
|
|
[string[]] $assemblyFileList,
|
|
[string] $winBinPath
|
|
)
|
|
|
|
$supportedRefList = @(
|
|
"Microsoft.PowerShell.Commands.Utility",
|
|
"Microsoft.PowerShell.ConsoleHost",
|
|
"Microsoft.PowerShell.Commands.Management",
|
|
"Microsoft.PowerShell.Commands.Security",
|
|
"System.Management.Automation"
|
|
)
|
|
|
|
switch ($assemblyName) {
|
|
"Microsoft.PowerShell.SDK" {
|
|
foreach ($asmFileName in $assemblyFileList) {
|
|
$fileName = [System.IO.Path]::GetFileNameWithoutExtension($asmFileName)
|
|
|
|
if ($fileName -in $supportedRefList) {
|
|
$refFile = Join-Path -Path $refBinPath -ChildPath $asmFileName
|
|
if (Test-Path -Path $refFile) {
|
|
$refDoc = Join-Path -Path $refBinPath -ChildPath ([System.IO.Path]::ChangeExtension($asmFileName, "xml"))
|
|
Copy-Item $refFile, $refDoc -Destination $refNugetPath -Force
|
|
Write-Log "Copied file '$refFile' and '$refDoc' to '$refNugetPath'"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
default {
|
|
$refDll = Join-Path -Path $refBinPath -ChildPath "$assemblyName.dll"
|
|
$refDoc = Join-Path -Path $refBinPath -ChildPath "$assemblyName.xml"
|
|
Copy-Item $refDll, $refDoc -Destination $refNugetPath -Force
|
|
Write-Log "Copied file '$refDll' and '$refDoc' to '$refNugetPath'"
|
|
}
|
|
}
|
|
}
|
|
|
|
<#
|
|
.SYNOPSIS
|
|
Return the list of packages and versions used by a project
|
|
|
|
.PARAMETER ProjectName
|
|
The name of the project to get the projects for.
|
|
#>
|
|
function Get-ProjectPackageInformation
|
|
{
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]
|
|
$ProjectName
|
|
)
|
|
|
|
$csproj = "$RepoRoot\src\$ProjectName\$ProjectName.csproj"
|
|
[xml] $csprojXml = (Get-Content -Raw -Path $csproj)
|
|
|
|
# get the package references
|
|
$packages=$csprojXml.Project.ItemGroup.PackageReference
|
|
|
|
# check to see if there is a newer package for each refernce
|
|
foreach($package in $packages)
|
|
{
|
|
if ($package.Version -notmatch '\*' -and $package.Include)
|
|
{
|
|
# Get the name of the package
|
|
[PSCustomObject] @{
|
|
Name = $package.Include
|
|
Version = $package.Version
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
<#
|
|
.SYNOPSIS
|
|
Creates a nuspec file.
|
|
|
|
.PARAMETER PackageId
|
|
ID of the package.
|
|
|
|
.PARAMETER PackageVersion
|
|
Version of the package.
|
|
|
|
.PARAMETER Dependency
|
|
Depedencies of the package.
|
|
|
|
.PARAMETER FilePath
|
|
Path to create the nuspec file.
|
|
#>
|
|
function New-NuSpec {
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string] $PackageId,
|
|
|
|
[Parameter(Mandatory = $true)]
|
|
[string] $PackageVersion,
|
|
|
|
[Parameter(Mandatory = $false)]
|
|
# An array of tuples of tuples to define the dependencies.
|
|
# First tuple defines 'id' and value eg: ["id", "System.Data.SqlClient"]
|
|
# Second tuple defines 'version' and value eg: ["version", "4.4.2"]
|
|
# Both these tuples combined together define one dependency.
|
|
# An array represents all the dependencies.
|
|
[tuple[ [tuple[string, string]], [tuple[string, string]] ] []] $Dependency,
|
|
|
|
[Parameter(Mandatory = $true)]
|
|
[string] $FilePath
|
|
)
|
|
|
|
if (-not $Environment.IsWindows)
|
|
{
|
|
throw "New-NuSpec can be only executed on Windows platform."
|
|
}
|
|
|
|
$nuspecTemplate = $packagingStrings.NuspecTemplate -f $PackageId,$PackageVersion,$iconFileName
|
|
$nuspecObj = [xml] $nuspecTemplate
|
|
|
|
if ( ($null -ne $Dependency) -and $Dependency.Count -gt 0 ) {
|
|
|
|
foreach($dep in $Dependency) {
|
|
# Each item is [tuple[ [tuple[string, string]], [tuple[string, string]] ]
|
|
$d = $nuspecObj.package.metadata.dependencies.group.AppendChild($nuspecObj.CreateElement("dependency"))
|
|
|
|
# 'id' and value
|
|
$d.SetAttribute($dep.Item1.Item1, $dep.Item1.Item2)
|
|
|
|
# 'version' and value
|
|
$d.SetAttribute($dep.Item2.Item1, $dep.Item2.Item2)
|
|
}
|
|
}
|
|
|
|
$nuspecObj.Save($filePath)
|
|
}
|
|
|
|
<#
|
|
.SYNOPSIS
|
|
Create a reference assembly from System.Management.Automation.dll
|
|
|
|
.DESCRIPTION
|
|
A unix variant of System.Management.Automation.dll is converted to a reference assembly.
|
|
GenAPI.exe generated the CS file containing the APIs.
|
|
This file is cleaned up and then compiled into a dll.
|
|
|
|
.PARAMETER Unix64BinPath
|
|
Path to the folder containing unix 64 bit assemblies.
|
|
|
|
.PARAMETER RefAssemblyDestinationPath
|
|
Path to the folder where the reference assembly is created.
|
|
|
|
.PARAMETER RefAssemblyVersion
|
|
Version of the reference assembly.
|
|
|
|
.PARAMETER GenAPIToolPath
|
|
Path to GenAPI.exe. Tool from https://www.nuget.org/packages/Microsoft.DotNet.BuildTools.GenAPI/
|
|
|
|
.PARAMETER SnkFilePath
|
|
Path to the snk file for strong name signing.
|
|
#>
|
|
|
|
function New-ReferenceAssembly
|
|
{
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string] $Linux64BinPath,
|
|
|
|
[Parameter(Mandatory = $true)]
|
|
[string] $RefAssemblyDestinationPath,
|
|
|
|
[Parameter(Mandatory = $true)]
|
|
[string] $RefAssemblyVersion,
|
|
|
|
[Parameter(Mandatory = $true)]
|
|
[string] $GenAPIToolPath,
|
|
|
|
[Parameter(Mandatory = $true)]
|
|
[string] $SnkFilePath
|
|
)
|
|
|
|
if (-not $Environment.IsWindows)
|
|
{
|
|
throw "New-ReferenceAssembly can be only executed on Windows platform."
|
|
}
|
|
|
|
$genAPIExe = Get-ChildItem -Path "$GenAPIToolPath/*GenAPI.exe" -Recurse
|
|
|
|
if (-not (Test-Path $genAPIExe))
|
|
{
|
|
throw "GenAPI.exe was not found at: $GenAPIToolPath"
|
|
}
|
|
|
|
Write-Log "GenAPI nuget package saved and expanded."
|
|
|
|
$genAPIFolder = New-TempFolder
|
|
Write-Log "Working directory: $genAPIFolder."
|
|
|
|
$SMAReferenceAssembly = $null
|
|
$assemblyNames = @(
|
|
"System.Management.Automation",
|
|
"Microsoft.PowerShell.Commands.Utility",
|
|
"Microsoft.PowerShell.ConsoleHost"
|
|
)
|
|
|
|
# Ensure needed dotNet version is available. Find-DotNet does this, and is part of build.psm1 which should already be imported.
|
|
Find-DotNet -Verbose
|
|
|
|
foreach ($assemblyName in $assemblyNames) {
|
|
|
|
Write-Log "Building reference assembly for '$assemblyName'"
|
|
$projectFolder = New-Item -Path "$genAPIFolder/$assemblyName" -ItemType Directory -Force
|
|
$generatedSource = Join-Path $projectFolder "$assemblyName.cs"
|
|
$filteredSource = Join-Path $projectFolder "${assemblyName}_Filtered.cs"
|
|
|
|
$linuxDllPath = Join-Path $Linux64BinPath "$assemblyName.dll"
|
|
if (-not (Test-Path $linuxDllPath)) {
|
|
throw "$assemblyName.dll was not found at: $Linux64BinPath"
|
|
}
|
|
|
|
$dllXmlDoc = Join-Path $Linux64BinPath "$assemblyName.xml"
|
|
if (-not (Test-Path $dllXmlDoc)) {
|
|
throw "$assemblyName.xml was not found at: $Linux64BinPath"
|
|
}
|
|
|
|
$genAPIArgs = "$linuxDllPath","-libPath:$Linux64BinPath,$Linux64BinPath\ref"
|
|
Write-Log "GenAPI cmd: $genAPIExe $genAPIArgs"
|
|
|
|
Start-NativeExecution { & $genAPIExe $genAPIArgs } | Out-File $generatedSource -Force
|
|
Write-Log "Reference assembly file generated at: $generatedSource"
|
|
|
|
CleanupGeneratedSourceCode -assemblyName $assemblyName -generatedSource $generatedSource -filteredSource $filteredSource
|
|
|
|
try
|
|
{
|
|
Push-Location $projectFolder
|
|
|
|
$sourceProjectRoot = Join-Path $PSScriptRoot "projects/reference/$assemblyName"
|
|
$sourceProjectFile = Join-Path $sourceProjectRoot "$assemblyName.csproj"
|
|
|
|
$destProjectFile = Join-Path $projectFolder "$assemblyName.csproj"
|
|
$nugetConfigFile = Join-Path $PSScriptRoot "../../nuget.config"
|
|
|
|
Copy-Item -Path $sourceProjectFile -Destination $destProjectFile -Force -Verbose
|
|
Copy-Item -Path $nugetConfigFile -Destination $projectFolder -Verbose
|
|
|
|
Send-AzdoFile -Path $destProjectFile
|
|
Send-AzdoFile -Path $generatedSource
|
|
|
|
$arguments = GenerateBuildArguments -AssemblyName $assemblyName -RefAssemblyVersion $RefAssemblyVersion -SnkFilePath $SnkFilePath -SMAReferencePath $SMAReferenceAssembly
|
|
|
|
Write-Log "Running: dotnet $arguments"
|
|
Start-NativeExecution -sb {dotnet $arguments}
|
|
|
|
$refBinPath = Join-Path $projectFolder "bin/Release/$script:netCoreRuntime/$assemblyName.dll"
|
|
if ($null -eq $refBinPath) {
|
|
throw "Reference assembly was not built."
|
|
}
|
|
|
|
Copy-Item $refBinPath $RefAssemblyDestinationPath -Force
|
|
Write-Log "Reference assembly '$assemblyName.dll' built and copied to $RefAssemblyDestinationPath"
|
|
|
|
Copy-Item $dllXmlDoc $RefAssemblyDestinationPath -Force
|
|
Write-Log "Xml document '$assemblyName.xml' copied to $RefAssemblyDestinationPath"
|
|
|
|
if ($assemblyName -eq "System.Management.Automation") {
|
|
$SMAReferenceAssembly = $refBinPath
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
Pop-Location
|
|
}
|
|
}
|
|
|
|
if (Test-Path $genAPIFolder)
|
|
{
|
|
Remove-Item $genAPIFolder -Recurse -Force -ErrorAction SilentlyContinue
|
|
}
|
|
}
|
|
|
|
<#
|
|
Helper function for New-ReferenceAssembly to further clean up the
|
|
C# source code generated from GenApi.exe.
|
|
#>
|
|
function CleanupGeneratedSourceCode
|
|
{
|
|
param(
|
|
[string] $assemblyName,
|
|
[string] $generatedSource,
|
|
[string] $filteredSource
|
|
)
|
|
|
|
$patternsToRemove = @(
|
|
'[System.Management.Automation.ArgumentToEncodingTransformationAttribute]'
|
|
'typeof(System.Security.AccessControl.FileSecurity)'
|
|
'[System.Management.Automation.ArgumentTypeConverterAttribute'
|
|
'[System.Runtime.CompilerServices.IteratorStateMachineAttribute'
|
|
'[Microsoft.PowerShell.Commands.ArgumentToModuleTransformationAttribute]'
|
|
'[Microsoft.PowerShell.Commands.SetStrictModeCommand.ArgumentToVersionTransformationAttribute]'
|
|
'[Microsoft.PowerShell.Commands.SetStrictModeCommand.ValidateVersionAttribute]'
|
|
'[System.Management.Automation.OutputTypeAttribute(typeof(System.Management.Automation.PSRemotingJob))]'
|
|
'typeof(System.Management.Automation.LanguagePrimitives.EnumMultipleTypeConverter)'
|
|
'[System.Management.Automation.Internal.CommonParameters.ValidateVariableName]'
|
|
'[System.Management.Automation.ArgumentEncodingCompletionsAttribute]'
|
|
'[Microsoft.PowerShell.Commands.AddMemberCommand'
|
|
'[System.Management.Automation.ArgumentCompleterAttribute(typeof(Microsoft.PowerShell.Commands.Utility.JoinItemCompleter))]'
|
|
'[System.Management.Automation.ArgumentCompleterAttribute(typeof(System.Management.Automation.PropertyNameCompleter))]'
|
|
'[Microsoft.PowerShell.Commands.ArgumentToTypeNameTransformationAttribute]'
|
|
'[System.Management.Automation.Internal.ArchitectureSensitiveAttribute]'
|
|
'[Microsoft.PowerShell.Commands.SelectStringCommand.FileinfoToStringAttribute]'
|
|
'[System.Runtime.CompilerServices.IsReadOnlyAttribute]'
|
|
'[System.Runtime.CompilerServices.NullableContextAttribute('
|
|
'[System.Runtime.CompilerServices.NullableAttribute((byte)0)]'
|
|
'[System.Runtime.CompilerServices.NullableAttribute(new byte[]{ (byte)2, (byte)1, (byte)1})]'
|
|
'[System.Runtime.CompilerServices.AsyncStateMachineAttribute'
|
|
'[Microsoft.PowerShell.Commands.SetStrictModeCommand.ArgumentToPSVersionTransformationAttribute]'
|
|
'[Microsoft.PowerShell.Commands.HttpVersionCompletionsAttribute]'
|
|
'[System.Management.Automation.ArgumentToVersionTransformationAttribute]'
|
|
'[Microsoft.PowerShell.Commands.InvokeCommandCommand.ArgumentToPSVersionTransformationAttribute]'
|
|
'[Microsoft.PowerShell.Commands.InvokeCommandCommand.ValidateVersionAttribute]',
|
|
'[System.Management.Automation.OutputTypeAttribute(new System.Type[]{ typeof(Microsoft.PowerShell.Commands.Internal.Format.FormatStartData), typeof(Microsoft.PowerShell.Commands.Internal.Format.FormatEntryData), typeof(Microsoft.PowerShell.Commands.Internal.Format.FormatEndData), typeof(Microsoft.PowerShell.Commands.Internal.Format.GroupStartData), typeof(Microsoft.PowerShell.Commands.Internal.Format.GroupEndData)})]'
|
|
)
|
|
|
|
$patternsToReplace = @(
|
|
@{
|
|
ApplyTo = @("Microsoft.PowerShell.Commands.Utility")
|
|
Pattern = "[System.Runtime.CompilerServices.IsReadOnlyAttribute]ref Microsoft.PowerShell.Commands.JsonObject.ConvertToJsonContext"
|
|
Replacement = "in Microsoft.PowerShell.Commands.JsonObject.ConvertToJsonContext"
|
|
},
|
|
@{
|
|
ApplyTo = @("Microsoft.PowerShell.Commands.Utility")
|
|
Pattern = "public partial struct ConvertToJsonContext"
|
|
Replacement = "public readonly struct ConvertToJsonContext"
|
|
},
|
|
@{
|
|
ApplyTo = @("Microsoft.PowerShell.Commands.Utility")
|
|
Pattern = "Unable to resolve assembly 'Assembly(Name=Newtonsoft.Json"
|
|
Replacement = "// Unable to resolve assembly 'Assembly(Name=Newtonsoft.Json"
|
|
},
|
|
@{
|
|
ApplyTo = @("System.Management.Automation")
|
|
Pattern = "Unable to resolve assembly 'Assembly(Name=System.Security.Principal.Windows"
|
|
Replacement = "// Unable to resolve assembly 'Assembly(Name=System.Security.Principal.Windows"
|
|
},
|
|
@{
|
|
ApplyTo = @("System.Management.Automation")
|
|
Pattern = "Unable to resolve assembly 'Assembly(Name=Microsoft.Management.Infrastructure"
|
|
Replacement = "// Unable to resolve assembly 'Assembly(Name=Microsoft.Management.Infrastructure"
|
|
},
|
|
@{
|
|
ApplyTo = @("System.Management.Automation")
|
|
Pattern = "Unable to resolve assembly 'Assembly(Name=System.Security.AccessControl"
|
|
Replacement = "// Unable to resolve assembly 'Assembly(Name=System.Security.AccessControl"
|
|
},
|
|
@{
|
|
ApplyTo = @("System.Management.Automation")
|
|
Pattern = "[System.Runtime.CompilerServices.NullableAttribute(new byte[]{ (byte)1, (byte)2, (byte)1})]"
|
|
Replacement = "/* [System.Runtime.CompilerServices.NullableAttribute(new byte[]{ (byte)1, (byte)2, (byte)1})] */ "
|
|
},
|
|
@{
|
|
ApplyTo = @("System.Management.Automation")
|
|
Pattern = "[System.Runtime.CompilerServices.NullableAttribute(new byte[]{ (byte)2, (byte)1})]"
|
|
Replacement = "/* [System.Runtime.CompilerServices.NullableAttribute(new byte[]{ (byte)2, (byte)1})] */ "
|
|
},
|
|
@{
|
|
ApplyTo = @("System.Management.Automation")
|
|
Pattern = "[System.Runtime.CompilerServices.CompilerGeneratedAttribute, System.Runtime.CompilerServices.NullableContextAttribute((byte)2)]"
|
|
Replacement = "/* [System.Runtime.CompilerServices.CompilerGeneratedAttribute, System.Runtime.CompilerServices.NullableContextAttribute((byte)2)] */ "
|
|
},
|
|
@{
|
|
ApplyTo = @("System.Management.Automation")
|
|
Pattern = "[System.Runtime.CompilerServices.CompilerGeneratedAttribute, System.Runtime.CompilerServices.IsReadOnlyAttribute]"
|
|
Replacement = "/* [System.Runtime.CompilerServices.CompilerGeneratedAttribute, System.Runtime.CompilerServices.IsReadOnlyAttribute] */ "
|
|
},
|
|
@{
|
|
ApplyTo = @("System.Management.Automation")
|
|
Pattern = "[System.Runtime.CompilerServices.CompilerGeneratedAttribute, System.Runtime.CompilerServices.NullableContextAttribute((byte)1)]"
|
|
Replacement = "/* [System.Runtime.CompilerServices.CompilerGeneratedAttribute, System.Runtime.CompilerServices.NullableContextAttribute((byte)1)] */ "
|
|
},
|
|
@{
|
|
ApplyTo = @("System.Management.Automation", "Microsoft.PowerShell.ConsoleHost")
|
|
Pattern = "[System.Runtime.CompilerServices.NullableAttribute((byte)2)]"
|
|
Replacement = "/* [System.Runtime.CompilerServices.NullableAttribute((byte)2)] */"
|
|
},
|
|
@{
|
|
ApplyTo = @("System.Management.Automation", "Microsoft.PowerShell.ConsoleHost")
|
|
Pattern = "[System.Runtime.CompilerServices.NullableAttribute((byte)1)]"
|
|
Replacement = "/* [System.Runtime.CompilerServices.NullableAttribute((byte)1)] */"
|
|
}
|
|
)
|
|
|
|
$reader = [System.IO.File]::OpenText($generatedSource)
|
|
$writer = [System.IO.File]::CreateText($filteredSource)
|
|
|
|
while($null -ne ($line = $reader.ReadLine()))
|
|
{
|
|
$lineWasProcessed = $false
|
|
foreach ($patternToReplace in $patternsToReplace)
|
|
{
|
|
if ($assemblyName -in $patternToReplace.ApplyTo -and $line.Contains($patternToReplace.Pattern)) {
|
|
$line = $line.Replace($patternToReplace.Pattern, $patternToReplace.Replacement)
|
|
$lineWasProcessed = $true
|
|
}
|
|
}
|
|
|
|
if (!$lineWasProcessed) {
|
|
$match = Select-String -InputObject $line -Pattern $patternsToRemove -SimpleMatch
|
|
if ($null -ne $match)
|
|
{
|
|
$line = "//$line"
|
|
}
|
|
}
|
|
|
|
$writer.WriteLine($line)
|
|
}
|
|
|
|
if ($null -ne $reader)
|
|
{
|
|
$reader.Close()
|
|
}
|
|
|
|
if ($null -ne $writer)
|
|
{
|
|
$writer.Close()
|
|
}
|
|
|
|
Move-Item $filteredSource $generatedSource -Force
|
|
Write-Log "Code cleanup complete for reference assembly '$assemblyName'."
|
|
}
|
|
|
|
<#
|
|
Helper function for New-ReferenceAssembly to get the arguments
|
|
for building reference assemblies.
|
|
#>
|
|
function GenerateBuildArguments
|
|
{
|
|
param(
|
|
[string] $AssemblyName,
|
|
[string] $RefAssemblyVersion,
|
|
[string] $SnkFilePath,
|
|
[string] $SMAReferencePath
|
|
)
|
|
|
|
$arguments = @('build')
|
|
$arguments += @('-c','Release')
|
|
$arguments += "/p:RefAsmVersion=$RefAssemblyVersion"
|
|
$arguments += "/p:SnkFile=$SnkFilePath"
|
|
|
|
if ($AssemblyName -ne "System.Management.Automation") {
|
|
$arguments += "/p:SmaRefFile=$SMAReferencePath"
|
|
}
|
|
|
|
return $arguments
|
|
}
|
|
|
|
<#
|
|
.SYNOPSIS
|
|
Create a NuGet package from a nuspec.
|
|
|
|
.DESCRIPTION
|
|
Creates a NuGet using the nuspec at the specified folder.
|
|
It is expected that the lib / ref / runtime folders are welformed.
|
|
The genereated NuGet package is copied over to the $PackageDestinationPath
|
|
|
|
.PARAMETER NuSpecPath
|
|
Path to the folder containing the nuspec file.
|
|
|
|
.PARAMETER PackageDestinationPath
|
|
Path to which NuGet package should be copied. Destination is created if it does not exist.
|
|
#>
|
|
|
|
function New-NugetPackage
|
|
{
|
|
[CmdletBinding()]
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string] $NuSpecPath,
|
|
|
|
[Parameter(Mandatory = $true)]
|
|
[string] $PackageDestinationPath
|
|
)
|
|
|
|
$nuget = Get-Command -Type Application nuget -ErrorAction SilentlyContinue
|
|
|
|
if ($null -eq $nuget)
|
|
{
|
|
throw 'nuget application is not available in PATH'
|
|
}
|
|
|
|
Push-Location $NuSpecPath
|
|
|
|
Start-NativeExecution { nuget pack . } > $null
|
|
|
|
if (-not (Test-Path $PackageDestinationPath))
|
|
{
|
|
New-Item $PackageDestinationPath -ItemType Directory -Force > $null
|
|
}
|
|
|
|
Copy-Item *.nupkg $PackageDestinationPath -Force -Verbose
|
|
Pop-Location
|
|
}
|
|
|
|
function New-SubFolder
|
|
{
|
|
[CmdletBinding(SupportsShouldProcess=$true)]
|
|
param(
|
|
[string]
|
|
$Path,
|
|
|
|
[String]
|
|
$ChildPath,
|
|
|
|
[switch]
|
|
$Clean
|
|
)
|
|
|
|
$subFolderPath = Join-Path -Path $Path -ChildPath $ChildPath
|
|
if ($Clean.IsPresent -and (Test-Path $subFolderPath))
|
|
{
|
|
Remove-Item -Path $subFolderPath -Recurse -Force -ErrorAction SilentlyContinue
|
|
}
|
|
|
|
if (!(Test-Path $subFolderPath))
|
|
{
|
|
$null = New-Item -Path $subFolderPath -ItemType Directory
|
|
}
|
|
return $subFolderPath
|
|
}
|
|
|
|
# Builds coming out of this project can have version number as 'a.b.c-stringf.d-e-f' OR 'a.b.c.d-e-f'
|
|
# This function converts the above version into semantic version major.minor[.build-quality[.revision]] format
|
|
function Get-PackageSemanticVersion
|
|
{
|
|
[CmdletBinding()]
|
|
param (
|
|
# Version of the Package
|
|
[Parameter(Mandatory = $true)]
|
|
[ValidateNotNullOrEmpty()]
|
|
[string] $Version,
|
|
[switch] $NuGet
|
|
)
|
|
|
|
Write-Verbose "Extract the semantic version in the form of major.minor[.build-quality[.revision]] for $Version"
|
|
$packageVersionTokens = $Version.Split('.')
|
|
|
|
if ($packageVersionTokens.Count -eq 3) {
|
|
# In case the input is of the form a.b.c, we use the same form
|
|
$packageSemanticVersion = $Version
|
|
} elseif ($packageVersionTokens.Count -eq 4) {
|
|
# We have all the four fields
|
|
$packageRevisionTokens = ($packageVersionTokens[3].Split('-'))[0]
|
|
if ($NuGet.IsPresent)
|
|
{
|
|
$packageRevisionTokens = $packageRevisionTokens.Replace('.','-')
|
|
}
|
|
$packageSemanticVersion = $packageVersionTokens[0],$packageVersionTokens[1],$packageVersionTokens[2],$packageRevisionTokens -join '.'
|
|
} else {
|
|
throw "Cannot create Semantic Version from the string $Version containing 4 or more tokens"
|
|
}
|
|
|
|
$packageSemanticVersion
|
|
}
|
|
|
|
# Builds coming out of this project can have version number as 'M.m.p-previewName[Number]' OR 'M.m.p'
|
|
# This function converts the above version into semantic version major.minor.patch[-previewName[Number]] format
|
|
function Get-LinuxPackageSemanticVersion
|
|
{
|
|
[CmdletBinding()]
|
|
param (
|
|
# Version of the Package
|
|
[Parameter(Mandatory = $true)]
|
|
[ValidatePattern("^\d+\.\d+\.\d+(-\w+(\.\d+)?)?$")]
|
|
[ValidateNotNullOrEmpty()]
|
|
[string] $Version
|
|
)
|
|
|
|
Write-Verbose "Extract the semantic version in the form of major.minor[.build-quality[.revision]] for $Version"
|
|
$packageVersionTokens = $Version.Split('-')
|
|
|
|
if ($packageVersionTokens.Count -eq 1) {
|
|
# In case the input is of the form a.b.c, we use the same form
|
|
$packageSemanticVersion = $Version
|
|
} elseif ($packageVersionTokens.Count -ge 2) {
|
|
$packageRevisionTokens = ($packageVersionTokens[1..($packageVersionTokens.Count-1)] -join '-')
|
|
$packageSemanticVersion = ('{0}-{1}' -f $packageVersionTokens[0], $packageRevisionTokens)
|
|
}
|
|
|
|
$packageSemanticVersion
|
|
}
|
|
|
|
# Builds coming out of this project can have version number as 'a.b.c-stringf.d-e-f' OR 'a.b.c.d-e-f'
|
|
# This function converts the above version into semantic version major.minor[.build-quality[-revision]] format needed for nuget
|
|
function Get-NugetSemanticVersion
|
|
{
|
|
[CmdletBinding()]
|
|
param (
|
|
# Version of the Package
|
|
[Parameter(Mandatory = $true)]
|
|
[ValidateNotNullOrEmpty()]
|
|
[string] $Version
|
|
)
|
|
|
|
$packageVersionTokens = $Version.Split('.')
|
|
|
|
Write-Verbose "Extract the semantic version in the form of major.minor[.build-quality[-revision]] for $Version"
|
|
$versionPartTokens = @()
|
|
$identifierPortionTokens = @()
|
|
$inIdentifier = $false
|
|
foreach($token in $packageVersionTokens) {
|
|
$tokenParts = $null
|
|
if ($token -match '-') {
|
|
$tokenParts = $token.Split('-')
|
|
}
|
|
elseif ($inIdentifier) {
|
|
$tokenParts = @($token)
|
|
}
|
|
|
|
# If we don't have token parts, then it's a versionPart
|
|
if (!$tokenParts) {
|
|
$versionPartTokens += $token
|
|
}
|
|
else {
|
|
foreach($idToken in $tokenParts) {
|
|
# The first token after we detect the id Part is still
|
|
# a version part
|
|
if (!$inIdentifier) {
|
|
$versionPartTokens += $idToken
|
|
$inIdentifier = $true
|
|
}
|
|
else {
|
|
$identifierPortionTokens += $idToken
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($versionPartTokens.Count -gt 3) {
|
|
throw "Cannot create Semantic Version from the string $Version containing 4 or more version tokens"
|
|
}
|
|
|
|
$packageSemanticVersion = ($versionPartTokens -join '.')
|
|
if ($identifierPortionTokens.Count -gt 0) {
|
|
$packageSemanticVersion += '-' + ($identifierPortionTokens -join '-')
|
|
}
|
|
|
|
$packageSemanticVersion
|
|
}
|
|
|
|
<#
|
|
.Synopsis
|
|
Creates a Windows AppX MSIX package and assumes that the binaries are already built using 'Start-PSBuild'.
|
|
This only works on a Windows machine due to the usage of makeappx.exe.
|
|
.EXAMPLE
|
|
# This example shows how to produce a Debug-x64 installer for development purposes.
|
|
cd $RootPathOfPowerShellRepo
|
|
Import-Module .\build.psm1; Import-Module .\tools\packaging\packaging.psm1
|
|
New-MSIXPackage -Verbose -ProductSourcePath '.\src\powershell-win-core\bin\Debug\net8.0\win7-x64\publish' -ProductTargetArchitecture x64 -ProductVersion '1.2.3'
|
|
#>
|
|
function New-MSIXPackage
|
|
{
|
|
[CmdletBinding(SupportsShouldProcess, ConfirmImpact='Low')]
|
|
param (
|
|
|
|
# Name of the Product
|
|
[ValidateNotNullOrEmpty()]
|
|
[string] $ProductName = 'PowerShell',
|
|
|
|
# Suffix of the Name
|
|
[string] $ProductNameSuffix,
|
|
|
|
# Version of the Product
|
|
[Parameter(Mandatory = $true)]
|
|
[ValidateNotNullOrEmpty()]
|
|
[string] $ProductVersion,
|
|
|
|
# Source Path to the Product Files - required to package the contents into an MSIX
|
|
[Parameter(Mandatory = $true)]
|
|
[ValidateNotNullOrEmpty()]
|
|
[string] $ProductSourcePath,
|
|
|
|
# Processor Architecture
|
|
[Parameter(Mandatory = $true)]
|
|
[ValidateSet('x64','x86','arm','arm64')]
|
|
[string] $Architecture,
|
|
|
|
# Produce private package for testing in Store
|
|
[Switch] $Private,
|
|
|
|
# Produce LTS package
|
|
[Switch] $LTS,
|
|
|
|
# Force overwrite of package
|
|
[Switch] $Force,
|
|
|
|
[string] $CurrentLocation = (Get-Location)
|
|
)
|
|
|
|
$makeappx = Get-Command makeappx -CommandType Application -ErrorAction Ignore
|
|
if ($null -eq $makeappx) {
|
|
# This is location in our dockerfile
|
|
$dockerPath = Join-Path $env:SystemDrive "makeappx"
|
|
if (Test-Path $dockerPath) {
|
|
$makeappx = Get-ChildItem $dockerPath -Include makeappx.exe -Recurse | Select-Object -First 1
|
|
}
|
|
|
|
if ($null -eq $makeappx) {
|
|
# Try to find in well known location
|
|
$makeappx = Get-ChildItem "${env:ProgramFiles(x86)}\Windows Kits\10\bin\*\x64" -Include makeappx.exe -Recurse | Select-Object -First 1
|
|
if ($null -eq $makeappx) {
|
|
throw "Could not locate makeappx.exe, make sure Windows 10 SDK is installed"
|
|
}
|
|
}
|
|
}
|
|
|
|
$makepri = Get-Item (Join-Path $makeappx.Directory "makepri.exe") -ErrorAction Stop
|
|
|
|
$displayName = $ProductName
|
|
$ProductSemanticVersion = Get-PackageSemanticVersion -Version $ProductVersion
|
|
|
|
if ($Private) {
|
|
$ProductName = 'PowerShell-Private'
|
|
$displayName = 'PowerShell-Private'
|
|
} elseif ($ProductSemanticVersion.Contains('-')) {
|
|
$ProductName += 'Preview'
|
|
$displayName += ' Preview'
|
|
} elseif ($LTS) {
|
|
$ProductName += '-LTS'
|
|
$displayName += ' LTS'
|
|
}
|
|
|
|
Write-Verbose -Verbose "ProductName: $productName"
|
|
Write-Verbose -Verbose "DisplayName: $displayName"
|
|
|
|
$packageName = $ProductName + '-' + $ProductSemanticVersion
|
|
|
|
# Appends Architecture to the package name
|
|
if ($ProductNameSuffix) {
|
|
$packageName += "-$ProductNameSuffix"
|
|
}
|
|
|
|
$ProductVersion = Get-WindowsVersion -PackageName $packageName
|
|
|
|
# Any app that is submitted to the Store must have a PhoneIdentity in its appxmanifest.
|
|
# If you submit a package without this information to the Store, the Store will silently modify your package to include it.
|
|
# To find the PhoneProductId value, you need to run a package through the Store certification process,
|
|
# and use the PhoneProductId value from the Store certified package to update the manifest in your source code.
|
|
# This is the PhoneProductId for the "Microsoft.PowerShell" package.
|
|
$PhoneProductId = "5b3ae196-2df7-446e-8060-94b4ad878387"
|
|
|
|
$isPreview = Test-IsPreview -Version $ProductSemanticVersion
|
|
if ($isPreview) {
|
|
# This is the PhoneProductId for the "Microsoft.PowerShellPreview" package.
|
|
$PhoneProductId = "67859fd2-b02a-45be-8fb5-62c569a3e8bf"
|
|
Write-Verbose "Using Preview assets" -Verbose
|
|
} elseif ($LTS) {
|
|
# This is the PhoneProductId for the "Microsoft.PowerShell-LTS" package.
|
|
$PhoneProductId = "b7a4b003-3704-47a9-b018-cfcc9801f4fc"
|
|
Write-Verbose "Using LTS assets" -Verbose
|
|
}
|
|
|
|
# Appx manifest needs to be in root of source path, but the embedded version needs to be updated.
|
|
# authenticodeFormer is 'CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US'
|
|
$releasePublisher = 'CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US'
|
|
|
|
$appxManifest = Get-Content "$RepoRoot\assets\AppxManifest.xml" -Raw
|
|
$appxManifest = $appxManifest.
|
|
Replace('$VERSION$', $ProductVersion).
|
|
Replace('$ARCH$', $Architecture).
|
|
Replace('$PRODUCTNAME$', $productName).
|
|
Replace('$DISPLAYNAME$', $displayName).
|
|
Replace('$PUBLISHER$', $releasePublisher).
|
|
Replace('$PHONEPRODUCTID$', $PhoneProductId)
|
|
|
|
$xml = [xml]$appxManifest
|
|
if ($isPreview) {
|
|
Write-Verbose -Verbose "Adding pwsh-preview.exe alias"
|
|
$aliasNode = $xml.Package.Applications.Application.Extensions.Extension.AppExecutionAlias.ExecutionAlias.Clone()
|
|
$aliasNode.alias = "pwsh-preview.exe"
|
|
$xml.Package.Applications.Application.Extensions.Extension.AppExecutionAlias.AppendChild($aliasNode) | Out-Null
|
|
}
|
|
$xml.Save("$ProductSourcePath\AppxManifest.xml")
|
|
|
|
# Necessary image assets need to be in source assets folder
|
|
$assets = @(
|
|
'Square150x150Logo'
|
|
'Square44x44Logo'
|
|
'Square44x44Logo.targetsize-48'
|
|
'Square44x44Logo.targetsize-48_altform-unplated'
|
|
'StoreLogo'
|
|
)
|
|
|
|
if (!(Test-Path "$ProductSourcePath\assets")) {
|
|
$null = New-Item -ItemType Directory -Path "$ProductSourcePath\assets"
|
|
}
|
|
|
|
$assets | ForEach-Object {
|
|
if ($isPreview) {
|
|
Copy-Item -Path "$RepoRoot\assets\$_-Preview.png" -Destination "$ProductSourcePath\assets\$_.png"
|
|
}
|
|
else {
|
|
Copy-Item -Path "$RepoRoot\assets\$_.png" -Destination "$ProductSourcePath\assets\"
|
|
}
|
|
}
|
|
|
|
if ($PSCmdlet.ShouldProcess("Create .msix package?")) {
|
|
Write-Verbose "Creating priconfig.xml" -Verbose
|
|
Start-NativeExecution -VerboseOutputOnError { & $makepri createconfig /o /cf (Join-Path $ProductSourcePath "priconfig.xml") /dq en-US }
|
|
Write-Verbose "Creating resources.pri" -Verbose
|
|
Push-Location $ProductSourcePath
|
|
Start-NativeExecution -VerboseOutputOnError { & $makepri new /v /o /pr $ProductSourcePath /cf (Join-Path $ProductSourcePath "priconfig.xml") }
|
|
Pop-Location
|
|
Write-Verbose "Creating msix package" -Verbose
|
|
Start-NativeExecution -VerboseOutputOnError { & $makeappx pack /o /v /h SHA256 /d $ProductSourcePath /p (Join-Path -Path $CurrentLocation -ChildPath "$packageName.msix") }
|
|
Write-Verbose "Created $packageName.msix" -Verbose
|
|
Join-Path -Path $CurrentLocation -ChildPath "$packageName.msix"
|
|
}
|
|
}
|
|
|
|
# Removes a ComponentRef node in the files.wxs Xml Doc
|
|
function Remove-ComponentRefNode
|
|
{
|
|
param(
|
|
[Parameter(Mandatory)]
|
|
[System.Xml.XmlDocument]
|
|
$XmlDoc,
|
|
[Parameter(Mandatory)]
|
|
[System.Xml.XmlNamespaceManager]
|
|
$XmlNsManager,
|
|
[Parameter(Mandatory)]
|
|
[string]
|
|
$Id
|
|
)
|
|
|
|
$compRefXPath = '//Wix:ComponentRef[@Id="{0}"]' -f $Id
|
|
$node = Get-XmlNodeByXPath -XmlDoc $XmlDoc -XmlNsManager $XmlNsManager -XPath $compRefXPath
|
|
if ($node)
|
|
{
|
|
Remove-XmlElement -element $node
|
|
}
|
|
else
|
|
{
|
|
Write-Warning "could not remove node"
|
|
}
|
|
}
|
|
|
|
# Get the ComponentGroup node in the files.wxs Xml Doc
|
|
function Get-ComponentGroupNode
|
|
{
|
|
param(
|
|
[Parameter(Mandatory)]
|
|
[System.Xml.XmlDocument]
|
|
$XmlDoc,
|
|
[Parameter(Mandatory)]
|
|
[System.Xml.XmlNamespaceManager]
|
|
$XmlNsManager
|
|
)
|
|
|
|
if (!$XmlNsManager.HasNamespace('Wix'))
|
|
{
|
|
throw 'Namespace manager must have "wix" defined.'
|
|
}
|
|
|
|
$compGroupXPath = '//Wix:ComponentGroup'
|
|
$node = Get-XmlNodeByXPath -XmlDoc $XmlDoc -XmlNsManager $XmlNsManager -XPath $compGroupXPath
|
|
return $node
|
|
}
|
|
|
|
# Gets the Directory Node the files.wxs Xml Doc
|
|
# Creates it if it does not exist
|
|
function Get-DirectoryNode
|
|
{
|
|
param(
|
|
[Parameter(Mandatory)]
|
|
[System.Xml.XmlElement]
|
|
$Node,
|
|
[Parameter(Mandatory)]
|
|
[System.Xml.XmlDocument]
|
|
$XmlDoc,
|
|
[Parameter(Mandatory)]
|
|
[System.Xml.XmlNamespaceManager]
|
|
$XmlNsManager
|
|
)
|
|
|
|
if (!$XmlNsManager.HasNamespace('Wix'))
|
|
{
|
|
throw 'Namespace manager must have "wix" defined.'
|
|
}
|
|
|
|
$pathStack = [System.Collections.Stack]::new()
|
|
|
|
[System.Xml.XmlElement] $dirNode = $Node.ParentNode.ParentNode
|
|
$dirNodeType = $dirNode.LocalName
|
|
if ($dirNodeType -eq 'DirectoryRef')
|
|
{
|
|
return Get-XmlNodeByXPath -XmlDoc $XmlDoc -XmlNsManager $XmlNsManager -XPath "//Wix:DirectoryRef"
|
|
}
|
|
if ($dirNodeType -eq 'Directory')
|
|
{
|
|
while($dirNode.LocalName -eq 'Directory') {
|
|
$pathStack.Push($dirNode.Name)
|
|
$dirNode = $dirNode.ParentNode
|
|
}
|
|
$path = "//"
|
|
[System.Xml.XmlElement] $lastNode = $null
|
|
while($pathStack.Count -gt 0){
|
|
$dirName = $pathStack.Pop()
|
|
$path += 'Wix:Directory[@Name="{0}"]' -f $dirName
|
|
$node = Get-XmlNodeByXPath -XmlDoc $XmlDoc -XmlNsManager $XmlNsManager -XPath $path
|
|
|
|
if (!$node)
|
|
{
|
|
if (!$lastNode)
|
|
{
|
|
# Inserting at the root
|
|
$lastNode = Get-XmlNodeByXPath -XmlDoc $XmlDoc -XmlNsManager $XmlNsManager -XPath "//Wix:DirectoryRef"
|
|
}
|
|
|
|
$newDirectory = New-XmlElement -XmlDoc $XmlDoc -LocalName 'Directory' -Node $lastNode -PassThru -NamespaceUri 'http://schemas.microsoft.com/wix/2006/wi'
|
|
New-XmlAttribute -XmlDoc $XmlDoc -Element $newDirectory -Name 'Name' -Value $dirName
|
|
New-XmlAttribute -XmlDoc $XmlDoc -Element $newDirectory -Name 'Id' -Value (New-WixId -Prefix 'dir')
|
|
$lastNode = $newDirectory
|
|
}
|
|
else
|
|
{
|
|
$lastNode = $node
|
|
}
|
|
if ($pathStack.Count -gt 0)
|
|
{
|
|
$path += '/'
|
|
}
|
|
}
|
|
return $lastNode
|
|
}
|
|
|
|
throw "unknown element type: $dirNodeType"
|
|
}
|
|
|
|
# Creates a new Wix Id in the proper format
|
|
function New-WixId
|
|
{
|
|
param(
|
|
[Parameter(Mandatory)]
|
|
[string]
|
|
$Prefix
|
|
)
|
|
|
|
$guidPortion = (New-Guid).Guid.ToUpperInvariant() -replace '\-' ,''
|
|
"$Prefix$guidPortion"
|
|
}
|
|
|
|
function Get-WindowsVersion {
|
|
param (
|
|
[parameter(Mandatory)]
|
|
[string]$PackageName
|
|
)
|
|
|
|
$ProductVersion = Get-PackageVersionAsMajorMinorBuildRevision -Version $ProductVersion
|
|
if (([Version]$ProductVersion).Revision -eq -1) {
|
|
$ProductVersion += ".0"
|
|
}
|
|
|
|
# The Store requires the last digit of the version to be 0 so we swap the build and revision
|
|
# This only affects Preview versions where the last digit is the preview number
|
|
# For stable versions, the last digit is already zero so no changes
|
|
$pversion = [version]$ProductVersion
|
|
if ($pversion.Revision -ne 0) {
|
|
$revision = $pversion.Revision
|
|
if ($packageName.Contains('-rc')) {
|
|
# For Release Candidates, we use numbers in the 100 range
|
|
$revision += 100
|
|
}
|
|
|
|
$pversion = [version]::new($pversion.Major, $pversion.Minor, $revision, 0)
|
|
$ProductVersion = $pversion.ToString()
|
|
}
|
|
|
|
Write-Verbose "Version: $productversion" -Verbose
|
|
return $productversion
|
|
}
|
|
|
|
# Builds coming out of this project can have version number as 'a.b.c' OR 'a.b.c-d-f'
|
|
# This function converts the above version into major.minor[.build[.revision]] format
|
|
function Get-PackageVersionAsMajorMinorBuildRevision
|
|
{
|
|
[CmdletBinding()]
|
|
param (
|
|
# Version of the Package
|
|
[Parameter(Mandatory = $true)]
|
|
[ValidateNotNullOrEmpty()]
|
|
[string] $Version,
|
|
[switch] $IncrementBuildNumber
|
|
)
|
|
|
|
Write-Verbose "Extract the version in the form of major.minor[.build[.revision]] for $Version"
|
|
$packageVersionTokens = $Version.Split('-')
|
|
$packageVersion = ([regex]::matches($Version, "\d+(\.\d+)+"))[0].value
|
|
|
|
if (1 -eq $packageVersionTokens.Count -and ([Version]$packageVersion).Revision -eq -1) {
|
|
# In case the input is of the form a.b.c, add a '0' at the end for revision field
|
|
$packageVersion = $packageVersion + '.0'
|
|
} elseif (1 -lt $packageVersionTokens.Count) {
|
|
# We have all the four fields
|
|
$packageBuildTokens = ([regex]::Matches($packageVersionTokens[1], "\d+"))[0].value
|
|
|
|
if ($packageBuildTokens)
|
|
{
|
|
if($packageBuildTokens.length -gt 4)
|
|
{
|
|
# MSIX will fail if it is more characters
|
|
$packageBuildTokens = $packageBuildTokens.Substring(0,4)
|
|
}
|
|
|
|
if ($packageVersionTokens[1] -match 'rc' -and $IncrementBuildNumber) {
|
|
$packageBuildTokens = [int]$packageBuildTokens + 100
|
|
}
|
|
|
|
$packageVersion = $packageVersion + '.' + $packageBuildTokens
|
|
}
|
|
else
|
|
{
|
|
$packageVersion = $packageVersion
|
|
}
|
|
}
|
|
|
|
$packageVersion
|
|
}
|
|
|
|
<#
|
|
.SYNOPSIS
|
|
Create a smaller framework dependent package based off fxdependent package for dotnet-sdk container images.
|
|
|
|
.PARAMETER Path
|
|
Path to the folder containing the fxdependent package.
|
|
|
|
.PARAMETER KeepWindowsRuntimes
|
|
Specify this switch if the Windows runtimes are to be kept.
|
|
#>
|
|
function ReduceFxDependentPackage
|
|
{
|
|
[CmdletBinding()]
|
|
param(
|
|
[Parameter(Mandatory)] [string] $Path,
|
|
[switch] $KeepWindowsRuntimes
|
|
)
|
|
|
|
if (-not (Test-Path $path))
|
|
{
|
|
throw "Path not found: $Path"
|
|
}
|
|
|
|
## Remove unnecessary files
|
|
$localeFolderToRemove = 'cs', 'de', 'es', 'fr', 'it', 'ja', 'ko', 'pl', 'pt-BR', 'ru', 'tr', 'zh-Hans', 'zh-Hant'
|
|
Get-ChildItem $Path -Recurse -Directory | Where-Object { $_.Name -in $localeFolderToRemove } | ForEach-Object { Remove-Item $_.FullName -Force -Recurse -Verbose }
|
|
|
|
Write-Log -message "Starting to cleanup runtime folders"
|
|
|
|
$runtimeFolder = Get-ChildItem $Path -Recurse -Directory -Filter 'runtimes'
|
|
|
|
$runtimeFolderPath = $runtimeFolder | Out-String
|
|
|
|
if ($runtimeFolder.Count -eq 0)
|
|
{
|
|
Write-Log -message "No runtimes folder found under $Path. Completing cleanup."
|
|
return
|
|
}
|
|
|
|
Write-Log -message $runtimeFolderPath
|
|
|
|
if ($runtimeFolder.Count -eq 0)
|
|
{
|
|
throw "runtimes folder not found under $Path"
|
|
}
|
|
|
|
Write-Log -message (Get-ChildItem $Path | Out-String)
|
|
|
|
# donet SDK container image microsoft/dotnet:2.2-sdk supports the following:
|
|
# win10-x64 (Nano Server)
|
|
# win-arm (Nano Server)
|
|
# win-x64 to get PowerShell.Native components
|
|
# linux-musl-x64 (Alpine 3.8)
|
|
# linux-x64 (bionic / stretch)
|
|
# unix, linux, win for dependencies
|
|
# linux-arm and linux-arm64 for arm containers
|
|
# osx to run global tool on macOS
|
|
$runtimesToKeep = if ($KeepWindowsRuntimes) {
|
|
'win10-x64', 'win-arm', 'win-x64', 'win'
|
|
} else {
|
|
'linux-x64', 'linux-musl-x64', 'unix', 'linux', 'linux-arm', 'linux-arm64', 'osx'
|
|
}
|
|
|
|
$runtimeFolder | ForEach-Object {
|
|
Get-ChildItem -Path $_.FullName -Directory -Exclude $runtimesToKeep | Remove-Item -Force -Recurse -Verbose
|
|
}
|
|
|
|
## Remove the shim layer assemblies
|
|
Get-ChildItem -Path $Path -Filter "Microsoft.PowerShell.GlobalTool.Shim.*" | Remove-Item -Verbose
|
|
}
|
|
|
|
<#
|
|
.SYNOPSIS
|
|
Performs clean up work for preparation to running New-GlobalToolNupkgSource package source creation.
|
|
|
|
.DESCRIPTION
|
|
Unnecessary package source files are removed.
|
|
|
|
.PARAMETER LinuxBinPath
|
|
Path to the folder containing the fxdependent package for Linux.
|
|
|
|
.PARAMETER WindowsBinPath
|
|
Path to the folder containing the fxdependent package for Windows.
|
|
|
|
.PARAMETER WindowsDesktopBinPath
|
|
Path to the folder containing desktop framework package for Windows.
|
|
#>
|
|
function Start-PrepForGlobalToolNupkg
|
|
{
|
|
[CmdletBinding()]
|
|
param(
|
|
[Parameter(Mandatory)] [string] $LinuxBinPath,
|
|
[Parameter(Mandatory)] [string] $WindowsBinPath,
|
|
[Parameter(Mandatory)] [string] $WindowsDesktopBinPath,
|
|
[Parameter(Mandatory)] [string] $AlpineBinPath
|
|
)
|
|
|
|
Write-Log "Start-PrepForGlobalToolNupkg: Running clean up for New-GlobalToolNupkg package creation."
|
|
|
|
$libCryptoPath = Join-Path $LinuxBinPath 'libcrypto.so.1.0.0'
|
|
$libSSLPath = Join-Path $LinuxBinPath 'libssl.so.1.0.0'
|
|
|
|
if (Test-Path $libCryptoPath) {
|
|
Remove-Item -Path $libCryptoPath -Verbose -Force
|
|
}
|
|
|
|
if (Test-Path $libSSLPath) {
|
|
Remove-Item -Path $libSSLPath -Verbose -Force
|
|
}
|
|
|
|
# Remove unnecessary xml files
|
|
Get-ChildItem -Path $LinuxBinPath, $WindowsBinPath, $WindowsDesktopBinPath, $AlpineBinPath -Filter *.xml | Remove-Item -Verbose
|
|
}
|
|
|
|
<#
|
|
.SYNOPSIS
|
|
Create a single PowerShell Global tool nuget package NuSpec source directory for the provied
|
|
package type.
|
|
|
|
.DESCRIPTION
|
|
A single NuSpec source directory is created for the individual package type, and the created
|
|
directory path is set to the environement variable name: "GlobaToolNuSpecSourcePath_${PackageType}".
|
|
|
|
.PARAMETER PackageType
|
|
Global tool package type to create.
|
|
|
|
.PARAMETER LinuxBinPath
|
|
Path to the folder containing the fxdependent package for Linux.
|
|
|
|
.PARAMETER WindowsBinPath
|
|
Path to the folder containing the fxdependent package for Windows.
|
|
|
|
.PARAMETER WindowsDesktopBinPath
|
|
Path to the folder containing desktop framework package for Windows.
|
|
|
|
.PARAMETER PackageVersion
|
|
Version for the NuGet package that will be generated.
|
|
#>
|
|
function New-GlobalToolNupkgSource
|
|
{
|
|
[CmdletBinding()]
|
|
param(
|
|
[Parameter(Mandatory)] [string] $PackageType,
|
|
[Parameter(Mandatory)] [string] $LinuxBinPath,
|
|
[Parameter(Mandatory)] [string] $WindowsBinPath,
|
|
[Parameter(Mandatory)] [string] $WindowsDesktopBinPath,
|
|
[Parameter(Mandatory)] [string] $AlpineBinPath,
|
|
[Parameter(Mandatory)] [string] $PackageVersion,
|
|
[Parameter()] [switch] $SkipCGManifest
|
|
)
|
|
|
|
if ($PackageType -ne "Unified")
|
|
{
|
|
Write-Log "New-GlobalToolNupkgSource: Reducing package size for non-unified packages."
|
|
|
|
Write-Log "New-GlobalToolNupkgSource: Reducing size of Linux package"
|
|
ReduceFxDependentPackage -Path $LinuxBinPath
|
|
|
|
Write-Log "New-GlobalToolNupkgSource: Reducing size of Alpine package"
|
|
ReduceFxDependentPackage -Path $AlpineBinPath
|
|
|
|
Write-Log "New-GlobalToolNupkgSource: Reducing size of Windows package"
|
|
ReduceFxDependentPackage -Path $WindowsBinPath -KeepWindowsRuntimes
|
|
|
|
Write-Log "New-GlobalToolNupkgSource: Reducing size of WindowsDesktop package"
|
|
ReduceFxDependentPackage -Path $WindowsDesktopBinPath -KeepWindowsRuntimes
|
|
}
|
|
|
|
Write-Log "New-GlobalToolNupkgSource: Creating package: $PackageType"
|
|
|
|
switch ($PackageType)
|
|
{
|
|
"Unified"
|
|
{
|
|
$ShimDllPath = Join-Path $WindowsDesktopBinPath "Microsoft.PowerShell.GlobalTool.Shim.dll"
|
|
|
|
$PackageName = "PowerShell"
|
|
$RootFolder = New-TempFolder
|
|
|
|
Copy-Item -Path $iconPath -Destination "$RootFolder/$iconFileName" -Verbose
|
|
|
|
$ridFolder = New-Item -Path (Join-Path $RootFolder "tools/$script:netCoreRuntime/any") -ItemType Directory
|
|
$winFolder = New-Item (Join-Path $ridFolder "win") -ItemType Directory
|
|
$unixFolder = New-Item (Join-Path $ridFolder "unix") -ItemType Directory
|
|
|
|
Write-Log "New-GlobalToolNupkgSource: Copying runtime assemblies from $WindowsDesktopBinPath"
|
|
Copy-Item "$WindowsDesktopBinPath\*" -Destination $winFolder -Recurse
|
|
|
|
Write-Log "New-GlobalToolNupkgSource: Copying runtime assemblies from $LinuxBinPath"
|
|
Copy-Item "$LinuxBinPath\*" -Destination $unixFolder -Recurse
|
|
|
|
Write-Log "New-GlobalToolNupkgSource: Copying shim dll from $ShimDllPath"
|
|
Copy-Item $ShimDllPath -Destination $ridFolder
|
|
|
|
$shimConfigFile = Join-Path (Split-Path $ShimDllPath -Parent) 'Microsoft.PowerShell.GlobalTool.Shim.runtimeconfig.json'
|
|
Write-Log "New-GlobalToolNupkgSource: Copying shim config file from $shimConfigFile"
|
|
Copy-Item $shimConfigFile -Destination $ridFolder -ErrorAction Stop
|
|
|
|
$toolSettings = $packagingStrings.GlobalToolSettingsFile -f (Split-Path $ShimDllPath -Leaf)
|
|
}
|
|
|
|
"PowerShell.Linux.Alpine"
|
|
{
|
|
$PackageName = "PowerShell.Linux.Alpine"
|
|
$RootFolder = New-TempFolder
|
|
|
|
Copy-Item -Path $iconPath -Destination "$RootFolder/$iconFileName" -Verbose
|
|
|
|
$ridFolder = New-Item -Path (Join-Path $RootFolder "tools/$script:netCoreRuntime/any") -ItemType Directory
|
|
|
|
Write-Log "New-GlobalToolNupkgSource: Copying runtime assemblies from $AlpineBinPath for $PackageType"
|
|
Copy-Item "$AlpineBinPath/*" -Destination $ridFolder -Recurse
|
|
$toolSettings = $packagingStrings.GlobalToolSettingsFile -f "pwsh.dll"
|
|
}
|
|
|
|
"PowerShell.Linux.x64"
|
|
{
|
|
$PackageName = "PowerShell.Linux.x64"
|
|
$RootFolder = New-TempFolder
|
|
|
|
Copy-Item -Path $iconPath -Destination "$RootFolder/$iconFileName" -Verbose
|
|
|
|
$ridFolder = New-Item -Path (Join-Path $RootFolder "tools/$script:netCoreRuntime/any") -ItemType Directory
|
|
|
|
Write-Log "New-GlobalToolNupkgSource: Copying runtime assemblies from $LinuxBinPath for $PackageType"
|
|
Copy-Item "$LinuxBinPath/*" -Destination $ridFolder -Recurse
|
|
Remove-Item -Path $ridFolder/runtimes/linux-arm -Recurse -Force
|
|
Remove-Item -Path $ridFolder/runtimes/linux-arm64 -Recurse -Force
|
|
Remove-Item -Path $ridFolder/runtimes/linux-musl-x64 -Recurse -Force
|
|
Remove-Item -Path $ridFolder/runtimes/osx -Recurse -Force
|
|
$toolSettings = $packagingStrings.GlobalToolSettingsFile -f "pwsh.dll"
|
|
}
|
|
|
|
"PowerShell.Linux.arm32"
|
|
{
|
|
$PackageName = "PowerShell.Linux.arm32"
|
|
$RootFolder = New-TempFolder
|
|
|
|
Copy-Item -Path $iconPath -Destination "$RootFolder/$iconFileName" -Verbose
|
|
|
|
$ridFolder = New-Item -Path (Join-Path $RootFolder "tools/$script:netCoreRuntime/any") -ItemType Directory
|
|
|
|
Write-Log "New-GlobalToolNupkgSource: Copying runtime assemblies from $LinuxBinPath for $PackageType"
|
|
Copy-Item "$LinuxBinPath/*" -Destination $ridFolder -Recurse
|
|
Remove-Item -Path $ridFolder/runtimes/linux-arm64 -Recurse -Force
|
|
Remove-Item -Path $ridFolder/runtimes/linux-musl-x64 -Recurse -Force
|
|
Remove-Item -Path $ridFolder/runtimes/linux-x64 -Recurse -Force
|
|
Remove-Item -Path $ridFolder/runtimes/osx -Recurse -Force
|
|
$toolSettings = $packagingStrings.GlobalToolSettingsFile -f "pwsh.dll"
|
|
}
|
|
|
|
"PowerShell.Linux.arm64"
|
|
{
|
|
$PackageName = "PowerShell.Linux.arm64"
|
|
$RootFolder = New-TempFolder
|
|
|
|
Copy-Item -Path $iconPath -Destination "$RootFolder/$iconFileName" -Verbose
|
|
|
|
$ridFolder = New-Item -Path (Join-Path $RootFolder "tools/$script:netCoreRuntime/any") -ItemType Directory
|
|
|
|
Write-Log "New-GlobalToolNupkgSource: Copying runtime assemblies from $LinuxBinPath for $PackageType"
|
|
Copy-Item "$LinuxBinPath/*" -Destination $ridFolder -Recurse
|
|
Remove-Item -Path $ridFolder/runtimes/linux-arm -Recurse -Force
|
|
Remove-Item -Path $ridFolder/runtimes/linux-musl-x64 -Recurse -Force
|
|
Remove-Item -Path $ridFolder/runtimes/linux-x64 -Recurse -Force
|
|
Remove-Item -Path $ridFolder/runtimes/osx -Recurse -Force
|
|
$toolSettings = $packagingStrings.GlobalToolSettingsFile -f "pwsh.dll"
|
|
}
|
|
|
|
# Due to needing a signed shim for the global tool, we build the global tool in build instead of packaging.
|
|
# keeping the code for reference.
|
|
# "PowerShell.Windows.x64"
|
|
# {
|
|
# $PackageName = "PowerShell.Windows.x64"
|
|
# $RootFolder = New-TempFolder
|
|
|
|
# Copy-Item -Path $iconPath -Destination "$RootFolder/$iconFileName" -Verbose
|
|
|
|
# $ridFolder = New-Item -Path (Join-Path $RootFolder "tools/$script:netCoreRuntime/any") -ItemType Directory
|
|
|
|
# Write-Log "New-GlobalToolNupkgSource: Copying runtime assemblies from $WindowsDesktopBinPath for $PackageType"
|
|
# Copy-Item "$WindowsDesktopBinPath/*" -Destination $ridFolder -Recurse
|
|
# Remove-Item -Path $ridFolder/runtimes/win-arm -Recurse -Force
|
|
# $toolSettings = $packagingStrings.GlobalToolSettingsFile -f "pwsh.dll"
|
|
# }
|
|
|
|
"PowerShell.Windows.arm32"
|
|
{
|
|
$PackageName = "PowerShell.Windows.arm32"
|
|
$RootFolder = New-TempFolder
|
|
|
|
Copy-Item -Path $iconPath -Destination "$RootFolder/$iconFileName" -Verbose
|
|
|
|
$ridFolder = New-Item -Path (Join-Path $RootFolder "tools/$script:netCoreRuntime/any") -ItemType Directory
|
|
|
|
Write-Log "New-GlobalToolNupkgSource: Copying runtime assemblies from $WindowsBinPath for $PackageType"
|
|
Copy-Item "$WindowsBinPath/*" -Destination $ridFolder -Recurse
|
|
Remove-Item -Path $ridFolder/runtimes/win-x64 -Recurse -Force
|
|
$toolSettings = $packagingStrings.GlobalToolSettingsFile -f "pwsh.dll"
|
|
}
|
|
|
|
default { throw "New-GlobalToolNupkgSource: Unknown package type: $PackageType" }
|
|
}
|
|
|
|
$nuSpec = $packagingStrings.GlobalToolNuSpec -f $PackageName, $PackageVersion, $iconFileName
|
|
$nuSpec | Out-File -FilePath (Join-Path $RootFolder "$PackageName.nuspec") -Encoding ascii
|
|
$toolSettings | Out-File -FilePath (Join-Path $ridFolder "DotnetToolSettings.xml") -Encoding ascii
|
|
|
|
# Source created.
|
|
Write-Log "New-GlobalToolNupkgSource: Global tool package ($PackageName) source created at: $RootFolder"
|
|
|
|
# Set VSTS environment variable for package NuSpec source path.
|
|
$pkgNuSpecSourcePathVar = "GlobalToolNuSpecSourcePath"
|
|
Write-Log "New-GlobalToolNupkgSource: Creating NuSpec source path VSTS variable: $pkgNuSpecSourcePathVar"
|
|
Write-Verbose -Verbose "sending: [task.setvariable variable=$pkgNuSpecSourcePathVar]$RootFolder"
|
|
Write-Host "##vso[task.setvariable variable=$pkgNuSpecSourcePathVar]$RootFolder"
|
|
$global:GlobalToolNuSpecSourcePath = $RootFolder
|
|
|
|
# Set VSTS environment variable for package Name.
|
|
$pkgNameVar = "GlobalToolPkgName"
|
|
Write-Log "New-GlobalToolNupkgSource: Creating current package name variable: $pkgNameVar"
|
|
Write-Verbose -Verbose "sending: vso[task.setvariable variable=$pkgNameVar]$PackageName"
|
|
Write-Host "##vso[task.setvariable variable=$pkgNameVar]$PackageName"
|
|
$global:GlobalToolPkgName = $PackageName
|
|
|
|
if ($SkipCGManifest.IsPresent) {
|
|
Write-Verbose -Verbose "New-GlobalToolNupkgSource: Skipping CGManifest creation."
|
|
return
|
|
}
|
|
|
|
# Set VSTS environment variable for CGManifest file path.
|
|
$globalToolCGManifestPFilePath = Join-Path -Path "$env:REPOROOT" -ChildPath "tools/cgmanifest/main/cgmanifest.json"
|
|
$globalToolCGManifestFilePath = Resolve-Path -Path $globalToolCGManifestPFilePath -ErrorAction SilentlyContinue
|
|
if (($null -eq $globalToolCGManifestFilePath) -or (! (Test-Path -Path $globalToolCGManifestFilePath)))
|
|
{
|
|
throw "New-GlobalToolNupkgSource: Invalid build source CGManifest file path: $globalToolCGManifestPFilePath"
|
|
}
|
|
$globalToolCGManifestSourceRoot = New-TempFolder
|
|
Write-Log "New-GlobalToolNupkgSource: Creating new CGManifest.json file at: $globalToolCGManifestSourceRoot"
|
|
Copy-Item -Path $globalToolCGManifestFilePath -Destination $globalToolCGManifestSourceRoot -Force
|
|
|
|
$globalToolCGManifestPathVar = "GlobalToolCGManifestPath"
|
|
Write-Log "New-GlobalToolNupkgSource: Creating CGManifest path variable, $globalToolCGManifestPathVar, for path: $globalToolCGManifestSourceRoot"
|
|
Write-Host "##vso[task.setvariable variable=$globalToolCGManifestPathVar]$globalToolCGManifestSourceRoot"
|
|
}
|
|
|
|
<#
|
|
.SYNOPSIS
|
|
Create a single PowerShell Global tool nuget package from the provied package source folder.
|
|
|
|
.DESCRIPTION
|
|
Creates a single PowerShell Global tool nuget package based on the provided package NuSpec source
|
|
folder (created by New-GlobalNupkgSource), and places the created package in the provided destination
|
|
folder.
|
|
|
|
.PARAMETER PackageNuSpecPath
|
|
Location of NuSpec path containing source for package creation.
|
|
|
|
.PARAMETER PackageName
|
|
Name of Global Tool package being created.
|
|
|
|
.PARAMETER DestinationPath
|
|
Path to the folder where the generated package is placed.
|
|
#>
|
|
function New-GlobalToolNupkgFromSource
|
|
{
|
|
[CmdletBinding()]
|
|
param (
|
|
[Parameter(Mandatory)] [string] $PackageNuSpecPath,
|
|
[Parameter(Mandatory)] [string] $PackageName,
|
|
[Parameter(Mandatory)] [string] $DestinationPath,
|
|
[Parameter()] [string] $CGManifestPath
|
|
)
|
|
|
|
if (! (Test-Path -Path $PackageNuSpecPath))
|
|
{
|
|
throw "New-GlobalToolNupkgFromSource: failed because NuSpec path does not exist: $PackageNuSpecPath"
|
|
}
|
|
|
|
Write-Log "New-GlobalToolNupkgFromSource: Creating package: $PackageName"
|
|
New-NugetPackage -NuSpecPath $PackageNuSpecPath -PackageDestinationPath $DestinationPath
|
|
|
|
Write-Log "New-GlobalToolNupkgFromSource: Removing GlobalTool NuSpec source directory: $PackageNuSpecPath"
|
|
Remove-Item -Path $PackageNuSpecPath -Recurse -Force -ErrorAction SilentlyContinue
|
|
|
|
if (-not ($PSBoundParameters.ContainsKey('CGManifestPath')))
|
|
{
|
|
Write-Verbose -Verbose "New-GlobalToolNupkgFromSource: CGManifest file path not provided."
|
|
return
|
|
}
|
|
|
|
Write-Log "New-GlobalToolNupkgFromSource: Removing GlobalTool CGManifest source directory: $CGManifestPath"
|
|
if (! (Test-Path -Path $CGManifestPath))
|
|
{
|
|
Write-Verbose -Verbose -Message "New-GlobalToolNupkgFromSource: CGManifest file does not exist: $CGManifestPath"
|
|
return
|
|
}
|
|
Remove-Item -Path $CGManifestPath -Recurse -Force -ErrorAction SilentlyContinue
|
|
}
|
|
|
|
<#
|
|
Apply the file permissions specified in the json file $FilePath to the files under $RootPath.
|
|
The format of the json file is like:
|
|
|
|
{
|
|
"System.Net.WebClient.dll": "744",
|
|
"Schemas/PSMaml/developer.xsd": "644",
|
|
"ref/System.Security.AccessControl.dll": "744",
|
|
"ref/System.IO.dll": "744",
|
|
"cs/Microsoft.CodeAnalysis.resources.dll": "744",
|
|
"Schemas/PSMaml/base.xsd": "644",
|
|
"Schemas/PSMaml/structureProcedure.xsd": "644",
|
|
"ref/System.Net.Security.dll": "744"
|
|
}
|
|
#>
|
|
function Set-LinuxFilePermission {
|
|
[CmdletBinding()]
|
|
param (
|
|
[Parameter(Mandatory)] [string] $FilePath,
|
|
[Parameter(Mandatory)] [string] $RootPath
|
|
)
|
|
|
|
if (-not (Test-Path $FilePath)) {
|
|
throw "File does not exist: $FilePath"
|
|
}
|
|
|
|
if (-not (Test-Path $RootPath)) {
|
|
throw "File does not exist: $RootPath"
|
|
}
|
|
|
|
try {
|
|
Push-Location $RootPath
|
|
$filePermission = Get-Content $FilePath -Raw | ConvertFrom-Json -AsHashtable
|
|
|
|
Write-Verbose -Verbose -Message "Got file permission: $($filePermission.Count) for $FilePath"
|
|
|
|
$filePermission.GetEnumerator() | ForEach-Object {
|
|
$file = $_.Name
|
|
$permission = $_.Value
|
|
$fileFullName = Join-Path -Path $RootPath -ChildPath $file
|
|
Write-Verbose "Set permission $permission to $fileFullName" -Verbose
|
|
chmod $permission $fileFullName
|
|
}
|
|
}
|
|
finally {
|
|
Pop-Location
|
|
}
|
|
}
|
|
|
|
<#
|
|
Store the linux file permissions for all the files under root path $RootPath to the json file $FilePath.
|
|
The json file stores them as relative paths to the root.
|
|
|
|
The format of the json file is like:
|
|
|
|
{
|
|
"System.Net.WebClient.dll": "744",
|
|
"Schemas/PSMaml/developer.xsd": "644",
|
|
"ref/System.Security.AccessControl.dll": "744",
|
|
"ref/System.IO.dll": "744",
|
|
"cs/Microsoft.CodeAnalysis.resources.dll": "744",
|
|
"Schemas/PSMaml/base.xsd": "644",
|
|
"Schemas/PSMaml/structureProcedure.xsd": "644",
|
|
"ref/System.Net.Security.dll": "744"
|
|
}
|
|
|
|
#>
|
|
function Export-LinuxFilePermission {
|
|
[CmdletBinding()]
|
|
param (
|
|
[Parameter(Mandatory)] [string] $FilePath,
|
|
[Parameter(Mandatory)] [string] $RootPath,
|
|
[Parameter(Mandatory, ValueFromPipeline = $true)] [System.IO.FileInfo[]] $InputObject,
|
|
[Parameter()] [switch] $Force
|
|
)
|
|
|
|
begin {
|
|
if (Test-Path $FilePath) {
|
|
if (-not $Force) {
|
|
throw "File '$FilePath' already exists."
|
|
}
|
|
else {
|
|
Remove-Item $FilePath -Force
|
|
}
|
|
}
|
|
|
|
$fileData = @{}
|
|
}
|
|
|
|
process {
|
|
foreach ($object in $InputObject) {
|
|
Write-Verbose "Processing $($object.FullName)"
|
|
# This gets the unix stat information for the file in the format that chmod expects, like '644'.
|
|
$filePerms = [convert]::ToString($object.unixstat.mode, 8).substring(3)
|
|
$relativePath = [System.IO.Path]::GetRelativePath($RootPath, $_.FullName)
|
|
$fileData.Add($relativePath, $filePerms)
|
|
}
|
|
}
|
|
|
|
end {
|
|
$fileData | ConvertTo-Json -Depth 10 | Out-File -FilePath $FilePath
|
|
}
|
|
}
|
|
|
|
enum PackageManifestResultStatus {
|
|
Mismatch
|
|
Match
|
|
MissingFromManifest
|
|
MissingFromPackage
|
|
}
|
|
|
|
class PackageManifestResult {
|
|
[string] $File
|
|
[string] $ExpectedHash
|
|
[string] $ActualHash
|
|
[PackageManifestResultStatus] $Status
|
|
}
|
|
|
|
function Test-PackageManifest {
|
|
param (
|
|
[Parameter(Mandatory)]
|
|
[string]
|
|
$PackagePath
|
|
)
|
|
|
|
Begin {
|
|
$spdxManifestPath = Join-Path $PackagePath -ChildPath "/_manifest/spdx_2.2/manifest.spdx.json"
|
|
$man = Get-Content $spdxManifestPath -ErrorAction Stop | convertfrom-json
|
|
$inManifest = @()
|
|
}
|
|
|
|
Process {
|
|
Write-Verbose "Processing $($man.files.count) files..." -verbose
|
|
$man.files | ForEach-Object {
|
|
$filePath = Join-Path $PackagePath -childPath $_.fileName
|
|
$checksumObj = $_.checksums | Where-Object {$_.algorithm -eq 'sha256'}
|
|
$sha256 = $checksumObj.checksumValue
|
|
$actualHash = $null
|
|
$actualHash = (Get-FileHash -Path $filePath -Algorithm sha256 -ErrorAction SilentlyContinue).Hash
|
|
$inManifest += $filePath
|
|
if($actualHash -ne $sha256) {
|
|
$status = [PackageManifestResultStatus]::Mismatch
|
|
if (!$actualHash) {
|
|
$status = [PackageManifestResultStatus]::MissingFromPackage
|
|
}
|
|
[PackageManifestResult] $result = @{
|
|
File = $filePath
|
|
ExpectedHash = $sha256
|
|
ActualHash = $actualHash
|
|
Status = $status
|
|
}
|
|
Write-Output $result
|
|
}
|
|
else {
|
|
[PackageManifestResult] $result = @{
|
|
File = $filePath
|
|
ExpectedHash = $sha256
|
|
ActualHash = $actualHash
|
|
Status = [PackageManifestResultStatus]::Match
|
|
}
|
|
Write-Output $result
|
|
}
|
|
}
|
|
|
|
|
|
Get-ChildItem $PackagePath -recurse | Select-Object -ExpandProperty FullName | foreach-object {
|
|
if(!$inManifest -contains $_) {
|
|
$actualHash = (get-filehash -Path $_ -algorithm sha256 -erroraction silentlycontinue).Hash
|
|
[PackageManifestResult] $result = @{
|
|
File = $_
|
|
ExpectedHash = $null
|
|
ActualHash = $actualHash
|
|
Status = [PackageManifestResultStatus]::MissingFromManifest
|
|
}
|
|
Write-Output $result
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
# Get the PE information for a file
|
|
function Get-PEInfo {
|
|
[CmdletBinding()]
|
|
param([Parameter(ValueFromPipeline = $true)][string] $File)
|
|
BEGIN {
|
|
# retrieved from ILCompiler.PEWriter.MachineOSOverride
|
|
enum MachineOSOverride {
|
|
Windows = 0
|
|
SunOS = 6546
|
|
NetBSD = 6547
|
|
Apple = 17988
|
|
Linux = 31609
|
|
FreeBSD = 44484
|
|
}
|
|
|
|
# The information we want
|
|
class PsPeInfo {
|
|
[string]$File
|
|
[bool]$CrossGen
|
|
[Nullable[MachineOSOverride]]$OS
|
|
[System.Reflection.PortableExecutable.Machine]$Architecture
|
|
[Nullable[System.Reflection.PortableExecutable.CorFlags]]$Flags
|
|
}
|
|
|
|
}
|
|
PROCESS {
|
|
$filePath = (get-item $file).fullname
|
|
$CrossGenFlag = 4
|
|
try {
|
|
$stream = [System.IO.FileStream]::new($FilePath, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read)
|
|
$peReader = [System.Reflection.PortableExecutable.PEReader]::new($stream)
|
|
$flags = $peReader.PEHeaders.CorHeader.Flags
|
|
if (-not $flags) {
|
|
Write-Warning "$filePath is not a managed assembly"
|
|
}
|
|
$machine = $peReader.PEHeaders.CoffHeader.Machine
|
|
if (-not $machine) {
|
|
throw "Null Machine"
|
|
}
|
|
} catch {
|
|
$er = [system.management.automation.errorrecord]::new(([InvalidOperationException]::new($_)), "Get-PEInfo:InvalidOperation", "InvalidOperation", $filePath)
|
|
$PSCmdlet.WriteError($er)
|
|
return
|
|
} finally {
|
|
if ($peReader) {
|
|
$peReader.Dispose()
|
|
}
|
|
}
|
|
|
|
[ushort]$r2rOsArch = $machine
|
|
|
|
$RealOS = $null
|
|
$realarch = "unknown"
|
|
foreach ($os in [enum]::GetValues([MachineOSOverride])) {
|
|
foreach ($architecture in [Enum]::GetValues([System.Reflection.PortableExecutable.Machine])) {
|
|
if (([ushort]$architecture -BXOR [ushort]$os) -eq [ushort]$r2rOsArch) {
|
|
$realOS = $os
|
|
$realArch = $architecture
|
|
|
|
[PsPeInfo]@{
|
|
File = $File
|
|
OS = $realos
|
|
Architecture = $realarch
|
|
CrossGen = [bool]($flags -band $CrossGenFlag)
|
|
Flags = $flags
|
|
}
|
|
return
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function ConvertTo-PEArchitecture {
|
|
[CmdletBinding()]
|
|
param(
|
|
[Parameter(ValueFromPipeline = $true)]
|
|
[string]
|
|
$Architecture
|
|
)
|
|
|
|
PROCESS {
|
|
switch ($Architecture) {
|
|
"x86" { "I386" }
|
|
"x64" { "AMD64" }
|
|
"arm" { "ArmThumb2" }
|
|
default { $Architecture }
|
|
}
|
|
}
|
|
}
|
|
|
|
function ConvertTo-PEOperatingSystem {
|
|
[CmdletBinding()]
|
|
param(
|
|
[Parameter(ValueFromPipeline = $true)]
|
|
[string]
|
|
$OperatingSystem
|
|
)
|
|
|
|
PROCESS {
|
|
switch -regex ($OperatingSystem) {
|
|
"win.*" { "Windows" }
|
|
"Linux" { "Linux" }
|
|
"OSX" { "Apple" }
|
|
default { $OperatingSystem }
|
|
}
|
|
}
|
|
}
|
|
|
|
# Upload an artifact in Azure DevOps
|
|
# On other systems will just log where the file was placed
|
|
function Send-AzdoFile {
|
|
param (
|
|
[parameter(Mandatory, ParameterSetName = 'contents')]
|
|
[string[]]
|
|
$Contents,
|
|
[parameter(Mandatory, ParameterSetName = 'contents')]
|
|
[string]
|
|
$LogName,
|
|
[parameter(Mandatory, ParameterSetName = 'path')]
|
|
[ValidateScript({ Test-Path -Path $_ })]
|
|
[string]
|
|
$Path
|
|
)
|
|
|
|
$logFolder = Join-Path -Path $PWD -ChildPath 'logfile'
|
|
if (!(Test-Path -Path $logFolder)) {
|
|
$null = New-Item -Path $logFolder -ItemType Directory
|
|
if ($IsMacOS -or $IsLinux) {
|
|
$null = chmod a+rw $logFolder
|
|
}
|
|
}
|
|
|
|
if ($LogName) {
|
|
$effectiveLogName = $LogName + '.txt'
|
|
} else {
|
|
$effectiveLogName = Split-Path -Leaf -Path $Path
|
|
}
|
|
|
|
$newName = ([System.Io.Path]::GetRandomFileName() + "-$effectiveLogName")
|
|
if ($Contents) {
|
|
$logFile = Join-Path -Path $logFolder -ChildPath $newName
|
|
|
|
$Contents | Out-File -path $logFile -Encoding ascii
|
|
} else {
|
|
$logFile = Join-Path -Path $logFolder -ChildPath $newName
|
|
Copy-Item -Path $Path -Destination $logFile
|
|
}
|
|
|
|
Write-Verbose "Capture the log file as '$newName'" -Verbose
|
|
if($env:TF_BUILD) {
|
|
## In Azure DevOps
|
|
Write-Host "##vso[artifact.upload containerfolder=$newName;artifactname=$newName]$logFile"
|
|
} elseif ($env:GITHUB_WORKFLOW -and $env:SYSTEM_ARTIFACTSDIRECTORY) {
|
|
## In GitHub Actions
|
|
$destinationPath = $env:SYSTEM_ARTIFACTSDIRECTORY
|
|
Write-Verbose "Upload '$logFile' to '$destinationPath' in GitHub Action" -Verbose
|
|
|
|
# Create the folder if it does not exist
|
|
if (!(Test-Path -Path $destinationPath)) {
|
|
$null = New-Item -ItemType Directory -Path $destinationPath -Force
|
|
}
|
|
|
|
Copy-Item -Path $logFile -Destination $destinationPath -Force -Verbose
|
|
} else {
|
|
Write-Warning "This environment is neither Azure Devops nor GitHub Actions. Cannot capture the log file in this environment."
|
|
}
|
|
}
|