From 76e184b0ba0fc08682bcd0d74d22c10a2c45138c Mon Sep 17 00:00:00 2001 From: Travis Plunk Date: Sun, 23 Jul 2017 12:29:33 -0700 Subject: [PATCH] move packaging CmdLets to separate module (#4319) * fix PsScriptAnalyzer issues * update release docs --- build.psm1 | 794 +++++---------------------------- docs/maintainers/releasing.md | 4 +- tools/appveyor.psm1 | 4 +- tools/packaging/packaging.psd1 | 11 + tools/packaging/packaging.psm1 | 599 +++++++++++++++++++++++++ tools/travis.ps1 | 1 + 6 files changed, 735 insertions(+), 678 deletions(-) create mode 100644 tools/packaging/packaging.psd1 create mode 100644 tools/packaging/packaging.psm1 diff --git a/build.psm1 b/build.psm1 index 22c97149ec..b5504b6351 100644 --- a/build.psm1 +++ b/build.psm1 @@ -1,64 +1,75 @@ -# Use the .NET Core APIs to determine the current platform; if a runtime -# exception is thrown, we are on FullCLR, not .NET Core. -try { - $Runtime = [System.Runtime.InteropServices.RuntimeInformation] - $OSPlatform = [System.Runtime.InteropServices.OSPlatform] - - $IsCoreCLR = $true - $IsLinux = $Runtime::IsOSPlatform($OSPlatform::Linux) - $IsOSX = $Runtime::IsOSPlatform($OSPlatform::OSX) - $IsWindows = $Runtime::IsOSPlatform($OSPlatform::Windows) -} catch { - # If these are already set, then they're read-only and we're done - try { - $IsCoreCLR = $false - $IsLinux = $false - $IsOSX = $false - $IsWindows = $true - } - catch { } -} +# On Unix paths is separated by colon +# On Windows paths is separated by semicolon +$script:TestModulePathSeparator = [System.IO.Path]::PathSeparator $dotnetCLIChannel = "preview" $dotnetCLIRequiredVersion = "2.0.0-preview2-006502" -# On Unix paths is separated by colon -# On Windows paths is separated by semicolon -$TestModulePathSeparator = ':' - -if ($IsWindows) +function Get-EnvironmentInformation { - $TestModulePathSeparator = ';' - $IsAdmin = (New-Object Security.Principal.WindowsPrincipal ([Security.Principal.WindowsIdentity]::GetCurrent())).IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator) - # Can't use $env:HOME - not available on older systems (e.g. in AppVeyor) - $nugetPackagesRoot = "${env:HOMEDRIVE}${env:HOMEPATH}\.nuget\packages" -} -else -{ - $nugetPackagesRoot = "${env:HOME}/.nuget/packages" -} + $environment = @{} + # Use the .NET Core APIs to determine the current platform + # if a runtime exception is thrown, we are on FullCLR, not .NET Core. + # because System.Runtime.InteropServices.RuntimeInformation + # and System.Runtime.InteropServices.OSPlatform + # do not exist in FullCLR + try { + $Runtime = [System.Runtime.InteropServices.RuntimeInformation] + $OSPlatform = [System.Runtime.InteropServices.OSPlatform] -if ($IsLinux) { - $LinuxInfo = Get-Content /etc/os-release -Raw | ConvertFrom-StringData - - $IsUbuntu = $LinuxInfo.ID -match 'ubuntu' - $IsUbuntu14 = $IsUbuntu -and $LinuxInfo.VERSION_ID -match '14.04' - $IsUbuntu16 = $IsUbuntu -and $LinuxInfo.VERSION_ID -match '16.04' - $IsCentOS = $LinuxInfo.ID -match 'centos' -and $LinuxInfo.VERSION_ID -match '7' - $IsFedora = $LinuxInfo.ID -match 'fedora' -and $LinuxInfo.VERSION_ID -ge 24 - $IsOpenSUSE = $LinuxInfo.ID -match 'opensuse' - $IsOpenSUSE13 = $IsOpenSUSE -and $LinuxInfo.VERSION_ID -match '13' - ${IsOpenSUSE42.1} = $IsOpenSUSE -and $LinuxInfo.VERSION_ID -match '42.1' - $IsRedHatFamily = $IsCentOS -or $IsFedora -or $IsOpenSUSE - - # Workaround for temporary LD_LIBRARY_PATH hack for Fedora 24 - # https://github.com/PowerShell/PowerShell/issues/2511 - if ($IsFedora -and (Test-Path ENV:\LD_LIBRARY_PATH)) { - Remove-Item -Force ENV:\LD_LIBRARY_PATH - Get-ChildItem ENV: + $environment += @{'IsCoreCLR' = $true} + $environment += @{'IsLinux' = $Runtime::IsOSPlatform($OSPlatform::Linux)} + $environment += @{'IsOSX' = $Runtime::IsOSPlatform($OSPlatform::OSX)} + $environment += @{'IsWindows' = $Runtime::IsOSPlatform($OSPlatform::Windows)} + } catch { + # If these are already set, then they're read-only and we're done + try { + $environment += @{'IsCoreCLR' = $false} + $environment += @{'IsLinux' = $false} + $environment += @{'IsOSX' = $false} + $environment += @{'IsWindows' = $true} + } + catch { } } + + if ($Environment.IsWindows) + { + $environment += @{'IsAdmin' = (New-Object Security.Principal.WindowsPrincipal ([Security.Principal.WindowsIdentity]::GetCurrent())).IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator)} + # Can't use $env:HOME - not available on older systems (e.g. in AppVeyor) + $environment += @{'nugetPackagesRoot' = "${env:HOMEDRIVE}${env:HOMEPATH}\.nuget\packages"} + } + else + { + $environment += @{'nugetPackagesRoot' = "${env:HOME}/.nuget/packages"} + } + + if ($Environment.IsLinux) { + $LinuxInfo = Get-Content /etc/os-release -Raw | ConvertFrom-StringData + + $environment += @{'LinuxInfo' = $LinuxInfo} + $environment += @{'IsUbuntu' = $LinuxInfo.ID -match 'ubuntu'} + $environment += @{'IsUbuntu14' = $Environment.IsUbuntu -and $LinuxInfo.VERSION_ID -match '14.04'} + $environment += @{'IsUbuntu16' = $Environment.IsUbuntu -and $LinuxInfo.VERSION_ID -match '16.04'} + $environment += @{'IsCentOS' = $LinuxInfo.ID -match 'centos' -and $LinuxInfo.VERSION_ID -match '7'} + $environment += @{'IsFedora' = $LinuxInfo.ID -match 'fedora' -and $LinuxInfo.VERSION_ID -ge 24} + $environment += @{'IsOpenSUSE' = $LinuxInfo.ID -match 'opensuse'} + $environment += @{'IsOpenSUSE13' = $Environment.IsOpenSUSE -and $LinuxInfo.VERSION_ID -match '13'} + $environment += @{'IsOpenSUSE42.1' = $Environment.IsOpenSUSE -and $LinuxInfo.VERSION_ID -match '42.1'} + $environment += @{'IsRedHatFamily' = $Environment.IsCentOS -or $Environment.IsFedora -or $Environment.IsOpenSUSE} + + # Workaround for temporary LD_LIBRARY_PATH hack for Fedora 24 + # https://github.com/PowerShell/PowerShell/issues/2511 + if ($environment.IsFedora -and (Test-Path ENV:\LD_LIBRARY_PATH)) { + Remove-Item -Force ENV:\LD_LIBRARY_PATH + Get-ChildItem ENV: + } + } + + return [PSCustomObject] $environment } +$Environment = Get-EnvironmentInformation + # Autoload (in current session) temporary modules used in our tests $TestModulePath = Join-Path $PSScriptRoot "test/tools/Modules" if ( $env:PSModulePath -notcontains $TestModulePath ) { @@ -211,7 +222,7 @@ Fix steps: return } - if ($IsWindows) { + if ($Environment.IsWindows) { # cmake is needed to build powershell.exe $precheck = $precheck -and (precheck 'cmake' 'cmake not found. Run "Start-PSBootstrap -BuildNative". You can also install it from https://chocolatey.org/packages/cmake') @@ -242,7 +253,7 @@ Fix steps: $msbuildConfiguration = 'Release' } - } elseif ($IsLinux -or $IsOSX) { + } elseif ($Environment.IsLinux -or $Environment.IsOSX) { foreach ($Dependency in 'cmake', 'make', 'g++') { $precheck = $precheck -and (precheck $Dependency "Build dependency '$Dependency' not found. Run 'Start-PSBootstrap -BuildNative'.") } @@ -318,10 +329,10 @@ Fix steps: } # Build native components - if (($IsLinux -or $IsOSX) -and -not $SMAOnly) { - $Ext = if ($IsLinux) { + if (($Environment.IsLinux -or $Environment.IsOSX) -and -not $SMAOnly) { + $Ext = if ($Environment.IsLinux) { "so" - } elseif ($IsOSX) { + } elseif ($Environment.IsOSX) { "dylib" } @@ -341,7 +352,7 @@ Fix steps: if (-not (Test-Path $Lib)) { throw "Compilation of $Lib failed" } - } elseif ($IsWindows -and (-not $SMAOnly)) { + } elseif ($Environment.IsWindows -and (-not $SMAOnly)) { log "Start building native Windows binaries" try { @@ -481,12 +492,12 @@ cmd.exe /C cd /d "$location" "&" "$($vcPath)\vcvarsall.bat" "$NativeHostArch" "& } # copy PowerShell host profile if Windows - if ($IsWindows) + if ($Environment.IsWindows) { Copy-Item -Path "$PSScriptRoot/src/powershell-win-core/Microsoft.PowerShell_profile.ps1" -Destination $publishPath -Force } - if ($IsRedHatFamily) { + if ($Environment.IsRedHatFamily) { # add two symbolic links to system shared libraries that libmi.so is dependent on to handle # platform specific changes. This is the only set of platforms needed for this currently # as Ubuntu has these specific library files in the platform and OSX builds for itself @@ -582,29 +593,29 @@ function New-PSOptions { $ConfigWarningMsg = "The passed-in Configuration value '{0}' is not supported on '{1}'. Use '{2}' instead." if (-not $Configuration) { - $Configuration = if ($IsLinux -or $IsOSX) { + $Configuration = if ($Environment.IsLinux -or $Environment.IsOSX) { "Linux" - } elseif ($IsWindows) { + } elseif ($Environment.IsWindows) { "Debug" } } else { switch ($Configuration) { "Linux" { - if ($IsWindows) { + if ($Environment.IsWindows) { $Configuration = "Debug" Write-Warning ($ConfigWarningMsg -f $switch.Current, "Windows", $Configuration) } } "CodeCoverage" { - if(-not $IsWindows) { + if(-not $Environment.IsWindows) { $Configuration = "Linux" - Write-Warning ($ConfigWarningMsg -f $switch.Current, $LinuxInfo.PRETTY_NAME, $Configuration) + Write-Warning ($ConfigWarningMsg -f $switch.Current, $Environment.LinuxInfo.PRETTY_NAME, $Configuration) } } Default { - if ($IsLinux -or $IsOSX) { + if ($Environment.IsLinux -or $Environment.IsOSX) { $Configuration = "Linux" - Write-Warning ($ConfigWarningMsg -f $switch.Current, $LinuxInfo.PRETTY_NAME, $Configuration) + Write-Warning ($ConfigWarningMsg -f $switch.Current, $Environment.LinuxInfo.PRETTY_NAME, $Configuration) } } } @@ -645,9 +656,9 @@ function New-PSOptions { } } - $Executable = if ($IsLinux -or $IsOSX) { + $Executable = if ($Environment.IsLinux -or $Environment.IsOSX) { "powershell" - } elseif ($IsWindows) { + } elseif ($Environment.IsWindows) { "powershell.exe" } @@ -669,6 +680,11 @@ function New-PSOptions { CrossGen = $CrossGen } } +# Get the Options of the last build +function Get-PSOptions { + return $script:Options +} + function Get-PSOutput { [CmdletBinding()]param( @@ -804,12 +820,12 @@ function Start-PSPester { # we need to do few checks and if user didn't provide $ExcludeTag explicitly, we should alternate the default if ($Unelevate) { - if (-not $IsWindows) + if (-not $Environment.IsWindows) { throw '-Unelevate is currently not supported on non-Windows platforms' } - if (-not $IsAdmin) + if (-not $Environment.IsAdmin) { throw '-Unelevate cannot be applied because the current user is not Administrator' } @@ -819,7 +835,7 @@ function Start-PSPester { $ExcludeTag += 'RequireAdminOnWindows' } } - elseif ($IsWindows -and (-not $IsAdmin)) + elseif ($Environment.IsWindows -and (-not $Environment.IsAdmin)) { if (-not $PSBoundParameters.ContainsKey('ExcludeTag')) { @@ -837,7 +853,7 @@ function Start-PSPester { $Command += '$env:PSModulePath = '+"'$TestModulePath$TestModulePathSeparator'" + '+$($env:PSModulePath);' # Windows needs the execution policy adjusted - if ($IsWindows) { + if ($Environment.IsWindows) { $Command += "Set-ExecutionPolicy -Scope Process Unrestricted; " } $startParams = @{binDir=$binDir} @@ -934,7 +950,7 @@ function script:Start-UnelevatedProcess [string]$process, [string[]]$arguments ) - if (-not $IsWindows) + if (-not $Environment.IsWindows) { throw "Start-UnelevatedProcess is currently not supported on non-Windows platforms" } @@ -994,11 +1010,11 @@ function Start-PSxUnit { log "xUnit tests are currently disabled pending fixes due to API and AssemblyLoadContext changes - @andschwa" return - if ($IsWindows) { + if ($Environment.IsWindows) { throw "xUnit tests are only currently supported on Linux / OS X" } - if ($IsOSX) { + if ($Environment.IsOSX) { log "Not yet supported on OS X, pretending they passed..." return } @@ -1049,11 +1065,11 @@ function Install-Dotnet { $obtainUrl = "https://raw.githubusercontent.com/dotnet/cli/master/scripts/obtain" # Install for Linux and OS X - if ($IsLinux -or $IsOSX) { + if ($Environment.IsLinux -or $Environment.IsOSX) { # Uninstall all previous dotnet packages - $uninstallScript = if ($IsUbuntu) { + $uninstallScript = if ($Environment.IsUbuntu) { "dotnet-uninstall-debian-packages.sh" - } elseif ($IsOSX) { + } elseif ($Environment.IsOSX) { "dotnet-uninstall-pkgs.sh" } @@ -1072,12 +1088,12 @@ function Install-Dotnet { curl -sO $obtainUrl/$installScript bash ./$installScript -c $Channel -v $Version } - } elseif ($IsWindows) { + } elseif ($Environment.IsWindows) { Remove-Item -ErrorAction SilentlyContinue -Recurse -Force ~\AppData\Local\Microsoft\dotnet $installScript = "dotnet-install.ps1" Invoke-WebRequest -Uri $obtainUrl/$installScript -OutFile $installScript - if (-not $IsCoreCLR) { + if (-not $Environment.IsCoreCLR) { & ./$installScript -Channel $Channel -Version $Version } else { # dotnet-install.ps1 uses APIs that are not supported in .NET Core, so we run it with Windows PowerShell @@ -1089,11 +1105,11 @@ function Install-Dotnet { } function Get-RedHatPackageManager { - if ($IsCentOS) { + if ($Environment.IsCentOS) { "yum install -y -q" - } elseif ($IsFedora) { + } elseif ($Environment.IsFedora) { "dnf install -y -q" - } elseif ($IsOpenSUSE) { + } elseif ($Environment.IsOpenSUSE) { "zypper --non-interactive install" } else { throw "Error determining package manager for this distribution." @@ -1124,7 +1140,7 @@ function Start-PSBootstrap { try { # Update googletest submodule for linux native cmake - if ($IsLinux -or $IsOSX) { + if ($Environment.IsLinux -or $Environment.IsOSX) { try { Push-Location $PSScriptRoot $Submodule = "$PSScriptRoot/src/libpsl-native/test/googletest" @@ -1137,14 +1153,14 @@ function Start-PSBootstrap { # Install ours and .NET's dependencies $Deps = @() - if ($IsUbuntu) { + if ($Environment.IsUbuntu) { # Build tools $Deps += "curl", "g++", "cmake", "make" # .NET Core required runtime libraries $Deps += "libunwind8" - if ($IsUbuntu14) { $Deps += "libicu52" } - elseif ($IsUbuntu16) { $Deps += "libicu55" } + if ($Environment.IsUbuntu14) { $Deps += "libicu52" } + elseif ($Environment.IsUbuntu16) { $Deps += "libicu55" } # Packaging tools if ($Package) { $Deps += "ruby-dev", "groff" } @@ -1154,7 +1170,7 @@ function Start-PSBootstrap { Invoke-Expression "$sudo apt-get update -qq" Invoke-Expression "$sudo apt-get install -y -qq $Deps" } - } elseif ($IsRedHatFamily) { + } elseif ($Environment.IsRedHatFamily) { # Build tools $Deps += "which", "curl", "gcc-c++", "cmake", "make" @@ -1178,7 +1194,7 @@ function Start-PSBootstrap { Start-NativeExecution { Invoke-Expression "$baseCommand $Deps" } - } elseif ($IsOSX) { + } elseif ($Environment.IsOSX) { precheck 'brew' "Bootstrap dependency 'brew' not found, must install Homebrew! See http://brew.sh/" # Build tools @@ -1231,7 +1247,7 @@ function Start-PSBootstrap { } # Install for Windows - if ($IsWindows) { + if ($Environment.IsWindows) { $machinePath = [Environment]::GetEnvironmentVariable('Path', 'MACHINE') $newMachineEnvironmentPath = $machinePath @@ -1303,528 +1319,6 @@ function Start-PSBootstrap { } } - -function Start-PSPackage { - [CmdletBinding(DefaultParameterSetName='Version')] - param( - # PowerShell packages use Semantic Versioning http://semver.org/ - [Parameter(ParameterSetName = "Version")] - [string]$Version, - - [Parameter(ParameterSetName = "ReleaseTag")] - [ValidatePattern("^v\d+\.\d+\.\d+(-\w+\.\d+)?$")] - [ValidateNotNullOrEmpty()] - [string]$ReleaseTag, - - # Package name - [ValidatePattern("^powershell")] - [string]$Name = "powershell", - - # Ubuntu, CentOS, Fedora, OS X, and Windows packages are supported - [ValidateSet("deb", "osxpkg", "rpm", "msi", "appx", "zip", "AppImage")] - [string[]]$Type, - - # Generate windows downlevel package - [ValidateSet("win81-x64", "win7-x86", "win7-x64")] - [ValidateScript({$IsWindows})] - [string]$WindowsDownLevel - ) - - # Runtime and Configuration settings required by the package - ($Runtime, $Configuration) = if ($WindowsDownLevel) { - $WindowsDownLevel, "Release" - } else { - New-PSOptions -Configuration "Release" -WarningAction SilentlyContinue | ForEach-Object { $_.Runtime, $_.Configuration } - } - Write-Verbose "Packaging RID: '$Runtime'; Packaging Configuration: '$Configuration'" -Verbose - - # Make sure the most recent build satisfies the package requirement - if (-not $Script:Options -or ## Start-PSBuild hasn't been executed yet - -not $Script:Options.CrossGen -or ## Last build didn't specify -CrossGen - $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 "netcoreapp2.0") ## Last build wasn't for CoreCLR - { - # It's possible that the most recent build doesn't satisfy the package requirement but - # an earlier build does. e.g., run the following in order on win10-x64: - # Start-PSBuild -Clean -CrossGen -Runtime win10-x64 -Configuration Release - # Start-PSBuild -FullCLR - # Start-PSPackage -Type msi - # 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. - throw "Please ensure you have run 'Start-PSBuild -Clean -CrossGen -Runtime $Runtime -Configuration $Configuration'!" - } - - # If ReleaseTag is specified, use the given tag to calculate Vesrion - if ($PSCmdlet.ParameterSetName -eq "ReleaseTag") { - $Version = $ReleaseTag -Replace '^v' - } - - # Use Git tag if not given a version - if (-not $Version) { - $Version = (git --git-dir="$PSScriptRoot/.git" describe) -Replace '^v' - } - - $Source = Split-Path -Path $Script:Options.Output -Parent - Write-Verbose "Packaging Source: '$Source'" -Verbose - - # Decide package output type - if (-not $Type) { - $Type = if ($IsLinux) { - if ($LinuxInfo.ID -match "ubuntu") { - "deb" - } elseif ($IsRedHatFamily) { - "rpm" - } else { - throw "Building packages for $($LinuxInfo.PRETTY_NAME) is unsupported!" - } - } elseif ($IsOSX) { - "osxpkg" - } elseif ($IsWindows) { - "msi", "appx" - } - Write-Warning "-Type was not specified, continuing with $Type!" - } - - # Build the name suffix for win-plat packages - if ($IsWindows) { - # Add the server name to the $RunTime. $runtime produced by dotnet is same for client or server - switch ($Runtime) { - 'win81-x64' {$NameSuffix = 'win81-win2012r2-x64'} - 'win10-x64' {$NameSuffix = 'win10-win2016-x64'} - 'win7-x64' {$NameSuffix = 'win7-win2008r2-x64'} - Default {$NameSuffix = $Runtime} - } - } - - switch ($Type) { - "zip" { - $Arguments = @{ - PackageNameSuffix = $NameSuffix - PackageSourcePath = $Source - PackageVersion = $Version - } - New-ZipPackage @Arguments - } - "msi" { - $TargetArchitecture = "x64" - if ($Runtime -match "-x86") - { - $TargetArchitecture = "x86" - } - - $Arguments = @{ - ProductNameSuffix = $NameSuffix - ProductSourcePath = $Source - ProductVersion = $Version - AssetsPath = "$PSScriptRoot\assets" - LicenseFilePath = "$PSScriptRoot\assets\license.rtf" - # Product Guid needs to be unique for every PowerShell version to allow SxS install - ProductGuid = [Guid]::NewGuid(); - ProductTargetArchitecture = $TargetArchitecture; - } - New-MSIPackage @Arguments - } - "appx" { - $Arguments = @{ - PackageNameSuffix = $NameSuffix - PackageSourcePath = $Source - PackageVersion = $Version - AssetsPath = "$PSScriptRoot\assets" - } - New-AppxPackage @Arguments - } - "AppImage" { - if ($IsUbuntu14) { - Start-NativeExecution { bash -iex "$PSScriptRoot/tools/appimage.sh" } - $appImage = Get-Item PowerShell-*.AppImage - if ($appImage.Count -gt 1) { - throw "Found more than one AppImage package, remove all *.AppImage files and try to create the package again" - } - Rename-Item $appImage.Name $appImage.Name.Replace("-","-$Version-") - } else { - Write-Warning "Ignoring AppImage type for non Ubuntu Trusty platform" - } - } - default { - $Arguments = @{ - Type = $_ - PackageSourcePath = $Source - Name = $Name - Version = $Version - } - New-UnixPackage @Arguments - } - } -} - - -function New-UnixPackage { - [CmdletBinding()] - 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" - ) - - # Validate platform - $ErrorMessage = "Must be on {0} to build '$Type' packages!" - switch ($Type) { - "deb" { - $WarningMessage = "Building for Ubuntu {0}.04!" - if (!$IsUbuntu) { - throw ($ErrorMessage -f "Ubuntu") - } elseif ($IsUbuntu14) { - Write-Warning ($WarningMessage -f "14") - } elseif ($IsUbuntu16) { - Write-Warning ($WarningMessage -f "16") - } - } - "rpm" { - if (!$IsRedHatFamily) { - throw ($ErrorMessage -f "Redhat Family") - } - } - "osxpkg" { - if (!$IsOSX) { - throw ($ErrorMessage -f "OS X") - } - } - } - - foreach ($Dependency in "fpm", "ronn") { - if (!(precheck $Dependency "Package dependency '$Dependency' not found. Run Start-PSBootstrap -Package")) { - # These tools are not added to the path automatically on OpenSUSE 13.2 - # try adding them to the path and re-tesing first - [string] $gemsPath = $null - [string] $depenencyPath = $null - $gemsPath = Get-ChildItem -Path /usr/lib64/ruby/gems | Sort-Object -Property LastWriteTime -Descending | Select-Object -First 1 -ExpandProperty FullName - if($gemsPath) { - $depenencyPath = Get-ChildItem -Path (Join-Path -Path $gemsPath -ChildPath "gems" -AdditionalChildPath $Dependency) -Recurse | Sort-Object -Property LastWriteTime -Descending | Select-Object -First 1 -ExpandProperty DirectoryName - $originalPath = $env:PATH - $env:PATH = $ENV:PATH +":" + $depenencyPath - if((precheck $Dependency "Package dependency '$Dependency' not found. Run Start-PSBootstrap -Package")) { - continue - } - else { - $env:PATH = $originalPath - } - } - - throw "Dependency precheck failed!" - } - } - - $Description = @" -PowerShell is an automation and configuration management platform. -It consists of a cross-platform command-line shell and associated scripting language. -"@ - - # Suffix is used for side-by-side package installation - $Suffix = $Name -replace "^powershell" - if (!$Suffix) { - Write-Warning "Suffix not given, building primary PowerShell package!" - $Suffix = $Version - } - - # Setup staging directory so we don't change the original source directory - $Staging = "$PSScriptRoot/staging" - Remove-Item -Recurse -Force -ErrorAction SilentlyContinue $Staging - Copy-Item -Recurse $PackageSourcePath $Staging - - # Rename files to given name if not "powershell" - if ($Name -ne "powershell") { - $Files = @("powershell", - "powershell.dll", - "powershell.deps.json", - "powershell.pdb", - "powershell.runtimeconfig.json", - "powershell.xml") - - foreach ($File in $Files) { - $NewName = $File -replace "^powershell", $Name - Move-Item "$Staging/$File" "$Staging/$NewName" - } - } - - # Follow the Filesystem Hierarchy Standard for Linux and OS X - $Destination = if ($IsLinux) { - "/opt/microsoft/powershell/$Suffix" - } elseif ($IsOSX) { - "/usr/local/microsoft/powershell/$Suffix" - } - - # Destination for symlink to powershell executable - $Link = if ($IsLinux) { - "/usr/bin" - } elseif ($IsOSX) { - "/usr/local/bin" - } - - New-Item -Force -ItemType SymbolicLink -Path "/tmp/$Name" -Target "$Destination/$Name" >$null - - if ($IsRedHatFamily) { - # add two symbolic links to system shared libraries that libmi.so is dependent on to handle - # platform specific changes. This is the only set of platforms needed for this currently - # as Ubuntu has these specific library files in the platform and OSX builds for itself - # against the correct versions. - New-Item -Force -ItemType SymbolicLink -Target "/lib64/libssl.so.10" -Path "$Staging/libssl.so.1.0.0" >$null - New-Item -Force -ItemType SymbolicLink -Target "/lib64/libcrypto.so.10" -Path "$Staging/libcrypto.so.1.0.0" >$null - - $AfterInstallScript = [io.path]::GetTempFileName() - $AfterRemoveScript = [io.path]::GetTempFileName() - @' -#!/bin/sh -if [ ! -f /etc/shells ] ; then - echo "{0}" > /etc/shells -else - grep -q "^{0}$" /etc/shells || echo "{0}" >> /etc/shells -fi -'@ -f "$Link/$Name" | Out-File -FilePath $AfterInstallScript -Encoding ascii - - @' -if [ "$1" = 0 ] ; then - if [ -f /etc/shells ] ; then - TmpFile=`/bin/mktemp /tmp/.powershellmXXXXXX` - grep -v '^{0}$' /etc/shells > $TmpFile - cp -f $TmpFile /etc/shells - rm -f $TmpFile - fi -fi -'@ -f "$Link/$Name" | Out-File -FilePath $AfterRemoveScript -Encoding ascii - } - elseif ($IsUbuntu) { - $AfterInstallScript = [io.path]::GetTempFileName() - $AfterRemoveScript = [io.path]::GetTempFileName() - @' -#!/bin/sh -set -e -case "$1" in - (configure) - add-shell "{0}" - ;; - (abort-upgrade|abort-remove|abort-deconfigure) - exit 0 - ;; - (*) - echo "postinst called with unknown argument '$1'" >&2 - exit 0 - ;; -esac -'@ -f "$Link/$Name" | Out-File -FilePath $AfterInstallScript -Encoding ascii - - @' -#!/bin/sh -set -e -case "$1" in - (remove) - remove-shell "{0}" - ;; -esac -'@ -f "$Link/$Name" | Out-File -FilePath $AfterRemoveScript -Encoding ascii - } - - - # there is a weird bug in fpm - # if the target of the powershell symlink exists, `fpm` aborts - # with a `utime` error on OS X. - # so we move it to make symlink broken - $symlink_dest = "$Destination/$Name" - $hack_dest = "./_fpm_symlink_hack_powershell" - if ($IsOSX) { - if (Test-Path $symlink_dest) { - Write-Warning "Move $symlink_dest to $hack_dest (fpm utime bug)" - Move-Item $symlink_dest $hack_dest - } - } - - # run ronn to convert man page to roff - $RonnFile = Join-Path $PSScriptRoot "/assets/powershell.1.ronn" - $RoffFile = $RonnFile -replace "\.ronn$" - - # Run ronn on assets file - # Run does not play well with files named powershell6.0.1, so we generate and then rename - Start-NativeExecution { ronn --roff $RonnFile } - - # Setup for side-by-side man pages (noop if primary package) - $FixedRoffFile = $RoffFile -replace "powershell.1$", "$Name.1" - if ($Name -ne "powershell") { - Move-Item $RoffFile $FixedRoffFile - } - - # gzip in assets directory - $GzipFile = "$FixedRoffFile.gz" - Start-NativeExecution { gzip -f $FixedRoffFile } - - $ManFile = Join-Path "/usr/local/share/man/man1" (Split-Path -Leaf $GzipFile) - - # Change permissions for packaging - Start-NativeExecution { - find $Staging -type d | xargs chmod 755 - find $Staging -type f | xargs chmod 644 - chmod 644 $GzipFile - chmod 755 "$Staging/$Name" # only the executable should be executable - } - - # Setup package dependencies - # These should match those in the Dockerfiles, but exclude tools like Git, which, and curl - $Dependencies = @() - if ($IsUbuntu) { - $Dependencies = @( - "libc6", - "libcurl3", - "libgcc1", - "libssl1.0.0", - "libstdc++6", - "libtinfo5", - "libunwind8", - "libuuid1", - "zlib1g" - ) - # Please note the different libicu package dependency! - if ($IsUbuntu14) { - $Dependencies += "libicu52" - } elseif ($IsUbuntu16) { - $Dependencies += "libicu55" - } - } elseif ($IsRedHatFamily) { - $Dependencies = @( - "glibc", - "libicu", - "openssl", - "libunwind", - "uuid", - "zlib" - ) - - if($IsFedora -or $IsCentOS) - { - $Dependencies += "libcurl" - $Dependencies += "libgcc" - $Dependencies += "libstdc++" - $Dependencies += "ncurses-base" - } - - if($IsOpenSUSE) - { - $Dependencies += "libgcc_s1" - $Dependencies += "libstdc++6" - } - } - - # iteration is "debian_revision" - # usage of this to differentiate distributions is allowed by non-standard - if ($IsUbuntu14) { - $Iteration += "ubuntu1.14.04.1" - } elseif ($IsUbuntu16) { - $Iteration += "ubuntu1.16.04.1" - } - - # We currently only support: - # CentOS 7 - # Fedora 24+ - # OpenSUSE 42.1 (13.2 might build but is EOL) - # Also SEE: https://fedoraproject.org/wiki/Packaging:DistTag - if ($IsCentOS) { - $rpm_dist = "el7" - } elseif ($IsFedora) { - $version_id = $LinuxInfo.VERSION_ID - $rpm_dist = "fedora.$version_id" - } elseif ($IsOpenSUSE) { - $version_id = $LinuxInfo.VERSION_ID - $rpm_dist = "suse.$version_id" - } - - - $Arguments = @( - "--force", "--verbose", - "--name", $Name, - "--version", $Version, - "--iteration", $Iteration, - "--maintainer", "PowerShell Team ", - "--vendor", "Microsoft Corporation", - "--url", "https://microsoft.com/powershell", - "--license", "MIT License", - "--description", $Description, - "--category", "shells", - "-t", $Type, - "-s", "dir" - ) - if ($IsRedHatFamily) { - $Arguments += @("--rpm-dist", $rpm_dist) - $Arguments += @("--rpm-os", "linux") - } - foreach ($Dependency in $Dependencies) { - $Arguments += @("--depends", $Dependency) - } - if ($AfterInstallScript) { - $Arguments += @("--after-install", $AfterInstallScript) - } - if ($AfterRemoveScript) { - $Arguments += @("--after-remove", $AfterRemoveScript) - } - $Arguments += @( - "$Staging/=$Destination/", - "$GzipFile=$ManFile", - "/tmp/$Name=$Link" - ) - # Build package - try { - $Output = Start-NativeExecution { fpm $Arguments } - } finally { - if ($IsOSX) { - # this is continuation of a fpm hack for a weird bug - if (Test-Path $hack_dest) { - Write-Warning "Move $hack_dest to $symlink_dest (fpm utime bug)" - Move-Item $hack_dest $symlink_dest - } - } - if ($AfterInstallScript) { - Remove-Item -erroraction 'silentlycontinue' $AfterInstallScript - } - if ($AfterRemoveScript) { - Remove-Item -erroraction 'silentlycontinue' $AfterRemoveScript - } - } - - # Magic to get path output - $createdPackage = Get-Item (Join-Path $PSScriptRoot (($Output[-1] -split ":path=>")[-1] -replace '["{}]')) - - if ($IsOSX) { - # Add the OS information to the OSX package file name. - $packageExt = [System.IO.Path]::GetExtension($createdPackage.Name) - $packageNameWithoutExt = [System.IO.Path]::GetFileNameWithoutExtension($createdPackage.Name) - - $newPackageName = "{0}-{1}{2}" -f $packageNameWithoutExt, $script:Options.Runtime, $packageExt - $newPackagePath = Join-Path $createdPackage.DirectoryName $newPackageName - $createdPackage = Rename-Item $createdPackage.FullName $newPackagePath -PassThru - } - - return $createdPackage -} - - function Publish-NuGetFeed { param( @@ -1890,7 +1384,7 @@ function Start-DevPowerShell { } try { - if ((-not $NoNewWindow) -and ($IsCoreCLR)) { + if ((-not $NoNewWindow) -and ($Environment.IsCoreCLR)) { Write-Warning "Start-DevPowerShell -NoNewWindow is currently implied in PowerShellCore edition https://github.com/PowerShell/PowerShell/issues/1543" $NoNewWindow = $true } @@ -2200,7 +1694,7 @@ function Start-ResGen function Find-Dotnet() { $originalPath = $env:PATH - $dotnetPath = if ($IsWindows) { + $dotnetPath = if ($Environment.IsWindows) { "$env:LocalAppData\Microsoft\dotnet" } else { "$env:HOME/.dotnet" @@ -2754,56 +2248,6 @@ function New-AppxPackage $appxLocationPath } -# Function to create a zip file for Nano Server and xcopy deployment -function New-ZipPackage -{ - [CmdletBinding()] - 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 - ) - - $ProductSemanticVersion = Get-PackageSemanticVersion -Version $PackageVersion - - $zipPackageName = $PackageName + "-" + $ProductSemanticVersion - if ($PackageNameSuffix) { - $zipPackageName = $zipPackageName, $PackageNameSuffix -join "-" - } - - Write-Verbose "Create Zip for Product $zipPackageName" - - $zipLocationPath = Join-Path $PWD "$zipPackageName.zip" - - If(Get-Command Compress-Archive -ErrorAction Ignore) - { - Compress-Archive -Path $PackageSourcePath\* -DestinationPath $zipLocationPath - - Write-Verbose "You can find the Zip @ $zipLocationPath" -Verbose - $zipLocationPath - - } - #TODO: Use .NET Api to do compresss-archive equivalent if the cmdlet is not present - else - { - Write-Error -Message "Compress-Archive cmdlet is missing in this PowerShell version" - } -} - function Start-CrossGen { [CmdletBinding()] param( @@ -2873,18 +2317,18 @@ function Start-CrossGen { } # Get the path to crossgen - $crossGenExe = if ($IsWindows) { "crossgen.exe" } else { "crossgen" } + $crossGenExe = if ($Environment.IsWindows) { "crossgen.exe" } else { "crossgen" } # The crossgen tool is only published for these particular runtimes - $crossGenRuntime = if ($IsWindows) { + $crossGenRuntime = if ($Environment.IsWindows) { if ($Runtime -match "-x86") { "win-x86" } else { "win-x64" } - } elseif ($IsLinux) { + } elseif ($Environment.IsLinux) { "linux-x64" - } elseif ($IsOSX) { + } elseif ($Environment.IsOSX) { "osx-x64" } @@ -2893,7 +2337,7 @@ function Start-CrossGen { } # Get the CrossGen.exe for the correct runtime with the latest version - $crossGenPath = Get-ChildItem $script:nugetPackagesRoot $crossGenExe -Recurse | ` + $crossGenPath = Get-ChildItem $script:Environment.nugetPackagesRoot $crossGenExe -Recurse | ` Where-Object { $_.FullName -match $crossGenRuntime } | ` Sort-Object -Property FullName -Descending | ` Select-Object -First 1 | ` @@ -2905,7 +2349,7 @@ function Start-CrossGen { # 'x' permission is not set for packages restored on Linux/OSX. # this is temporary workaround to a bug in dotnet.exe, tracking by dotnet/cli issue #6286 - if ($IsLinux -or $IsOSX) { + if ($Environment.IsLinux -or $Environment.IsOSX) { chmod u+x $crossGenPath } @@ -2915,11 +2359,11 @@ function Start-CrossGen { # clrjit.dll on Windows or libclrjit.so/dylib on Linux/OS X $crossGenRequiredAssemblies = @("mscorlib.dll", "System.Private.CoreLib.dll") - $crossGenRequiredAssemblies += if ($IsWindows) { + $crossGenRequiredAssemblies += if ($Environment.IsWindows) { "clrjit.dll" - } elseif ($IsLinux) { + } elseif ($Environment.IsLinux) { "libclrjit.so" - } elseif ($IsOSX) { + } elseif ($Environment.IsOSX) { "libclrjit.dylib" } @@ -2970,7 +2414,7 @@ function Start-CrossGen { ) # Add Windows specific libraries - if ($IsWindows) { + if ($Environment.IsWindows) { $psCoreAssemblyList += @( "Microsoft.WSMan.Management.dll", "Microsoft.WSMan.Runtime.dll", diff --git a/docs/maintainers/releasing.md b/docs/maintainers/releasing.md index b3a5d5c9dd..280e8d7b37 100644 --- a/docs/maintainers/releasing.md +++ b/docs/maintainers/releasing.md @@ -57,8 +57,8 @@ not `/home/username/src/PowerShell/...`. ### Packaging Overview -The `build.psm1` module contains a `Start-PSPackage` function to build packages. -It **requires** that PowerShell Core has been built via `Start-PSBuild`. +The `tools/packaging` module contains a `Start-PSPackage` function to build packages. +It **requires** that PowerShell Core has been built via `Start-PSBuild` from the `build.psm1` module. #### Windows diff --git a/tools/appveyor.psm1 b/tools/appveyor.psm1 index 257e48b7d7..19e968bcec 100644 --- a/tools/appveyor.psm1 +++ b/tools/appveyor.psm1 @@ -9,7 +9,9 @@ if(Test-Path $dotNetPath) $env:PATH = $dotNetPath + ';' + $env:PATH } -Import-Module (Join-Path $repoRoot 'build.psm1') +#import build into the global scope so it can be used by packaging +Import-Module (Join-Path $repoRoot 'build.psm1') -Scope Global +Import-Module (Join-Path $repoRoot 'tools\packaging') function New-LocalUser { diff --git a/tools/packaging/packaging.psd1 b/tools/packaging/packaging.psd1 new file mode 100644 index 0000000000..e997b8f5bb --- /dev/null +++ b/tools/packaging/packaging.psd1 @@ -0,0 +1,11 @@ +@{ +GUID="41857994-4283-4757-a932-0b0edb104913" +Author="Microsoft Corporation" +CompanyName="Microsoft Corporation" +Copyright="© Microsoft Corporation. All rights reserved." +ModuleVersion="1.0.0" +PowerShellVersion="5.0" +CmdletsToExport="Start-PSPackage" +RootModule="packaging.psm1" +RequiredModules = @("build") +} diff --git a/tools/packaging/packaging.psm1 b/tools/packaging/packaging.psm1 new file mode 100644 index 0000000000..5265162bd5 --- /dev/null +++ b/tools/packaging/packaging.psm1 @@ -0,0 +1,599 @@ +$Environment = Get-EnvironmentInformation +function Start-PSPackage { + [CmdletBinding(DefaultParameterSetName='Version',SupportsShouldProcess=$true)] + param( + # PowerShell packages use Semantic Versioning http://semver.org/ + [Parameter(ParameterSetName = "Version")] + [string]$Version, + + [Parameter(ParameterSetName = "ReleaseTag")] + [ValidatePattern("^v\d+\.\d+\.\d+(-\w+\.\d+)?$")] + [ValidateNotNullOrEmpty()] + [string]$ReleaseTag, + + # Package name + [ValidatePattern("^powershell")] + [string]$Name = "powershell", + + # Ubuntu, CentOS, Fedora, OS X, and Windows packages are supported + [ValidateSet("deb", "osxpkg", "rpm", "msi", "appx", "zip", "AppImage")] + [string[]]$Type, + + # Generate windows downlevel package + [ValidateSet("win81-x64", "win7-x86", "win7-x64")] + [ValidateScript({$Environment.IsWindows})] + [string]$WindowsDownLevel + ) + + # Runtime and Configuration settings required by the package + ($Runtime, $Configuration) = if ($WindowsDownLevel) { + $WindowsDownLevel, "Release" + } else { + New-PSOptions -Configuration "Release" -WarningAction SilentlyContinue | ForEach-Object { $_.Runtime, $_.Configuration } + } + Write-Verbose "Packaging RID: '$Runtime'; Packaging Configuration: '$Configuration'" -Verbose + + $Script:Options = Get-PSOptions + + # Make sure the most recent build satisfies the package requirement + if (-not $Script:Options -or ## Start-PSBuild hasn't been executed yet + -not $Script:Options.CrossGen -or ## Last build didn't specify -CrossGen + $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 "netcoreapp2.0") ## Last build wasn't for CoreCLR + { + # It's possible that the most recent build doesn't satisfy the package requirement but + # an earlier build does. e.g., run the following in order on win10-x64: + # Start-PSBuild -Clean -CrossGen -Runtime win10-x64 -Configuration Release + # Start-PSBuild -FullCLR + # Start-PSPackage -Type msi + # 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. + throw "Please ensure you have run 'Start-PSBuild -Clean -CrossGen -Runtime $Runtime -Configuration $Configuration'!" + } + + # If ReleaseTag is specified, use the given tag to calculate Vesrion + if ($PSCmdlet.ParameterSetName -eq "ReleaseTag") { + $Version = $ReleaseTag -Replace '^v' + } + + # Use Git tag if not given a version + if (-not $Version) { + $Version = (git --git-dir="$PSScriptRoot/../../.git" describe) -Replace '^v' + } + + $Source = Split-Path -Path $Script:Options.Output -Parent + Write-Verbose "Packaging Source: '$Source'" -Verbose + + # Decide package output type + if (-not $Type) { + $Type = if ($Environment.IsLinux) { + if ($Environment.LinuxInfo.ID -match "ubuntu") { + "deb" + } elseif ($Environment.IsRedHatFamily) { + "rpm" + } else { + throw "Building packages for $($Environment.LinuxInfo.PRETTY_NAME) is unsupported!" + } + } elseif ($Environment.IsOSX) { + "osxpkg" + } elseif ($Environment.IsWindows) { + "msi", "appx" + } + Write-Warning "-Type was not specified, continuing with $Type!" + } + Write-Verbose "Packaging Type: $Type" -Verbose + + # Build the name suffix for win-plat packages + if ($Environment.IsWindows) { + # Add the server name to the $RunTime. $runtime produced by dotnet is same for client or server + switch ($Runtime) { + 'win81-x64' {$NameSuffix = 'win81-win2012r2-x64'} + 'win10-x64' {$NameSuffix = 'win10-win2016-x64'} + 'win7-x64' {$NameSuffix = 'win7-win2008r2-x64'} + Default {$NameSuffix = $Runtime} + } + } + + switch ($Type) { + "zip" { + $Arguments = @{ + PackageNameSuffix = $NameSuffix + PackageSourcePath = $Source + PackageVersion = $Version + } + + if($pscmdlet.ShouldProcess("Create Zip Package")) + { + New-ZipPackage @Arguments + } + } + "msi" { + $TargetArchitecture = "x64" + if ($Runtime -match "-x86") + { + $TargetArchitecture = "x86" + } + + $Arguments = @{ + ProductNameSuffix = $NameSuffix + ProductSourcePath = $Source + ProductVersion = $Version + AssetsPath = "$PSScriptRoot\..\..\assets" + LicenseFilePath = "$PSScriptRoot\..\..\assets\license.rtf" + # Product Guid needs to be unique for every PowerShell version to allow SxS install + ProductGuid = New-Guid + ProductTargetArchitecture = $TargetArchitecture; + } + + if($pscmdlet.ShouldProcess("Create MSI Package")) + { + New-MSIPackage @Arguments + } + } + "appx" { + $Arguments = @{ + PackageNameSuffix = $NameSuffix + PackageSourcePath = $Source + PackageVersion = $Version + AssetsPath = "$PSScriptRoot\..\..\assets" + } + New-AppxPackage @Arguments + } + "AppImage" { + if ($Environment.IsUbuntu14) { + $null = Start-NativeExecution { bash -iex "$PSScriptRoot/../appimage.sh" } + $appImage = Get-Item PowerShell-*.AppImage + if ($appImage.Count -gt 1) { + throw "Found more than one AppImage package, remove all *.AppImage files and try to create the package again" + } + Rename-Item $appImage.Name $appImage.Name.Replace("-","-$Version-") + } else { + Write-Warning "Ignoring AppImage type for non Ubuntu Trusty platform" + } + } + default { + $Arguments = @{ + Type = $_ + PackageSourcePath = $Source + Name = $Name + Version = $Version + } + + if($pscmdlet.ShouldProcess("Create $_ Package")) + { + New-UnixPackage @Arguments + } + } + } +} + +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" + ) + + # Validate platform + $ErrorMessage = "Must be on {0} to build '$Type' packages!" + switch ($Type) { + "deb" { + $WarningMessage = "Building for Ubuntu {0}.04!" + if (!$Environment.IsUbuntu) { + throw ($ErrorMessage -f "Ubuntu") + } elseif ($Environment.IsUbuntu14) { + Write-Warning ($WarningMessage -f "14") + } elseif ($Environment.IsUbuntu16) { + Write-Warning ($WarningMessage -f "16") + } + } + "rpm" { + if (!$Environment.IsRedHatFamily) { + throw ($ErrorMessage -f "Redhat Family") + } + } + "osxpkg" { + if (!$Environment.IsOSX) { + throw ($ErrorMessage -f "OS X") + } + } + } + + foreach ($Dependency in "fpm", "ronn") { + if (!(precheck $Dependency "Package dependency '$Dependency' not found. Run Start-PSBootstrap -Package")) { + # These tools are not added to the path automatically on OpenSUSE 13.2 + # try adding them to the path and re-tesing first + [string] $gemsPath = $null + [string] $depenencyPath = $null + $gemsPath = Get-ChildItem -Path /usr/lib64/ruby/gems | Sort-Object -Property LastWriteTime -Descending | Select-Object -First 1 -ExpandProperty FullName + if($gemsPath) { + $depenencyPath = Get-ChildItem -Path (Join-Path -Path $gemsPath -ChildPath "gems" -AdditionalChildPath $Dependency) -Recurse | Sort-Object -Property LastWriteTime -Descending | Select-Object -First 1 -ExpandProperty DirectoryName + $originalPath = $env:PATH + $env:PATH = $ENV:PATH +":" + $depenencyPath + if((precheck $Dependency "Package dependency '$Dependency' not found. Run Start-PSBootstrap -Package")) { + continue + } + else { + $env:PATH = $originalPath + } + } + + throw "Dependency precheck failed!" + } + } + + $Description = @" +PowerShell is an automation and configuration management platform. +It consists of a cross-platform command-line shell and associated scripting language. +"@ + + # Suffix is used for side-by-side package installation + $Suffix = $Name -replace "^powershell" + if (!$Suffix) { + Write-Warning "Suffix not given, building primary PowerShell package!" + $Suffix = $Version + } + + # Setup staging directory so we don't change the original source directory + $Staging = "$PSScriptRoot/staging" + if($pscmdlet.ShouldProcess("Create staging folder")) + { + Remove-Item -Recurse -Force -ErrorAction SilentlyContinue $Staging + Copy-Item -Recurse $PackageSourcePath $Staging + + # Rename files to given name if not "powershell" + if ($Name -ne "powershell") { + $Files = @("powershell", + "powershell.dll", + "powershell.deps.json", + "powershell.pdb", + "powershell.runtimeconfig.json", + "powershell.xml") + + foreach ($File in $Files) { + $NewName = $File -replace "^powershell", $Name + Move-Item "$Staging/$File" "$Staging/$NewName" + } + } + } + + # Follow the Filesystem Hierarchy Standard for Linux and OS X + $Destination = if ($Environment.IsLinux) { + "/opt/microsoft/powershell/$Suffix" + } elseif ($Environment.IsOSX) { + "/usr/local/microsoft/powershell/$Suffix" + } + + # Destination for symlink to powershell executable + $Link = if ($Environment.IsLinux) { + "/usr/bin" + } elseif ($Environment.IsOSX) { + "/usr/local/bin" + } + + if($pscmdlet.ShouldProcess("Create package file system")) + { + New-Item -Force -ItemType SymbolicLink -Path "/tmp/$Name" -Target "$Destination/$Name" >$null + + if ($Environment.IsRedHatFamily) { + # add two symbolic links to system shared libraries that libmi.so is dependent on to handle + # platform specific changes. This is the only set of platforms needed for this currently + # as Ubuntu has these specific library files in the platform and OSX builds for itself + # against the correct versions. + New-Item -Force -ItemType SymbolicLink -Target "/lib64/libssl.so.10" -Path "$Staging/libssl.so.1.0.0" >$null + New-Item -Force -ItemType SymbolicLink -Target "/lib64/libcrypto.so.10" -Path "$Staging/libcrypto.so.1.0.0" >$null + + $AfterInstallScript = [io.path]::GetTempFileName() + $AfterRemoveScript = [io.path]::GetTempFileName() + @' +#!/bin/sh +if [ ! -f /etc/shells ] ; then + echo "{0}" > /etc/shells +else + grep -q "^{0}$" /etc/shells || echo "{0}" >> /etc/shells +fi +'@ -f "$Link/$Name" | Out-File -FilePath $AfterInstallScript -Encoding ascii + + @' +if [ "$1" = 0 ] ; then + if [ -f /etc/shells ] ; then + TmpFile=`/bin/mktemp /tmp/.powershellmXXXXXX` + grep -v '^{0}$' /etc/shells > $TmpFile + cp -f $TmpFile /etc/shells + rm -f $TmpFile + fi +fi +'@ -f "$Link/$Name" | Out-File -FilePath $AfterRemoveScript -Encoding ascii + } + elseif ($Environment.IsUbuntu) { + $AfterInstallScript = [io.path]::GetTempFileName() + $AfterRemoveScript = [io.path]::GetTempFileName() + @' +#!/bin/sh +set -e +case "$1" in + (configure) + add-shell "{0}" + ;; + (abort-upgrade|abort-remove|abort-deconfigure) + exit 0 + ;; + (*) + echo "postinst called with unknown argument '$1'" >&2 + exit 0 + ;; +esac +'@ -f "$Link/$Name" | Out-File -FilePath $AfterInstallScript -Encoding ascii + + @' +#!/bin/sh +set -e +case "$1" in + (remove) + remove-shell "{0}" + ;; +esac +'@ -f "$Link/$Name" | Out-File -FilePath $AfterRemoveScript -Encoding ascii + } + + + # there is a weird bug in fpm + # if the target of the powershell symlink exists, `fpm` aborts + # with a `utime` error on OS X. + # so we move it to make symlink broken + $symlink_dest = "$Destination/$Name" + $hack_dest = "./_fpm_symlink_hack_powershell" + if ($Environment.IsOSX) { + if (Test-Path $symlink_dest) { + Write-Warning "Move $symlink_dest to $hack_dest (fpm utime bug)" + Move-Item $symlink_dest $hack_dest + } + } + + # run ronn to convert man page to roff + $RonnFile = Join-Path $PSScriptRoot "/../../assets/powershell.1.ronn" + $RoffFile = $RonnFile -replace "\.ronn$" + + # Run ronn on assets file + # Run does not play well with files named powershell6.0.1, so we generate and then rename + Start-NativeExecution { ronn --roff $RonnFile } + + # Setup for side-by-side man pages (noop if primary package) + $FixedRoffFile = $RoffFile -replace "powershell.1$", "$Name.1" + if ($Name -ne "powershell") { + Move-Item $RoffFile $FixedRoffFile + } + + # gzip in assets directory + $GzipFile = "$FixedRoffFile.gz" + Start-NativeExecution { gzip -f $FixedRoffFile } + + $ManFile = Join-Path "/usr/local/share/man/man1" (Split-Path -Leaf $GzipFile) + + # Change permissions for packaging + Start-NativeExecution { + find $Staging -type d | xargs chmod 755 + find $Staging -type f | xargs chmod 644 + chmod 644 $GzipFile + chmod 755 "$Staging/$Name" # only the executable should be executable + } + } + + # Setup package dependencies + # These should match those in the Dockerfiles, but exclude tools like Git, which, and curl + $Dependencies = @() + if ($Environment.IsUbuntu) { + $Dependencies = @( + "libc6", + "libcurl3", + "libgcc1", + "libssl1.0.0", + "libstdc++6", + "libtinfo5", + "libunwind8", + "libuuid1", + "zlib1g" + ) + # Please note the different libicu package dependency! + if ($Environment.IsUbuntu14) { + $Dependencies += "libicu52" + } elseif ($Environment.IsUbuntu16) { + $Dependencies += "libicu55" + } + } elseif ($Environment.IsRedHatFamily) { + $Dependencies = @( + "glibc", + "libicu", + "openssl", + "libunwind", + "uuid", + "zlib" + ) + + if($Environment.IsFedora -or $Environment.IsCentOS) + { + $Dependencies += "libcurl" + $Dependencies += "libgcc" + $Dependencies += "libstdc++" + $Dependencies += "ncurses-base" + } + + if($Environment.IsOpenSUSE) + { + $Dependencies += "libgcc_s1" + $Dependencies += "libstdc++6" + } + } + + # iteration is "debian_revision" + # usage of this to differentiate distributions is allowed by non-standard + if ($Environment.IsUbuntu14) { + $Iteration += "ubuntu1.14.04.1" + } elseif ($Environment.IsUbuntu16) { + $Iteration += "ubuntu1.16.04.1" + } + + # We currently only support: + # CentOS 7 + # Fedora 24+ + # OpenSUSE 42.1 (13.2 might build but is EOL) + # Also SEE: https://fedoraproject.org/wiki/Packaging:DistTag + if ($Environment.IsCentOS) { + $rpm_dist = "el7" + } elseif ($Environment.IsFedora) { + $version_id = $Environment.LinuxInfo.VERSION_ID + $rpm_dist = "fedora.$version_id" + } elseif ($Environment.IsOpenSUSE) { + $version_id = $Environment.LinuxInfo.VERSION_ID + $rpm_dist = "suse.$version_id" + } + + + $Arguments = @( + "--force", "--verbose", + "--name", $Name, + "--version", $Version, + "--iteration", $Iteration, + "--maintainer", "PowerShell Team ", + "--vendor", "Microsoft Corporation", + "--url", "https://microsoft.com/powershell", + "--license", "MIT License", + "--description", $Description, + "--category", "shells", + "-t", $Type, + "-s", "dir" + ) + if ($Environment.IsRedHatFamily) { + $Arguments += @("--rpm-dist", $rpm_dist) + $Arguments += @("--rpm-os", "linux") + } + foreach ($Dependency in $Dependencies) { + $Arguments += @("--depends", $Dependency) + } + if ($AfterInstallScript) { + $Arguments += @("--after-install", $AfterInstallScript) + } + if ($AfterRemoveScript) { + $Arguments += @("--after-remove", $AfterRemoveScript) + } + $Arguments += @( + "$Staging/=$Destination/", + "$GzipFile=$ManFile", + "/tmp/$Name=$Link" + ) + # Build package + try { + if($pscmdlet.ShouldProcess("Create $type package")) + { + $Output = Start-NativeExecution { fpm $Arguments } + } + } finally { + if ($Environment.IsOSX) { + # this is continuation of a fpm hack for a weird bug + if (Test-Path $hack_dest) { + Write-Warning "Move $hack_dest to $symlink_dest (fpm utime bug)" + Move-Item $hack_dest $symlink_dest + } + } + if ($AfterInstallScript) { + Remove-Item -erroraction 'silentlycontinue' $AfterInstallScript + } + if ($AfterRemoveScript) { + Remove-Item -erroraction 'silentlycontinue' $AfterRemoveScript + } + } + + # Magic to get path output + $createdPackage = Get-Item (Join-Path $PWD (($Output[-1] -split ":path=>")[-1] -replace '["{}]')) + + if ($Environment.IsOSX) { + if($pscmdlet.ShouldProcess("Fix package name")) + { + # Add the OS information to the OSX package file name. + $packageExt = [System.IO.Path]::GetExtension($createdPackage.Name) + $packageNameWithoutExt = [System.IO.Path]::GetFileNameWithoutExtension($createdPackage.Name) + + $newPackageName = "{0}-{1}{2}" -f $packageNameWithoutExt, $script:Options.Runtime, $packageExt + $newPackagePath = Join-Path $createdPackage.DirectoryName $newPackageName + $createdPackage = Rename-Item $createdPackage.FullName $newPackagePath -PassThru + } + } + + return $createdPackage +} + +# 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 + ) + + $ProductSemanticVersion = Get-PackageSemanticVersion -Version $PackageVersion + + $zipPackageName = $PackageName + "-" + $ProductSemanticVersion + if ($PackageNameSuffix) { + $zipPackageName = $zipPackageName, $PackageNameSuffix -join "-" + } + + Write-Verbose "Create Zip for Product $zipPackageName" + + $zipLocationPath = Join-Path $PWD "$zipPackageName.zip" + + If(Get-Command Compress-Archive -ErrorAction Ignore) + { + if($pscmdlet.ShouldProcess("Create zip package")) + { + Compress-Archive -Path $PackageSourcePath\* -DestinationPath $zipLocationPath + } + + Write-Verbose "You can find the Zip @ $zipLocationPath" -Verbose + $zipLocationPath + + } + #TODO: Use .NET Api to do compresss-archive equivalent if the pscmdlet is not present + else + { + Write-Error -Message "Compress-Archive cmdlet is missing in this PowerShell version" + } +} diff --git a/tools/travis.ps1 b/tools/travis.ps1 index fb1a2db909..5fbf65eb48 100644 --- a/tools/travis.ps1 +++ b/tools/travis.ps1 @@ -3,6 +3,7 @@ param( ) Import-Module $PSScriptRoot/../build.psm1 -Force +Import-Module $PSScriptRoot/packaging -Force # This function retrieves the appropriate svg to be used when presenting # the daily test run badge