2018-02-13 18:23:53 +01:00
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.
2017-07-23 21:29:33 +02:00
# On Unix paths is separated by colon
# On Windows paths is separated by semicolon
$script:TestModulePathSeparator = [ System.IO.Path ] :: PathSeparator
2016-02-22 01:21:04 +01:00
2017-08-17 02:32:46 +02:00
$dotnetCLIChannel = " release "
2017-11-10 01:28:39 +01:00
$dotnetCLIRequiredVersion = $ ( Get-Content $PSScriptRoot / global . json | ConvertFrom-Json ) . Sdk . Version
2017-06-21 18:32:06 +02:00
2017-08-01 23:35:50 +02:00
# Track if tags have been sync'ed
$tagsUpToDate = $false
# Sync Tags
# When not using a branch in PowerShell/PowerShell, tags will not be fetched automatically
# Since code that uses Get-PSCommitID and Get-PSLatestTag assume that tags are fetched,
# This function can ensure that tags have been fetched.
# This function is used during the setup phase in tools/appveyor.psm1 and tools/travis.ps1
function Sync-PSTags
{
param (
[ Switch ]
$AddRemoteIfMissing
)
$PowerShellRemoteUrl = " https://github.com/powershell/powershell.git "
$upstreamRemoteDefaultName = 'upstream'
$remotes = Start-NativeExecution { git - -git -dir = " $PSScriptRoot /.git " remote }
$upstreamRemote = $null
foreach ( $remote in $remotes )
{
$url = Start-NativeExecution { git - -git -dir = " $PSScriptRoot /.git " remote get-url $remote }
if ( $url -eq $PowerShellRemoteUrl )
{
$upstreamRemote = $remote
break
}
}
if ( ! $upstreamRemote -and $AddRemoteIfMissing . IsPresent -and $remotes -notcontains $upstreamRemoteDefaultName )
{
$null = Start-NativeExecution { git - -git -dir = " $PSScriptRoot /.git " remote add $upstreamRemoteDefaultName $PowerShellRemoteUrl }
$upstreamRemote = $upstreamRemoteDefaultName
}
elseif ( ! $upstreamRemote )
{
Write-Error " Please add a remote to PowerShell\PowerShell. Example: git remote add $upstreamRemoteDefaultName $PowerShellRemoteUrl " -ErrorAction Stop
}
$null = Start-NativeExecution { git - -git -dir = " $PSScriptRoot /.git " fetch - -tags - -quiet $upstreamRemote }
$script:tagsUpToDate = $true
}
# Gets the latest tag for the current branch
function Get-PSLatestTag
{
2017-09-07 00:20:25 +02:00
[ CmdletBinding ( ) ]
param ( )
2017-08-01 23:35:50 +02:00
# This function won't always return the correct value unless tags have been sync'ed
# So, Write a warning to run Sync-PSTags
if ( ! $tagsUpToDate )
{
Write-Warning " Run Sync-PSTags to update tags "
}
return ( Start-NativeExecution { git - -git -dir = " $PSScriptRoot /.git " describe - -abbrev = 0 } )
}
function Get-PSVersion
{
2017-09-07 00:20:25 +02:00
[ CmdletBinding ( ) ]
2017-08-01 23:35:50 +02:00
param (
[ switch ]
$OmitCommitId
)
if ( $OmitCommitId . IsPresent )
{
return ( Get-PSLatestTag ) -replace '^v'
}
else
{
return ( Get-PSCommitId ) -replace '^v'
}
}
function Get-PSCommitId
{
2017-09-07 00:20:25 +02:00
[ CmdletBinding ( ) ]
param ( )
2017-08-01 23:35:50 +02:00
# This function won't always return the correct value unless tags have been sync'ed
# So, Write a warning to run Sync-PSTags
if ( ! $tagsUpToDate )
{
Write-Warning " Run Sync-PSTags to update tags "
}
return ( Start-NativeExecution { git - -git -dir = " $PSScriptRoot /.git " describe - -dirty - -abbrev = 60 } )
}
2017-07-23 21:29:33 +02:00
function Get-EnvironmentInformation
2016-09-16 04:22:18 +02:00
{
2017-07-23 21:29:33 +02:00
$environment = @ { }
2017-12-02 12:39:04 +01:00
# PowerShell Core will likely not be built on pre-1709 nanoserver
if ( $PSVersionTable . ContainsKey ( " PSEdition " ) -and " Core " -eq $PSVersionTable . PSEdition ) {
2017-07-23 21:29:33 +02:00
$environment + = @ { 'IsCoreCLR' = $true }
2017-12-02 12:39:04 +01:00
$environment + = @ { 'IsLinux' = $IsLinux }
$environment + = @ { 'IsMacOS' = $IsMacOS }
$environment + = @ { 'IsWindows' = $IsWindows }
} else {
2017-07-25 00:26:04 +02:00
$environment + = @ { 'IsCoreCLR' = $false }
$environment + = @ { 'IsLinux' = $false }
2017-09-07 19:34:40 +02:00
$environment + = @ { 'IsMacOS' = $false }
2017-07-25 00:26:04 +02:00
$environment + = @ { 'IsWindows' = $true }
2017-07-23 21:29:33 +02:00
}
if ( $Environment . IsWindows )
{
$environment + = @ { 'IsAdmin' = ( New-Object Security . Principal . WindowsPrincipal ( [ Security.Principal.WindowsIdentity ] :: GetCurrent ( ) ) ) . IsInRole ( [ Security.Principal.WindowsBuiltinRole ] :: Administrator ) }
2018-08-18 07:06:51 +02:00
$environment + = @ { 'nugetPackagesRoot' = " ${env:USERPROFILE} \.nuget\packages " }
2017-07-23 21:29:33 +02:00
}
else
{
$environment + = @ { 'nugetPackagesRoot' = " ${env:HOME} /.nuget/packages " }
}
if ( $Environment . IsLinux ) {
$LinuxInfo = Get-Content / etc / os-release -Raw | ConvertFrom-StringData
2016-09-16 04:22:18 +02:00
2017-07-23 21:29:33 +02:00
$environment + = @ { 'LinuxInfo' = $LinuxInfo }
2017-10-03 23:29:43 +02:00
$environment + = @ { 'IsDebian' = $LinuxInfo . ID -match 'debian' }
$environment + = @ { 'IsDebian8' = $Environment . IsDebian -and $LinuxInfo . VERSION_ID -match '8' }
$environment + = @ { 'IsDebian9' = $Environment . IsDebian -and $LinuxInfo . VERSION_ID -match '9' }
2017-07-23 21:29:33 +02:00
$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' }
2018-05-07 21:30:58 +02:00
$environment + = @ { 'IsUbuntu18' = $Environment . IsUbuntu -and $LinuxInfo . VERSION_ID -match '18.04' }
2017-07-23 21:29:33 +02:00
$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' }
2018-01-22 23:58:17 +01:00
$environment + = @ { 'IsSLES' = $LinuxInfo . ID -match 'sles' }
2018-08-14 22:21:26 +02:00
$environment + = @ { 'IsRedHat' = $LinuxInfo . ID -match 'rhel' }
$environment + = @ { 'IsRedHat7' = $Environment . IsRedHat -and $LinuxInfo . VERSION_ID -match '7' }
2018-01-22 23:58:17 +01:00
$environment + = @ { 'IsOpenSUSE13' = $Environmenst . IsOpenSUSE -and $LinuxInfo . VERSION_ID -match '13' }
2017-07-23 21:29:33 +02:00
$environment + = @ { 'IsOpenSUSE42.1' = $Environment . IsOpenSUSE -and $LinuxInfo . VERSION_ID -match '42.1' }
2018-08-14 22:21:26 +02:00
$environment + = @ { 'IsRedHatFamily' = $Environment . IsCentOS -or $Environment . IsFedora -or $Environment . IsRedHat }
2018-01-22 23:58:17 +01:00
$environment + = @ { 'IsSUSEFamily' = $Environment . IsSLES -or $Environment . IsOpenSUSE }
2018-11-27 19:45:49 +01:00
$environment + = @ { 'IsAlpine' = $LinuxInfo . ID -match 'alpine' }
2017-07-23 21:29:33 +02:00
# 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 :
}
2018-08-14 22:21:26 +02:00
if ( -not (
$environment . IsDebian -or
$environment . IsUbuntu -or
2018-08-17 01:44:20 +02:00
$environment . IsRedHatFamily -or
2018-11-27 19:45:49 +01:00
$environment . IsSUSEFamily -or
$environment . IsAlpine )
2018-08-14 22:21:26 +02:00
) {
throw " The current OS : $( $LinuxInfo . ID ) is not supported for building PowerShell. "
}
2017-01-06 19:04:18 +01:00
}
2017-07-23 21:29:33 +02:00
return [ PSCustomObject ] $environment
2016-06-20 20:39:39 +02:00
}
2017-07-23 21:29:33 +02:00
$Environment = Get-EnvironmentInformation
2017-03-27 06:46:39 +02:00
# Autoload (in current session) temporary modules used in our tests
$TestModulePath = Join-Path $PSScriptRoot " test/tools/Modules "
2018-05-08 18:20:39 +02:00
if ( -not $env:PSModulePath . Contains ( $TestModulePath ) ) {
2017-03-27 06:46:39 +02:00
$env:PSModulePath = $TestModulePath + $TestModulePathSeparator + $ ( $env:PSModulePath )
}
2018-06-19 00:54:11 +02:00
<#
. Synopsis
Tests if a version is preview
. EXAMPLE
Test-IsPreview -version '6.1.0-sometthing' # returns true
Test-IsPreview -version '6.1.0' # returns false
#>
function Test-IsPreview
{
param (
[ parameter ( Mandatory ) ]
[ string ]
$Version
)
return $Version -like '*-*'
}
2016-04-01 23:41:13 +02:00
function Start-PSBuild {
2018-10-03 21:49:24 +02:00
[ CmdletBinding ( DefaultParameterSetName = " Default " ) ]
2016-02-22 00:06:39 +01:00
param (
2016-07-20 04:09:06 +02:00
# When specified this switch will stops running dev powershell
# to help avoid compilation error, because file are in use.
[ switch ] $StopDevPowerShell ,
2016-02-24 01:21:00 +01:00
[ switch ] $Restore ,
2018-10-30 01:43:41 +01:00
# Accept a path to the output directory
# When specified, --output <path> will be passed to dotnet
2016-05-06 19:28:45 +02:00
[ string ] $Output ,
2016-06-15 12:42:54 +02:00
[ switch ] $ResGen ,
2016-06-17 20:23:29 +02:00
[ switch ] $TypeGen ,
2016-06-23 04:46:16 +02:00
[ switch ] $Clean ,
2018-10-03 21:49:24 +02:00
[ Parameter ( ParameterSetName = " Legacy " ) ]
2016-11-18 18:55:23 +01:00
[ switch ] $PSModuleRestore ,
2018-10-03 21:49:24 +02:00
[ Parameter ( ParameterSetName = " Default " ) ]
[ switch ] $NoPSModuleRestore ,
2017-12-05 02:16:53 +01:00
[ switch ] $CI ,
2016-03-12 01:19:05 +01:00
2016-08-26 22:46:03 +02:00
# this switch will re-build only System.Management.Automation.dll
2016-07-19 21:51:11 +02:00
# it's useful for development, to do a quick changes in the engine
[ switch ] $SMAOnly ,
2016-03-04 00:54:52 +01:00
# These runtimes must match those in project.json
# We do not use ValidateScript since we want tab completion
2017-11-15 19:52:06 +01:00
# If this parameter is not provided it will get determined automatically.
2018-12-05 19:55:09 +01:00
[ ValidateSet ( " alpine-x64 " ,
" fxdependent " ,
2018-09-10 21:47:32 +02:00
" linux-arm " ,
2018-12-05 19:55:09 +01:00
" linux-arm64 " ,
2017-09-18 18:31:07 +02:00
" linux-x64 " ,
2018-06-28 00:30:38 +02:00
" osx-x64 " ,
2017-12-09 00:43:53 +01:00
" win-arm " ,
2018-06-28 00:30:38 +02:00
" win-arm64 " ,
" win7-x64 " ,
" win7-x86 " ) ]
2016-03-12 01:19:05 +01:00
[ string ] $Runtime ,
2018-05-15 00:01:44 +02:00
[ ValidateSet ( 'Debug' , 'Release' , 'CodeCoverage' , '' ) ] # We might need "Checked" as well
2016-07-29 19:00:46 +02:00
[ string ] $Configuration ,
2017-06-05 19:21:28 +02:00
[ switch ] $CrossGen ,
2017-11-16 01:55:13 +01:00
[ ValidatePattern ( " ^v\d+\.\d+\.\d+(-\w+(\.\d+)?)? $ " ) ]
2017-06-05 19:21:28 +02:00
[ ValidateNotNullOrEmpty ( ) ]
[ string ] $ReleaseTag
2016-02-24 01:21:00 +01:00
)
2016-02-22 00:06:39 +01:00
2018-10-03 21:49:24 +02:00
if ( $PsCmdlet . ParameterSetName -eq " Default " -and ! $NoPSModuleRestore )
{
$PSModuleRestore = $true
}
2017-09-25 21:49:43 +02:00
if ( $Runtime -eq " linux-arm " -and -not $Environment . IsUbuntu ) {
throw " Cross compiling for linux-arm is only supported on Ubuntu environment "
}
2017-12-09 00:43:53 +01:00
if ( " win-arm " , " win-arm64 " -contains $Runtime -and -not $Environment . IsWindows ) {
throw " Cross compiling for win-arm or win-arm64 is only supported on Windows environment "
}
2016-08-03 22:23:21 +02:00
function Stop-DevPowerShell {
2017-10-25 01:28:22 +02:00
Get-Process pwsh * |
2016-08-03 22:23:21 +02:00
Where-Object {
$_ . Modules |
Where-Object {
2016-07-20 04:09:06 +02:00
$_ . FileName -eq ( Resolve-Path $script:Options . Output ) . Path
2016-08-03 22:23:21 +02:00
}
} |
2016-07-20 04:09:06 +02:00
Stop-Process -Verbose
}
2016-08-03 22:23:21 +02:00
if ( $Clean ) {
2018-07-09 19:16:56 +02:00
Write-Log " Cleaning your working directory. You can also do it with 'git clean -fdx --exclude .vs/PowerShell/v15/Server/sqlite3' "
2016-07-26 04:53:21 +02:00
Push-Location $PSScriptRoot
2016-08-03 22:23:21 +02:00
try {
2018-07-09 19:16:56 +02:00
# Excluded sqlite3 folder is due to this Roslyn issue: https://github.com/dotnet/roslyn/issues/23060
2018-11-05 19:48:05 +01:00
# Excluded src/Modules/nuget.config as this is required for release build.
git clean -fdx - -exclude . vs / PowerShell / v15 / Server / sqlite3 - -exclude src / Modules / nuget . config
2016-08-03 22:23:21 +02:00
} finally {
2016-07-26 04:53:21 +02:00
Pop-Location
}
2016-06-23 04:46:16 +02:00
}
2016-06-20 20:59:14 +02:00
# Add .NET CLI tools to PATH
Find-Dotnet
2016-04-14 01:41:50 +02:00
2017-09-25 21:49:43 +02:00
# Verify we have .NET SDK in place to do the build, and abort if the precheck failed
2016-08-05 01:15:49 +02:00
$precheck = precheck 'dotnet' " Build dependency 'dotnet' not found in PATH. Run Start-PSBootstrap. Also see: https://dotnet.github.io/getting-started/ "
2017-07-26 01:50:27 +02:00
if ( -not $precheck ) {
return
}
# Verify if the dotnet in-use is the required version
2017-08-22 00:23:52 +02:00
$dotnetCLIInstalledVersion = ( dotnet - -version )
2017-07-26 01:50:27 +02:00
If ( $dotnetCLIInstalledVersion -ne $dotnetCLIRequiredVersion ) {
2017-06-21 18:32:06 +02:00
Write-Warning @"
The currently installed . NET Command Line Tools is not the required version .
2017-07-07 18:43:37 +02:00
Installed version : $dotnetCLIInstalledVersion
2017-06-21 18:32:06 +02:00
Required version : $dotnetCLIRequiredVersion
Fix steps :
1 . Remove the installed version from :
- on windows '`$env:LOCALAPPDATA\Microsoft\dotnet'
2017-09-07 19:34:40 +02:00
- on macOS and linux '`$env:HOME/.dotnet'
2017-06-21 18:32:06 +02:00
2 . Run Start-PSBootstrap or Install-Dotnet
3 . Start-PSBuild -Clean
` n
" @
return
}
2016-04-15 00:06:14 +02:00
# set output options
2016-07-19 21:51:11 +02:00
$OptionsArguments = @ {
2016-09-28 19:47:16 +02:00
CrossGen = $CrossGen
2016-07-19 21:51:11 +02:00
Output = $Output
Runtime = $Runtime
Configuration = $Configuration
Verbose = $true
SMAOnly = [ bool ] $SMAOnly
2017-11-02 21:52:17 +01:00
PSModuleRestore = $PSModuleRestore
2016-07-19 21:51:11 +02:00
}
2016-04-15 00:06:14 +02:00
$script:Options = New-PSOptions @OptionsArguments
2016-04-02 02:51:38 +02:00
2016-08-03 22:23:21 +02:00
if ( $StopDevPowerShell ) {
2016-07-20 04:09:06 +02:00
Stop-DevPowerShell
}
2016-04-15 00:06:14 +02:00
# setup arguments
2018-03-08 19:01:19 +01:00
$Arguments = @ ( " publish " , " --no-restore " , " /property:GenerateFullPaths=true " )
2018-10-30 01:43:41 +01:00
if ( $Output -or $SMAOnly ) {
$Arguments + = " --output " , ( Split-Path $Options . Output )
2016-07-19 21:51:11 +02:00
}
2016-04-15 00:06:14 +02:00
$Arguments + = " --configuration " , $Options . Configuration
$Arguments + = " --framework " , $Options . Framework
2017-06-05 23:10:42 +02:00
2018-09-10 21:47:32 +02:00
if ( -not $SMAOnly -and $Options . Runtime -ne 'fxdependent' ) {
2017-06-05 23:10:42 +02:00
# libraries should not have runtime
$Arguments + = " --runtime " , $Options . Runtime
}
2016-04-02 02:51:38 +02:00
2017-09-07 00:20:25 +02:00
if ( $ReleaseTag ) {
$ReleaseTagToUse = $ReleaseTag -Replace '^v'
$Arguments + = " /property:ReleaseTag= $ReleaseTagToUse "
}
2016-03-12 03:24:43 +01:00
# handle Restore
2018-03-22 19:22:16 +01:00
Restore-PSPackage -Options $Options -Force: $Restore
2016-02-22 00:06:39 +01:00
2016-06-15 12:42:54 +02:00
# handle ResGen
2016-06-17 16:51:32 +02:00
# Heuristic to run ResGen on the fresh machine
2016-08-03 22:23:21 +02:00
if ( $ResGen -or -not ( Test-Path " $PSScriptRoot /src/Microsoft.PowerShell.ConsoleHost/gen " ) ) {
2018-03-08 19:47:20 +01:00
Write-Log " Run ResGen (generating C# bindings for resx files) "
2016-07-16 01:52:00 +02:00
Start-ResGen
}
2016-06-15 12:42:54 +02:00
2017-11-15 19:52:06 +01:00
# Handle TypeGen
# .inc file name must be different for Windows and Linux to allow build on Windows and WSL.
$incFileName = " powershell_ $( $Options . Runtime ) .inc "
2017-11-24 11:42:41 +01:00
if ( $TypeGen -or -not ( Test-Path " $PSScriptRoot /src/TypeCatalogGen/ $incFileName " ) ) {
2018-03-08 19:47:20 +01:00
Write-Log " Run TypeGen (generating CorePsTypeCatalog.cs) "
2017-11-15 19:52:06 +01:00
Start-TypeGen -IncFileName $incFileName
2016-06-17 20:23:29 +02:00
}
2017-10-18 02:25:11 +02:00
# Get the folder path where pwsh.exe is located.
2018-10-20 20:38:15 +02:00
if ( ( Split-Path $Options . Output -Leaf ) -like " pwsh* " ) {
$publishPath = Split-Path $Options . Output -Parent
}
else {
$publishPath = $Options . Output
}
2016-04-01 23:41:13 +02:00
try {
2016-04-01 21:14:09 +02:00
# Relative paths do not work well if cwd is not changed to project
2016-04-15 00:06:14 +02:00
Push-Location $Options . Top
2018-03-08 19:47:20 +01:00
Write-Log " Run dotnet $Arguments from $pwd "
2016-04-20 01:59:03 +02:00
Start-NativeExecution { dotnet $Arguments }
2016-07-29 19:00:46 +02:00
2018-09-10 21:47:32 +02:00
if ( $CrossGen -and $Options . Runtime -ne 'fxdependent' ) {
## fxdependent package cannot be CrossGen'ed
2016-11-20 21:59:50 +01:00
Start-CrossGen -PublishPath $publishPath -Runtime $script:Options . Runtime
2018-03-08 19:47:20 +01:00
Write-Log " pwsh.exe with ngen binaries is available at: $( $Options . Output ) "
2016-08-03 22:23:21 +02:00
} else {
2018-03-08 19:47:20 +01:00
Write-Log " PowerShell output: $( $Options . Output ) "
2016-07-29 19:00:46 +02:00
}
2016-04-01 23:41:13 +02:00
} finally {
2016-04-01 21:14:09 +02:00
Pop-Location
2016-03-12 01:19:05 +01:00
}
2016-11-17 22:54:38 +01:00
2018-05-03 01:58:39 +02:00
# publish netcoreapp2.1 reference assemblies
Move powershell to .NET Core 2.0 (#3556)
This change moves powershell to .NET Core 2.0. Major changes are:
1. PowerShell assemblies are now targeting `netcoreapp2.0`. We are using `microsoft.netcore.app-2.0.0-preview1-001913-00`, which is from dotnet-core build 4/4/17. We cannot target `netstandard2.0` because the packages `System.Reflection.Emit` and `System.Reflection.Emit.Lightweight`, which are needed for powershell class, cannot be referenced when targeting `netstandard2.0`.
2. Refactor code to remove most CLR stub types and extension types.
3. Update build scripts to enable CI builds. The `-cache` section is specified to depend on `appveyor.yml`, so the cache will be invalidated if `appveyor.yml` is changed.
4. Ship `netcoreapp` reference assemblies with powershell to fix the issues in `Add-Type` (#2764). By default `Add-Type` will reference all those reference assemblies when compiling C# code. If `-ReferenceAssembly` is specified, then we search reference assemblies first, then the framework runtime assemblies, and lastly the loaded assemblies (possibly a third-party one that was already loaded).
5. `dotnet publish` generates executable on Unix platforms, but doesn't set "x" permission and thus it cannot execute. Currently, the "x" permission is set in the build script, `dotnet/cli` issue [#6286](https://github.com/dotnet/cli/issues/6286) is tracking this.
6. Replace the use of some APIs with the ones that take `SecureString`.
7. osx.10.12 is required to update to `netcoreapp2.0` because `dotnet-cli` 2.0.0-preview only works on osx.10.12.
8. Add dependency to `System.ValueTuple` to work around a ambiguous type identity issue in coreclr. The issue is tracked by `dotnet/corefx` [#17797](https://github.com/dotnet/corefx/issues/17797). When moving to newer version of `netcoreapp2.0`, we need to verify if this dependency is still needed.
2017-04-17 20:52:38 +02:00
try {
Push-Location " $PSScriptRoot /src/TypeCatalogGen "
2017-11-15 19:52:06 +01:00
$refAssemblies = Get-Content -Path $incFileName | Where-Object { $_ -like " *microsoft.netcore.app* " } | ForEach-Object { $_ . TrimEnd ( ';' ) }
Move powershell to .NET Core 2.0 (#3556)
This change moves powershell to .NET Core 2.0. Major changes are:
1. PowerShell assemblies are now targeting `netcoreapp2.0`. We are using `microsoft.netcore.app-2.0.0-preview1-001913-00`, which is from dotnet-core build 4/4/17. We cannot target `netstandard2.0` because the packages `System.Reflection.Emit` and `System.Reflection.Emit.Lightweight`, which are needed for powershell class, cannot be referenced when targeting `netstandard2.0`.
2. Refactor code to remove most CLR stub types and extension types.
3. Update build scripts to enable CI builds. The `-cache` section is specified to depend on `appveyor.yml`, so the cache will be invalidated if `appveyor.yml` is changed.
4. Ship `netcoreapp` reference assemblies with powershell to fix the issues in `Add-Type` (#2764). By default `Add-Type` will reference all those reference assemblies when compiling C# code. If `-ReferenceAssembly` is specified, then we search reference assemblies first, then the framework runtime assemblies, and lastly the loaded assemblies (possibly a third-party one that was already loaded).
5. `dotnet publish` generates executable on Unix platforms, but doesn't set "x" permission and thus it cannot execute. Currently, the "x" permission is set in the build script, `dotnet/cli` issue [#6286](https://github.com/dotnet/cli/issues/6286) is tracking this.
6. Replace the use of some APIs with the ones that take `SecureString`.
7. osx.10.12 is required to update to `netcoreapp2.0` because `dotnet-cli` 2.0.0-preview only works on osx.10.12.
8. Add dependency to `System.ValueTuple` to work around a ambiguous type identity issue in coreclr. The issue is tracked by `dotnet/corefx` [#17797](https://github.com/dotnet/corefx/issues/17797). When moving to newer version of `netcoreapp2.0`, we need to verify if this dependency is still needed.
2017-04-17 20:52:38 +02:00
$refDestFolder = Join-Path -Path $publishPath -ChildPath " ref "
if ( Test-Path $refDestFolder -PathType Container ) {
Remove-Item $refDestFolder -Force -Recurse -ErrorAction Stop
}
New-Item -Path $refDestFolder -ItemType Directory -Force -ErrorAction Stop > $null
Copy-Item -Path $refAssemblies -Destination $refDestFolder -Force -ErrorAction Stop
} finally {
Pop-Location
}
2018-08-25 00:20:22 +02:00
if ( $Environment . IsRedHatFamily -or $Environment . IsDebian9 ) {
2017-07-20 18:42:34 +02:00
# 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
2017-09-07 19:34:40 +02:00
# as Ubuntu has these specific library files in the platform and macOS builds for itself
2017-07-20 18:42:34 +02:00
# against the correct versions.
2018-08-25 00:20:22 +02:00
if ( $Environment . IsDebian9 ) {
# NOTE: Debian 8 doesn't need these symlinks
$sslTarget = " /usr/lib/x86_64-linux-gnu/libssl.so.1.0.2 "
$cryptoTarget = " /usr/lib/x86_64-linux-gnu/libcrypto.so.1.0.2 "
}
else { #IsRedHatFamily
$sslTarget = " /lib64/libssl.so.10 "
$cryptoTarget = " /lib64/libcrypto.so.10 "
}
2017-07-20 18:42:34 +02:00
if ( ! ( test-path " $publishPath /libssl.so.1.0.0 " ) ) {
2018-08-25 00:20:22 +02:00
$null = New-Item -Force -ItemType SymbolicLink -Target $sslTarget -Path " $publishPath /libssl.so.1.0.0 " -ErrorAction Stop
2017-07-20 18:42:34 +02:00
}
if ( ! ( test-path " $publishPath /libcrypto.so.1.0.0 " ) ) {
2018-08-25 00:20:22 +02:00
$null = New-Item -Force -ItemType SymbolicLink -Target $cryptoTarget -Path " $publishPath /libcrypto.so.1.0.0 " -ErrorAction Stop
2017-07-20 18:42:34 +02:00
}
}
2017-11-11 19:32:10 +01:00
if ( $Environment . IsWindows ) {
## need RCEdit to modify the binaries embedded resources
2018-08-14 19:17:29 +02:00
$rcedit = " ~/.rcedit/rcedit-x64.exe "
if ( -not ( Test-Path -Type Leaf $rcedit ) ) {
2018-10-09 08:16:50 +02:00
$rcedit = Get-Command " rcedit-x64.exe " -CommandType Application -ErrorAction SilentlyContinue | Select-Object -First 1 | ForEach-Object Name
2018-08-14 19:17:29 +02:00
}
if ( -not $rcedit ) {
2017-11-11 19:32:10 +01:00
throw " RCEdit is required to modify pwsh.exe resources, please run 'Start-PSBootStrap' to install "
}
$ReleaseVersion = " "
if ( $ReleaseTagToUse ) {
$ReleaseVersion = $ReleaseTagToUse
} else {
2017-11-15 18:41:11 +01:00
$ReleaseVersion = ( Get-PSCommitId -WarningAction SilentlyContinue ) -replace '^v'
2017-11-11 19:32:10 +01:00
}
2017-11-15 20:40:49 +01:00
# in VSCode, depending on where you started it from, the git commit id may be empty so provide a default value
if ( ! $ReleaseVersion ) {
$ReleaseVersion = " 6.0.0 "
$fileVersion = " 6.0.0 "
} else {
$fileVersion = $ReleaseVersion . Split ( " - " ) [ 0 ]
}
# in VSCode, the build output folder doesn't include the name of the exe so we have to add it for rcedit
$pwshPath = $Options . Output
if ( ! $pwshPath . EndsWith ( " pwsh.exe " ) ) {
$pwshPath = Join-Path $Options . Output " pwsh.exe "
}
2017-11-11 19:32:10 +01:00
2018-06-19 00:54:11 +02:00
if ( Test-IsPreview $ReleaseVersion ) {
$iconPath = " $PSScriptRoot \assets\Powershell_av_colors.ico "
} else {
$iconPath = " $PSScriptRoot \assets\Powershell_black.ico "
}
2018-09-10 21:47:32 +02:00
# fxdependent package does not have an executable to set iconPath etc.
if ( $Options . Runtime -ne 'fxdependent' ) {
Start-NativeExecution { & $rcedit $pwshPath - -set -icon $iconPath `
- -set -file -version $fileVersion - -set -product -version $ReleaseVersion - -set -version -string " ProductName " " PowerShell Core 6 " `
- -set -version -string " LegalCopyright " " (C) Microsoft Corporation. All Rights Reserved. " `
- -application -manifest " $PSScriptRoot \assets\pwsh.manifest " } | Write-Verbose
}
2017-11-11 19:32:10 +01:00
}
Move powershell to .NET Core 2.0 (#3556)
This change moves powershell to .NET Core 2.0. Major changes are:
1. PowerShell assemblies are now targeting `netcoreapp2.0`. We are using `microsoft.netcore.app-2.0.0-preview1-001913-00`, which is from dotnet-core build 4/4/17. We cannot target `netstandard2.0` because the packages `System.Reflection.Emit` and `System.Reflection.Emit.Lightweight`, which are needed for powershell class, cannot be referenced when targeting `netstandard2.0`.
2. Refactor code to remove most CLR stub types and extension types.
3. Update build scripts to enable CI builds. The `-cache` section is specified to depend on `appveyor.yml`, so the cache will be invalidated if `appveyor.yml` is changed.
4. Ship `netcoreapp` reference assemblies with powershell to fix the issues in `Add-Type` (#2764). By default `Add-Type` will reference all those reference assemblies when compiling C# code. If `-ReferenceAssembly` is specified, then we search reference assemblies first, then the framework runtime assemblies, and lastly the loaded assemblies (possibly a third-party one that was already loaded).
5. `dotnet publish` generates executable on Unix platforms, but doesn't set "x" permission and thus it cannot execute. Currently, the "x" permission is set in the build script, `dotnet/cli` issue [#6286](https://github.com/dotnet/cli/issues/6286) is tracking this.
6. Replace the use of some APIs with the ones that take `SecureString`.
7. osx.10.12 is required to update to `netcoreapp2.0` because `dotnet-cli` 2.0.0-preview only works on osx.10.12.
8. Add dependency to `System.ValueTuple` to work around a ambiguous type identity issue in coreclr. The issue is tracked by `dotnet/corefx` [#17797](https://github.com/dotnet/corefx/issues/17797). When moving to newer version of `netcoreapp2.0`, we need to verify if this dependency is still needed.
2017-04-17 20:52:38 +02:00
# download modules from powershell gallery.
# - PowerShellGet, PackageManagement, Microsoft.PowerShell.Archive
2018-03-21 22:24:44 +01:00
if ( $PSModuleRestore ) {
Restore-PSModuleToBuild -PublishPath $publishPath
}
# Restore the Pester module
if ( $CI ) {
Restore-PSPester -Destination ( Join-Path $publishPath " Modules " )
2017-11-13 19:10:51 +01:00
}
}
2016-11-20 21:50:06 +01:00
2018-03-22 19:22:16 +01:00
function Restore-PSPackage
{
param (
[ ValidateNotNullOrEmpty ( ) ]
[ Parameter ( ) ]
[ string[] ] $ProjectDirs ,
[ ValidateNotNullOrEmpty ( ) ]
[ Parameter ( ) ]
$Options = ( Get-PSOptions -DefaultToNew ) ,
[ switch ] $Force
)
if ( -not $ProjectDirs )
{
$ProjectDirs = @ ( $Options . Top , " $PSScriptRoot /src/TypeCatalogGen " , " $PSScriptRoot /src/ResGen " , " $PSScriptRoot /src/Modules " )
}
if ( $Force -or ( -not ( Test-Path " $( $Options . Top ) /obj/project.assets.json " ) ) ) {
2018-09-10 21:47:32 +02:00
if ( $Options . Runtime -ne 'fxdependent' ) {
$RestoreArguments = @ ( " --runtime " , $Options . Runtime , " --verbosity " )
} else {
$RestoreArguments = @ ( " --verbosity " )
}
2018-03-22 19:22:16 +01:00
if ( $PSCmdlet . MyInvocation . BoundParameters [ " Verbose " ] . IsPresent ) {
$RestoreArguments + = " detailed "
} else {
$RestoreArguments + = " quiet "
}
$ProjectDirs | ForEach-Object {
2018-08-17 02:24:41 +02:00
$project = $_
Write-Log " Run dotnet restore $project $RestoreArguments "
2018-08-16 23:23:30 +02:00
$retryCount = 0
$maxTries = 5
while ( $retryCount -lt $maxTries )
{
try
{
2018-08-17 02:24:41 +02:00
Start-NativeExecution { dotnet restore $project $RestoreArguments }
2018-08-16 23:23:30 +02:00
}
catch
{
2018-08-17 02:24:41 +02:00
Write-Log " Failed to restore $project , retrying... "
2018-08-16 23:23:30 +02:00
$retryCount + +
if ( $retryCount -ge $maxTries )
{
throw
}
2018-08-17 02:24:41 +02:00
continue
2018-08-16 23:23:30 +02:00
}
2018-08-17 02:24:41 +02:00
Write-Log " Done restoring $project "
2018-08-16 23:23:30 +02:00
break
}
2018-03-22 19:22:16 +01:00
}
}
}
2017-11-13 19:10:51 +01:00
function Restore-PSModuleToBuild
{
param (
[ Parameter ( Mandatory ) ]
[ string ]
2018-03-21 22:24:44 +01:00
$PublishPath
2017-11-13 19:10:51 +01:00
)
2016-11-20 21:50:06 +01:00
2018-03-08 19:47:20 +01:00
Write-Log " Restore PowerShell modules to $publishPath "
2017-11-13 19:10:51 +01:00
$modulesDir = Join-Path -Path $publishPath -ChildPath " Modules "
2018-11-30 20:49:38 +01:00
Copy-PSGalleryModules -Destination $modulesDir -CsProjPath " $PSScriptRoot \src\Modules\PSGalleryModules.csproj "
2017-12-05 02:16:53 +01:00
}
2017-12-13 01:07:12 +01:00
function Restore-PSPester
{
2018-02-07 22:44:29 +01:00
param (
2017-12-13 01:07:12 +01:00
[ ValidateNotNullOrEmpty ( ) ]
2018-02-07 22:44:29 +01:00
[ string ] $Destination = ( [ IO.Path ] :: Combine ( ( Split-Path ( Get-PSOptions -DefaultToNew ) . Output ) , " Modules " ) )
2017-12-13 01:07:12 +01:00
)
2018-11-15 22:26:11 +01:00
Save-Module -Name Pester -Path $Destination -Repository PSGallery -RequiredVersion " 4.4.1 "
2017-12-13 01:07:12 +01:00
}
2018-02-07 22:44:29 +01:00
2016-09-29 01:20:29 +02:00
function Compress-TestContent {
[ CmdletBinding ( ) ]
param (
$Destination
)
2017-10-02 21:14:19 +02:00
$null = Publish-PSTestTools
2017-06-06 02:37:48 +02:00
$powerShellTestRoot = Join-Path $PSScriptRoot 'test'
2016-09-29 01:20:29 +02:00
Add-Type -AssemblyName System . IO . Compression . FileSystem
$resolvedPath = $ExecutionContext . SessionState . Path . GetUnresolvedProviderPathFromPSPath ( $Destination )
2017-01-16 22:31:14 +01:00
[ System.IO.Compression.ZipFile ] :: CreateFromDirectory ( $powerShellTestRoot , $resolvedPath )
2016-09-29 01:20:29 +02:00
}
2016-04-15 00:06:14 +02:00
function New-PSOptions {
[ CmdletBinding ( ) ]
param (
2018-05-15 00:01:44 +02:00
[ ValidateSet ( " Debug " , " Release " , " CodeCoverage " , '' ) ]
2016-04-15 00:06:14 +02:00
[ string ] $Configuration ,
2018-05-03 01:58:39 +02:00
[ ValidateSet ( " netcoreapp2.1 " ) ]
2016-04-15 00:06:14 +02:00
[ string ] $Framework ,
# These are duplicated from Start-PSBuild
# We do not use ValidateScript since we want tab completion
[ ValidateSet ( " " ,
2018-12-05 19:55:09 +01:00
" alpine-x64 " ,
2018-09-10 21:47:32 +02:00
" fxdependent " ,
2017-12-09 00:43:53 +01:00
" linux-arm " ,
2018-12-05 19:55:09 +01:00
" linux-arm64 " ,
2018-06-28 00:30:38 +02:00
" linux-x64 " ,
" osx-x64 " ,
2017-12-09 00:43:53 +01:00
" win-arm " ,
2018-06-28 00:30:38 +02:00
" win-arm64 " ,
" win7-x64 " ,
" win7-x86 " ) ]
2016-04-15 00:06:14 +02:00
[ string ] $Runtime ,
2016-09-28 19:47:16 +02:00
[ switch ] $CrossGen ,
2018-10-30 01:43:41 +01:00
# Accept a path to the output directory
# If not null or empty, name of the executable will be appended to
# this path, otherwise, to the default path, and then the full path
# of the output executable will be assigned to the Output property
2016-05-06 19:28:45 +02:00
[ string ] $Output ,
2016-04-15 00:06:14 +02:00
2017-11-02 21:52:17 +01:00
[ switch ] $SMAOnly ,
2017-11-09 18:23:00 +01:00
2017-11-02 21:52:17 +01:00
[ switch ] $PSModuleRestore
2016-04-15 00:06:14 +02:00
)
2016-06-20 20:59:14 +02:00
# Add .NET CLI tools to PATH
Find-Dotnet
2016-09-28 19:47:16 +02:00
$ConfigWarningMsg = " The passed-in Configuration value '{0}' is not supported on '{1}'. Use '{2}' instead. "
2016-04-15 00:06:14 +02:00
if ( -not $Configuration ) {
2018-05-15 00:01:44 +02:00
$Configuration = 'Debug'
2016-09-28 19:47:16 +02:00
} else {
switch ( $Configuration ) {
2016-11-11 20:12:07 +01:00
" CodeCoverage " {
2017-07-23 21:29:33 +02:00
if ( -not $Environment . IsWindows ) {
2018-05-15 00:01:44 +02:00
$Configuration = " Debug "
2017-07-23 21:29:33 +02:00
Write-Warning ( $ConfigWarningMsg -f $switch . Current , $Environment . LinuxInfo . PRETTY_NAME , $Configuration )
2016-09-28 19:47:16 +02:00
}
}
}
2016-04-15 00:06:14 +02:00
}
2016-09-28 19:47:16 +02:00
Write-Verbose " Using configuration ' $Configuration ' "
2016-04-15 00:06:14 +02:00
2018-05-15 00:01:44 +02:00
$PowerShellDir = if ( ! $Environment . IsWindows ) {
2016-09-28 19:47:16 +02:00
" powershell-unix "
2016-07-22 03:47:46 +02:00
} else {
2016-09-28 19:47:16 +02:00
" powershell-win-core "
2016-07-22 03:47:46 +02:00
}
2016-09-28 19:47:16 +02:00
$Top = [ IO.Path ] :: Combine ( $PSScriptRoot , " src " , $PowerShellDir )
2016-07-22 03:47:46 +02:00
Write-Verbose " Top project directory is $Top "
2016-04-15 00:06:14 +02:00
if ( -not $Framework ) {
2018-05-03 01:58:39 +02:00
$Framework = " netcoreapp2.1 "
2016-07-01 21:09:50 +02:00
Write-Verbose " Using framework ' $Framework ' "
2016-04-15 00:06:14 +02:00
}
if ( -not $Runtime ) {
2017-09-18 18:31:07 +02:00
if ( $Environment . IsLinux ) {
$Runtime = " linux-x64 "
2018-05-03 01:58:39 +02:00
} elseif ( $Environment . IsMacOS ) {
$Runtime = " osx-x64 "
2017-09-18 18:31:07 +02:00
} else {
$RID = dotnet - -info | ForEach-Object {
if ( $_ -match " RID " ) {
$_ -split " \s+ " | Select-Object -Last 1
}
2016-04-15 00:06:14 +02:00
}
2018-05-03 01:58:39 +02:00
# We plan to release packages targetting win7-x64 and win7-x86 RIDs,
# which supports all supported windows platforms.
# So we, will change the RID to win7-<arch>
$Runtime = $RID -replace " win\d+ " , " win7 "
2017-08-30 02:21:50 +02:00
}
2016-04-15 00:06:14 +02:00
if ( -not $Runtime ) {
Throw " Could not determine Runtime Identifier, please update dotnet "
} else {
2016-07-01 21:09:50 +02:00
Write-Verbose " Using runtime ' $Runtime ' "
2016-04-15 00:06:14 +02:00
}
2016-04-02 07:01:37 +02:00
}
2018-09-10 21:47:32 +02:00
$Executable = if ( $Runtime -eq 'fxdependent' ) {
" pwsh.dll "
} elseif ( $Environment . IsLinux -or $Environment . IsMacOS ) {
2017-10-18 02:25:11 +02:00
" pwsh "
2017-07-23 21:29:33 +02:00
} elseif ( $Environment . IsWindows ) {
2017-10-18 02:25:11 +02:00
" pwsh.exe "
2016-04-15 00:06:14 +02:00
}
2016-05-06 19:28:45 +02:00
# Build the Output path
2016-10-03 20:04:40 +02:00
if ( ! $Output ) {
2018-09-10 21:47:32 +02:00
if ( $Runtime -eq 'fxdependent' ) {
$Output = [ IO.Path ] :: Combine ( $Top , " bin " , $Configuration , $Framework , " publish " , $Executable )
} else {
$Output = [ IO.Path ] :: Combine ( $Top , " bin " , $Configuration , $Framework , $Runtime , " publish " , $Executable )
}
2018-10-30 01:43:41 +01:00
} else {
$Output = [ IO.Path ] :: Combine ( $Output , $Executable )
2016-05-06 19:28:45 +02:00
}
2016-04-15 00:06:14 +02:00
2016-07-19 21:51:11 +02:00
if ( $SMAOnly )
{
2016-09-28 19:47:16 +02:00
$Top = [ IO.Path ] :: Combine ( $PSScriptRoot , " src " , " System.Management.Automation " )
2016-07-19 21:51:11 +02:00
}
2017-08-16 01:17:45 +02:00
$RootInfo = @ { RepoPath = $PSScriptRoot }
# the valid root is the root of the filesystem and the folder PowerShell
$RootInfo [ 'ValidPath' ] = Join-Path -Path ( [ system.io.path ] :: GetPathRoot ( $RootInfo . RepoPath ) ) -ChildPath 'PowerShell'
2017-08-22 00:23:52 +02:00
2017-08-17 01:07:18 +02:00
if ( $RootInfo . RepoPath -ne $RootInfo . ValidPath )
2017-08-16 01:17:45 +02:00
{
2017-09-11 21:36:24 +02:00
$RootInfo [ 'Warning' ] = " Please ensure your repo is at the root of the file system and named 'PowerShell' (example: ' $( $RootInfo . ValidPath ) '), when building and packaging for release! "
2017-08-16 01:17:45 +02:00
$RootInfo [ 'IsValid' ] = $false
}
2017-08-22 00:23:52 +02:00
else
2017-08-16 01:17:45 +02:00
{
$RootInfo [ 'IsValid' ] = $true
}
2018-08-18 07:06:51 +02:00
return New-PSOptionsObject `
-RootInfo ( [ PSCustomObject ] $RootInfo ) `
-Top $Top `
-Runtime $Runtime `
-Crossgen $Crossgen . IsPresent `
-Configuration $Configuration `
-PSModuleRestore $PSModuleRestore . IsPresent `
-Framework $Framework `
-Output $Output
2016-04-15 00:06:14 +02:00
}
2017-07-23 21:29:33 +02:00
# Get the Options of the last build
function Get-PSOptions {
2017-12-13 01:07:12 +01:00
param (
2018-11-09 04:33:24 +01:00
[ Parameter ( HelpMessage = 'Defaults to New-PSOption if a build has not occurred.' ) ]
2017-12-13 01:07:12 +01:00
[ switch ]
$DefaultToNew
)
if ( ! $script:Options -and $DefaultToNew . IsPresent )
{
return New-PSOptions
}
2017-07-23 21:29:33 +02:00
return $script:Options
}
2017-11-13 19:10:51 +01:00
function Set-PSOptions {
param (
[ PSObject ]
$Options
)
$script:Options = $Options
}
2016-04-15 00:06:14 +02:00
function Get-PSOutput {
[ CmdletBinding ( ) ] param (
[ hashtable ] $Options
)
if ( $Options ) {
return $Options . Output
} elseif ( $script:Options ) {
return $script:Options . Output
} else {
return ( New-PSOptions ) . Output
}
2016-02-22 00:06:39 +01:00
}
2016-07-27 02:41:03 +02:00
function Get-PesterTag {
param ( [ Parameter ( Position = 0 ) ] [ string ] $testbase = " $PSScriptRoot /test/powershell " )
$alltags = @ { }
$warnings = @ ( )
2017-07-22 06:03:49 +02:00
get-childitem -Recurse $testbase -File | Where-Object { $_ . name -match " tests.ps1 " } | ForEach-Object {
2016-07-27 02:41:03 +02:00
$fullname = $_ . fullname
$tok = $err = $null
$ast = [ System.Management.Automation.Language.Parser ] :: ParseFile ( $FullName , [ ref ] $tok , [ ref ] $err )
$des = $ast . FindAll ( { $args [ 0 ] -is " System.Management.Automation.Language.CommandAst " -and $args [ 0 ] . CommandElements [ 0 ] . Value -eq " Describe " } , $true )
foreach ( $describe in $des ) {
$elements = $describe . CommandElements
$lineno = $elements [ 0 ] . Extent . StartLineNumber
2016-09-27 03:12:11 +02:00
$foundPriorityTags = @ ( )
2016-07-27 02:41:03 +02:00
for ( $i = 0 ; $i -lt $elements . Count ; $i + + ) {
if ( $elements [ $i ] . extent . text -match " ^-t " ) {
$vAst = $elements [ $i + 1 ]
if ( $vAst . FindAll ( { $args [ 0 ] -is " System.Management.Automation.Language.VariableExpressionAst " } , $true ) ) {
$warnings + = " TAGS must be static strings, error in ${fullname} , line $lineno "
}
$values = $vAst . FindAll ( { $args [ 0 ] -is " System.Management.Automation.Language.StringConstantExpressionAst " } , $true ) . Value
2017-07-22 06:03:49 +02:00
$values | ForEach-Object {
2018-02-16 20:13:23 +01:00
if ( @ ( 'REQUIREADMINONWINDOWS' , 'REQUIRESUDOONUNIX' , 'SLOW' ) -contains $_ ) {
2016-09-16 04:33:54 +02:00
# These are valid tags also, but they are not the priority tags
}
elseif ( @ ( 'CI' , 'FEATURE' , 'SCENARIO' ) -contains $_ ) {
2016-09-27 03:12:11 +02:00
$foundPriorityTags + = $_
2016-09-16 04:33:54 +02:00
}
else {
2016-07-27 02:41:03 +02:00
$warnings + = " ${fullname} includes improper tag ' $_ ', line ' $lineno ' "
}
2016-09-16 04:33:54 +02:00
2016-08-03 22:23:21 +02:00
$alltags [ $_ ] + +
2016-09-16 04:33:54 +02:00
}
2016-07-27 02:41:03 +02:00
}
}
2016-09-27 03:12:11 +02:00
if ( $foundPriorityTags . Count -eq 0 ) {
$warnings + = " ${fullname} : $lineno does not include -Tag in Describe "
}
elseif ( $foundPriorityTags . Count -gt 1 ) {
$warnings + = " ${fullname} : $lineno includes more then one scope -Tag: $foundPriorityTags "
2016-07-27 02:41:03 +02:00
}
}
}
if ( $Warnings . Count -gt 0 ) {
$alltags [ 'Result' ] = " Fail "
}
else {
$alltags [ 'Result' ] = " Pass "
}
$alltags [ 'Warnings' ] = $warnings
2016-08-03 22:23:21 +02:00
$o = [ pscustomobject ] $alltags
2016-07-27 02:41:03 +02:00
$o . psobject . TypeNames . Add ( " DescribeTagsInUse " )
$o
}
2016-09-12 19:39:42 +02:00
function Publish-PSTestTools {
2016-09-12 08:14:30 +02:00
[ CmdletBinding ( ) ]
param ( )
Find-Dotnet
2017-06-14 02:49:56 +02:00
$tools = @ (
@ { Path = " ${PSScriptRoot} /test/tools/TestExe " ; Output = " testexe " }
2017-08-31 11:30:35 +02:00
@ { Path = " ${PSScriptRoot} /test/tools/WebListener " ; Output = " WebListener " }
2018-08-28 20:03:12 +02:00
@ { Path = " ${PSScriptRoot} /test/tools/TestService " ; Output = " TestService " }
2017-06-14 02:49:56 +02:00
)
2017-12-13 01:07:12 +01:00
$Options = Get-PSOptions -DefaultToNew
2016-10-31 17:41:13 +01:00
2017-06-14 02:49:56 +02:00
# Publish tools so it can be run by tests
2016-10-31 17:41:13 +01:00
foreach ( $tool in $tools )
{
2017-06-14 02:49:56 +02:00
Push-Location $tool . Path
2016-10-31 17:41:13 +01:00
try {
2017-03-23 21:04:52 +01:00
dotnet publish - -output bin - -configuration $Options . Configuration - -framework $Options . Framework - -runtime $Options . Runtime
2017-06-14 02:49:56 +02:00
$toolPath = Join-Path -Path $tool . Path -ChildPath " bin "
Move powershell to .NET Core 2.0 (#3556)
This change moves powershell to .NET Core 2.0. Major changes are:
1. PowerShell assemblies are now targeting `netcoreapp2.0`. We are using `microsoft.netcore.app-2.0.0-preview1-001913-00`, which is from dotnet-core build 4/4/17. We cannot target `netstandard2.0` because the packages `System.Reflection.Emit` and `System.Reflection.Emit.Lightweight`, which are needed for powershell class, cannot be referenced when targeting `netstandard2.0`.
2. Refactor code to remove most CLR stub types and extension types.
3. Update build scripts to enable CI builds. The `-cache` section is specified to depend on `appveyor.yml`, so the cache will be invalidated if `appveyor.yml` is changed.
4. Ship `netcoreapp` reference assemblies with powershell to fix the issues in `Add-Type` (#2764). By default `Add-Type` will reference all those reference assemblies when compiling C# code. If `-ReferenceAssembly` is specified, then we search reference assemblies first, then the framework runtime assemblies, and lastly the loaded assemblies (possibly a third-party one that was already loaded).
5. `dotnet publish` generates executable on Unix platforms, but doesn't set "x" permission and thus it cannot execute. Currently, the "x" permission is set in the build script, `dotnet/cli` issue [#6286](https://github.com/dotnet/cli/issues/6286) is tracking this.
6. Replace the use of some APIs with the ones that take `SecureString`.
7. osx.10.12 is required to update to `netcoreapp2.0` because `dotnet-cli` 2.0.0-preview only works on osx.10.12.
8. Add dependency to `System.ValueTuple` to work around a ambiguous type identity issue in coreclr. The issue is tracked by `dotnet/corefx` [#17797](https://github.com/dotnet/corefx/issues/17797). When moving to newer version of `netcoreapp2.0`, we need to verify if this dependency is still needed.
2017-04-17 20:52:38 +02:00
2018-05-08 18:20:39 +02:00
if ( -not $env:PATH . Contains ( $toolPath ) ) {
2017-06-14 02:49:56 +02:00
$env:PATH = $toolPath + $TestModulePathSeparator + $ ( $env:PATH )
}
2016-10-31 17:41:13 +01:00
} finally {
Pop-Location
}
2016-09-12 08:14:30 +02:00
}
2018-10-01 23:40:48 +02:00
2018-11-30 20:49:38 +01:00
# `dotnet restore` on test project is not called if product projects have been restored unless -Force is specified.
Copy-PSGalleryModules -Destination " ${PSScriptRoot} /test/tools/Modules " -CsProjPath " $PSScriptRoot /test/tools/Modules/PSGalleryTestModules.csproj " -Force
2016-09-12 08:14:30 +02:00
}
2018-08-03 20:20:14 +02:00
function Get-ExperimentalFeatureTests {
$testMetadataFile = Join-Path $PSScriptRoot " test/tools/TestMetadata.json "
$metadata = Get-Content -Path $testMetadataFile -Raw | ConvertFrom-Json | ForEach-Object -MemberName ExperimentalFeatures
$features = $metadata | Get-Member -MemberType NoteProperty | ForEach-Object -MemberName Name
$featureTests = @ { }
foreach ( $featureName in $features ) {
$featureTests [ $featureName ] = $metadata . $featureName
}
$featureTests
}
2016-04-02 07:01:45 +02:00
function Start-PSPester {
2017-08-26 17:26:06 +02:00
[ CmdletBinding ( DefaultParameterSetName = 'default' ) ]
2016-08-31 21:13:05 +02:00
param (
2018-07-03 20:16:37 +02:00
[ Parameter ( Position = 0 ) ]
[ string[] ] $Path = @ ( " $PSScriptRoot /test/common " , " $PSScriptRoot /test/powershell " ) ,
2016-07-26 02:43:28 +02:00
[ string ] $OutputFormat = " NUnitXml " ,
[ string ] $OutputFile = " pester-tests.xml " ,
2016-09-16 04:22:18 +02:00
[ string[] ] $ExcludeTag = 'Slow' ,
2017-07-18 23:20:14 +02:00
[ string[] ] $Tag = @ ( " CI " , " Feature " ) ,
2016-08-30 20:47:27 +02:00
[ switch ] $ThrowOnFailure ,
2018-08-03 20:20:14 +02:00
[ string ] $BinDir = ( Split-Path ( Get-PSOptions -DefaultToNew ) . Output ) ,
[ string ] $powershell = ( Join-Path $BinDir 'pwsh' ) ,
[ string ] $Pester = ( [ IO.Path ] :: Combine ( $BinDir , " Modules " , " Pester " ) ) ,
2017-08-26 17:26:06 +02:00
[ Parameter ( ParameterSetName = 'Unelevate' , Mandatory = $true ) ]
2016-12-01 20:23:14 +01:00
[ switch ] $Unelevate ,
2017-03-06 22:13:34 +01:00
[ switch ] $Quiet ,
2017-09-19 23:56:32 +02:00
[ switch ] $Terse ,
2017-08-26 17:26:06 +02:00
[ Parameter ( ParameterSetName = 'PassThru' , Mandatory = $true ) ]
2017-09-13 23:14:08 +02:00
[ switch ] $PassThru ,
2018-02-16 20:13:23 +01:00
[ Parameter ( ParameterSetName = 'PassThru' , HelpMessage = 'Run commands on Linux with sudo.' ) ]
[ switch ] $Sudo ,
2018-08-03 20:20:14 +02:00
[ switch ] $IncludeFailingTest ,
2018-08-15 01:23:50 +02:00
[ string ] $ExperimentalFeatureName ,
[ Parameter ( HelpMessage = 'Title to publish the results as.' ) ]
[ string ] $Title = 'PowerShell Core Tests'
2016-04-02 07:01:45 +02:00
)
2016-07-27 01:01:31 +02:00
2018-07-03 20:16:37 +02:00
if ( -not ( Get-Module -ListAvailable -Name $Pester -ErrorAction SilentlyContinue | Where-Object { $_ . Version -ge " 4.2 " } ) )
2017-10-10 21:37:17 +02:00
{
2018-11-30 20:49:38 +01:00
Restore-PSPester
2017-10-10 21:37:17 +02:00
}
2017-09-13 23:14:08 +02:00
if ( $IncludeFailingTest . IsPresent )
{
$Path + = " $PSScriptRoot /tools/failingTests "
}
2017-09-18 18:31:07 +02:00
2016-09-16 04:22:18 +02:00
# we need to do few checks and if user didn't provide $ExcludeTag explicitly, we should alternate the default
if ( $Unelevate )
{
2017-07-23 21:29:33 +02:00
if ( -not $Environment . IsWindows )
2016-09-16 04:22:18 +02:00
{
throw '-Unelevate is currently not supported on non-Windows platforms'
}
2017-07-23 21:29:33 +02:00
if ( -not $Environment . IsAdmin )
2016-09-16 04:22:18 +02:00
{
throw '-Unelevate cannot be applied because the current user is not Administrator'
}
2016-09-26 22:15:58 +02:00
if ( -not $PSBoundParameters . ContainsKey ( 'ExcludeTag' ) )
2016-09-16 04:22:18 +02:00
{
$ExcludeTag + = 'RequireAdminOnWindows'
}
}
2017-07-23 21:29:33 +02:00
elseif ( $Environment . IsWindows -and ( -not $Environment . IsAdmin ) )
2016-09-16 04:22:18 +02:00
{
2016-09-26 22:15:58 +02:00
if ( -not $PSBoundParameters . ContainsKey ( 'ExcludeTag' ) )
2016-09-16 04:22:18 +02:00
{
$ExcludeTag + = 'RequireAdminOnWindows'
}
}
2018-02-27 18:23:05 +01:00
elseif ( -not $Environment . IsWindows -and ( -not $Sudo . IsPresent ) )
{
if ( -not $PSBoundParameters . ContainsKey ( 'ExcludeTag' ) )
{
$ExcludeTag + = 'RequireSudoOnUnix'
}
}
elseif ( -not $Environment . IsWindows -and $Sudo . IsPresent )
{
if ( -not $PSBoundParameters . ContainsKey ( 'Tag' ) )
{
$Tag = 'RequireSudoOnUnix'
}
}
2016-09-16 04:22:18 +02:00
2016-08-31 21:13:05 +02:00
Write-Verbose " Running pester tests at ' $path ' with tag ' $( $Tag -join ''', ''' ) ' and ExcludeTag ' $( $ExcludeTag -join ''', ''' ) ' " -Verbose
2017-08-26 17:26:06 +02:00
Publish-PSTestTools | ForEach-Object { Write-Host $_ }
2017-03-27 06:46:39 +02:00
2016-08-09 21:03:30 +02:00
# All concatenated commands/arguments are suffixed with the delimiter (space)
2018-04-13 19:29:39 +02:00
# Disable telemetry for all startups of pwsh in tests
$command = " `$ env:POWERSHELL_TELEMETRY_OPTOUT = 1; "
2017-09-19 23:56:32 +02:00
if ( $Terse )
{
2018-02-16 20:13:23 +01:00
$command + = " `$ ProgressPreference = 'silentlyContinue'; "
2017-09-19 23:56:32 +02:00
}
2016-08-09 21:03:30 +02:00
2017-03-27 06:46:39 +02:00
# Autoload (in subprocess) temporary modules used in our tests
2018-11-30 20:49:38 +01:00
$newPathFragment = $TestModulePath + $TestModulePathSeparator
2018-10-01 23:40:48 +02:00
$command + = '$env:PSModulePath = ' + " ' $newPathFragment ' " + '+$env:PSModulePath;'
2017-03-27 06:46:39 +02:00
2016-08-09 21:03:30 +02:00
# Windows needs the execution policy adjusted
2017-07-23 21:29:33 +02:00
if ( $Environment . IsWindows ) {
2018-02-16 20:13:23 +01:00
$command + = " Set-ExecutionPolicy -Scope Process Unrestricted; "
2016-08-09 21:03:30 +02:00
}
2018-02-16 20:13:23 +01:00
$command + = " Import-Module ' $Pester '; "
2016-09-16 04:22:18 +02:00
if ( $Unelevate )
{
$outputBufferFilePath = [ System.IO.Path ] :: GetTempFileName ( )
}
2017-01-16 22:31:14 +01:00
2018-02-16 20:13:23 +01:00
$command + = " Invoke-Pester "
2016-08-09 21:03:30 +02:00
2018-02-16 20:13:23 +01:00
$command + = " -OutputFormat ${OutputFormat} -OutputFile ${OutputFile} "
2016-08-09 21:03:30 +02:00
if ( $ExcludeTag -and ( $ExcludeTag -ne " " ) ) {
2018-02-16 20:13:23 +01:00
$command + = " -ExcludeTag @(' " + ( $ { ExcludeTag } -join " ',' " ) + " ') "
2016-08-05 01:46:44 +02:00
}
2016-08-09 21:03:30 +02:00
if ( $Tag ) {
2018-02-16 20:13:23 +01:00
$command + = " -Tag @(' " + ( $ { Tag } -join " ',' " ) + " ') "
2016-08-09 21:03:30 +02:00
}
2016-12-01 20:23:14 +01:00
# sometimes we need to eliminate Pester output, especially when we're
# doing a daily build as the log file is too large
if ( $Quiet ) {
2018-02-16 20:13:23 +01:00
$command + = " -Quiet "
2016-12-01 20:23:14 +01:00
}
2017-03-06 22:13:34 +01:00
if ( $PassThru ) {
2018-02-16 20:13:23 +01:00
$command + = " -PassThru "
2017-03-06 22:13:34 +01:00
}
2016-08-09 21:03:30 +02:00
2018-02-16 20:13:23 +01:00
$command + = " ' " + ( $Path -join " ',' " ) + " ' "
2016-09-16 04:22:18 +02:00
if ( $Unelevate )
{
2018-02-16 20:13:23 +01:00
$command + = " *> $outputBufferFilePath ; '__UNELEVATED_TESTS_THE_END__' >> $outputBufferFilePath "
2016-09-16 04:22:18 +02:00
}
2018-02-16 20:13:23 +01:00
Write-Verbose $command
2016-08-30 20:47:27 +02:00
2017-09-19 23:56:32 +02:00
$script:nonewline = $true
$script:inerror = $false
function Write-Terse([string ] $line )
{
$trimmedline = $line . Trim ( )
if ( $trimmedline . StartsWith ( " [+] " ) ) {
Write-Host " + " -NoNewline -ForegroundColor Green
$script:nonewline = $true
$script:inerror = $false
}
elseif ( $trimmedline . StartsWith ( " [?] " ) ) {
Write-Host " ? " -NoNewline -ForegroundColor Cyan
$script:nonewline = $true
$script:inerror = $false
}
elseif ( $trimmedline . StartsWith ( " [!] " ) ) {
Write-Host " ! " -NoNewline -ForegroundColor Gray
$script:nonewline = $true
$script:inerror = $false
}
2018-03-08 19:17:23 +01:00
elseif ( $trimmedline . StartsWith ( " Executing script " ) ) {
# Skip lines where Pester reports that is executing a test script
return
}
2018-02-27 01:57:27 +01:00
elseif ( $trimmedline -match " ^\d+(\.\d+)?m?s $ " ) {
# Skip the time elapse like '12ms', '1ms', '1.2s' and '12.53s'
return
}
2017-09-19 23:56:32 +02:00
else {
if ( $script:nonewline ) {
Write-Host " `n " -NoNewline
}
if ( $trimmedline . StartsWith ( " [-] " ) -or $script:inerror ) {
Write-Host $line -ForegroundColor Red
$script:inerror = $true
}
elseif ( $trimmedline . StartsWith ( " VERBOSE: " ) ) {
Write-Host $line -ForegroundColor Yellow
$script:inerror = $false
}
elseif ( $trimmedline . StartsWith ( " Describing " ) -or $trimmedline . StartsWith ( " Context " ) ) {
Write-Host $line -ForegroundColor Magenta
$script:inerror = $false
}
else {
Write-Host $line -ForegroundColor Gray
}
$script:nonewline = $false
}
}
2018-08-03 20:20:14 +02:00
$PSFlags = @ ( " -noprofile " )
if ( -not [ string ] :: IsNullOrEmpty ( $ExperimentalFeatureName ) ) {
$configFile = [ System.IO.Path ] :: GetTempFileName ( )
$configFile = [ System.IO.Path ] :: ChangeExtension ( $configFile , " .json " )
## Create the config.json file to enable the given experimental feature.
## On Windows, we need to have 'RemoteSigned' declared for ExecutionPolicy because the ExecutionPolicy is 'Restricted' by default.
## On Unix, ExecutionPolicy is not supported, so we don't need to declare it.
if ( $Environment . IsWindows ) {
$content = @"
{
" Microsoft.PowerShell:ExecutionPolicy " : " RemoteSigned " ,
" ExperimentalFeatures " : [
" $ExperimentalFeatureName "
]
}
" @
} else {
$content = @"
{
" ExperimentalFeatures " : [
" $ExperimentalFeatureName "
]
}
" @
}
Set-Content -Path $configFile -Value $content -Encoding Ascii -Force
$PSFlags = @ ( " -settings " , $configFile , " -noprofile " )
}
2016-08-09 21:03:30 +02:00
# To ensure proper testing, the module path must not be inherited by the spawned process
2017-08-16 21:07:45 +02:00
try {
$originalModulePath = $env:PSModulePath
2018-04-13 19:29:39 +02:00
$originalTelemetry = $env:POWERSHELL_TELEMETRY_OPTOUT
$env:POWERSHELL_TELEMETRY_OPTOUT = 1
2017-08-16 21:07:45 +02:00
if ( $Unelevate )
{
2018-08-03 20:20:14 +02:00
Start-UnelevatedProcess -process $powershell -arguments ( $PSFlags + " -c $Command " )
2017-08-16 21:07:45 +02:00
$currentLines = 0
while ( $true )
2016-09-16 04:22:18 +02:00
{
2017-08-16 21:07:45 +02:00
$lines = Get-Content $outputBufferFilePath | Select-Object -Skip $currentLines
2017-09-19 23:56:32 +02:00
if ( $Terse )
{
foreach ( $line in $lines )
{
Write-Terse -line $line
}
}
else
{
$lines | Write-Host
}
2017-08-16 21:07:45 +02:00
if ( $lines | Where-Object { $_ -eq '__UNELEVATED_TESTS_THE_END__' } )
2016-09-16 04:22:18 +02:00
{
2017-08-16 21:07:45 +02:00
break
}
2016-09-16 04:22:18 +02:00
2017-08-16 21:07:45 +02:00
$count = ( $lines | measure-object ) . Count
if ( $count -eq 0 )
{
2017-08-22 00:23:52 +02:00
Start-Sleep -Seconds 1
2017-08-16 21:07:45 +02:00
}
else
{
$currentLines + = $count
2016-09-16 04:22:18 +02:00
}
}
2017-01-16 22:31:14 +01:00
}
2017-08-16 21:07:45 +02:00
else
{
2017-08-26 17:26:06 +02:00
if ( $PassThru . IsPresent )
{
$passThruFile = [ System.IO.Path ] :: GetTempFileName ( )
2017-09-11 21:16:58 +02:00
try
2017-08-26 17:26:06 +02:00
{
2018-08-03 20:20:14 +02:00
$command + = " | Export-Clixml -Path ' $passThruFile ' -Force "
2018-02-16 20:13:23 +01:00
2018-08-03 20:20:14 +02:00
$passThruCommand = { & $powershell $PSFlags -c $command }
2018-02-16 20:13:23 +01:00
if ( $Sudo . IsPresent ) {
2018-08-15 01:23:50 +02:00
# -E says to preserve the environment
$passThruCommand = { & sudo -E $powershell $PSFlags -c $command }
2017-09-19 23:56:32 +02:00
}
2018-02-16 20:13:23 +01:00
$writeCommand = { Write-Host $_ }
if ( $Terse )
2017-09-19 23:56:32 +02:00
{
2018-02-16 20:13:23 +01:00
$writeCommand = { Write-Terse $_ }
2017-09-19 23:56:32 +02:00
}
2018-02-16 20:13:23 +01:00
Start-NativeExecution -sb $passThruCommand | ForEach-Object $writeCommand
2017-08-26 17:26:06 +02:00
Import-Clixml -Path $passThruFile | Where-Object { $_ . TotalCount -is [ Int32 ] }
}
finally
{
2018-02-16 20:13:23 +01:00
Remove-Item $passThruFile -ErrorAction SilentlyContinue -Force
2017-08-26 17:26:06 +02:00
}
}
2017-09-11 21:16:58 +02:00
else
2017-08-26 17:26:06 +02:00
{
2017-09-19 23:56:32 +02:00
if ( $Terse )
{
2018-08-03 20:20:14 +02:00
Start-NativeExecution -sb { & $powershell $PSFlags -c $command } | ForEach-Object { Write-Terse -line $_ }
2017-09-19 23:56:32 +02:00
}
else
{
2018-08-03 20:20:14 +02:00
Start-NativeExecution -sb { & $powershell $PSFlags -c $command }
2017-09-19 23:56:32 +02:00
}
2017-08-26 17:26:06 +02:00
}
2017-08-16 21:07:45 +02:00
}
} finally {
$env:PSModulePath = $originalModulePath
2018-04-13 19:29:39 +02:00
$env:POWERSHELL_TELEMETRY_OPTOUT = $originalTelemetry
2017-08-16 21:07:45 +02:00
if ( $Unelevate )
{
Remove-Item $outputBufferFilePath
}
2016-08-09 21:03:30 +02:00
}
2017-08-16 21:07:45 +02:00
2018-08-15 01:23:50 +02:00
Publish-TestResults -Path $OutputFile -Title $Title
2016-08-30 20:47:27 +02:00
if ( $ThrowOnFailure )
{
Test-PSPesterResults -TestResultsFile $OutputFile
}
}
2018-08-15 01:23:50 +02:00
function Publish-TestResults
{
param (
[ Parameter ( Mandatory ) ]
[ string ]
$Title ,
[ Parameter ( Mandatory ) ]
[ ValidateScript ( { Test-Path -Path $_ } ) ]
[ string ]
$Path ,
[ ValidateSet ( 'NUnit' , 'XUnit' ) ]
[ string ]
$Type = 'NUnit'
)
# In VSTS publish Test Results
if ( $env:TF_BUILD )
{
$resolvedPath = ( Resolve-Path -Path $Path ) . ProviderPath
Write-Host " ##vso[results.publish type= $Type ;mergeResults=true;runTitle= $Title ;publishRunAttachments=true;resultFiles= $resolvedPath ;] "
2018-08-16 22:11:24 +02:00
if ( $env:BUILD_REASON -ne 'PullRequest' )
{
Write-Host " ##vso[artifact.upload containerfolder=testResults;artifactname=testResults] $resolvedPath "
}
2018-08-15 01:23:50 +02:00
}
}
2016-09-16 04:22:18 +02:00
function script: Start-UnelevatedProcess
{
param (
[ string ] $process ,
[ string[] ] $arguments
)
2017-07-23 21:29:33 +02:00
if ( -not $Environment . IsWindows )
2016-09-16 04:22:18 +02:00
{
throw " Start-UnelevatedProcess is currently not supported on non-Windows platforms "
}
runas . exe / trustlevel : 0x20000 " $process $arguments "
}
2016-12-01 20:23:14 +01:00
function Show-PSPesterError
{
2017-08-26 17:26:06 +02:00
[ CmdletBinding ( DefaultParameterSetName = 'xml' ) ]
2017-09-11 21:16:58 +02:00
param (
2017-08-26 17:26:06 +02:00
[ Parameter ( ParameterSetName = 'xml' , Mandatory ) ]
[ Xml.XmlElement ] $testFailure ,
[ Parameter ( ParameterSetName = 'object' , Mandatory ) ]
[ PSCustomObject ] $testFailureObject
)
2017-09-11 21:16:58 +02:00
2017-08-26 17:26:06 +02:00
if ( $PSCmdLet . ParameterSetName -eq 'xml' )
{
$description = $testFailure . description
$name = $testFailure . name
$message = $testFailure . failure . message
$stackTrace = $testFailure . failure . " stack-trace "
}
elseif ( $PSCmdLet . ParameterSetName -eq 'object' )
{
$description = $testFailureObject . Describe + '/' + $testFailureObject . Context
$name = $testFailureObject . Name
$message = $testFailureObject . FailureMessage
$stackTrace = $testFailureObject . StackTrace
}
else
{
throw 'Unknown Show-PSPester parameter set'
}
2018-03-08 19:47:20 +01:00
Write-Log -Error ( " Description: " + $description )
Write-Log -Error ( " Name: " + $name )
Write-Log -Error " message: "
Write-Log -Error $message
Write-Log -Error " stack-trace: "
Write-Log -Error $stackTrace
2017-08-26 17:26:06 +02:00
2016-12-01 20:23:14 +01:00
}
2017-11-30 23:44:41 +01:00
function Test-XUnitTestResults
{
param (
[ Parameter ( Mandatory ) ]
[ ValidateNotNullOrEmpty ( ) ]
[ string ] $TestResultsFile
)
if ( -not ( Test-Path $TestResultsFile ) )
{
throw " File not found $TestResultsFile "
}
try
{
$results = [ xml ] ( Get-Content $TestResultsFile )
}
catch
{
throw " Cannot convert $TestResultsFile to xml : $( $_ . message ) "
}
$failedTests = $results . assemblies . assembly . collection | Where-Object failed -gt 0
if ( -not $failedTests )
{
return $true
}
foreach ( $failure in $failedTests )
{
$description = $failure . test . type
$name = $failure . test . method
$message = $failure . test . failure . message . '#cdata-section'
$stackTrace = $failure . test . failure . 'stack-trace' . '#cdata-section'
2018-03-08 19:47:20 +01:00
Write-Log -Error ( " Description: " + $description )
Write-Log -Error ( " Name: " + $name )
Write-Log -Error " message: "
Write-Log -Error $message
Write-Log -Error " stack-trace: "
Write-Log -Error $stackTrace
2017-11-30 23:44:41 +01:00
}
throw " $( $failedTests . failed ) tests failed "
}
2016-08-30 20:47:27 +02:00
#
# Read the test result file and
2017-01-16 22:31:14 +01:00
# Throw if a test failed
2016-08-30 20:47:27 +02:00
function Test-PSPesterResults
{
2017-08-26 17:26:06 +02:00
[ CmdletBinding ( DefaultParameterSetName = 'file' ) ]
2016-08-30 20:47:27 +02:00
param (
2017-08-26 17:26:06 +02:00
[ Parameter ( ParameterSetName = 'file' ) ]
2018-08-03 20:20:14 +02:00
[ string ] $TestResultsFile = " pester-tests.xml " ,
2017-08-26 17:26:06 +02:00
[ Parameter ( ParameterSetName = 'file' ) ]
2018-08-03 20:20:14 +02:00
[ string ] $TestArea = 'test/powershell' ,
[ Parameter ( ParameterSetName = 'PesterPassThruObject' , Mandatory ) ]
[ pscustomobject ] $ResultObject ,
[ Parameter ( ParameterSetName = 'PesterPassThruObject' ) ]
[ switch ] $CanHaveNoResult
)
2016-08-09 21:03:30 +02:00
2017-08-26 17:26:06 +02:00
if ( $PSCmdLet . ParameterSetName -eq 'file' )
2016-08-30 20:47:27 +02:00
{
2017-08-26 17:26:06 +02:00
if ( ! ( Test-Path $TestResultsFile ) )
{
throw " Test result file ' $testResultsFile ' not found for $TestArea . "
}
2016-08-30 20:47:27 +02:00
2017-08-26 17:26:06 +02:00
$x = [ xml ] ( Get-Content -raw $testResultsFile )
if ( [ int ] $x . 'test-results' . failures -gt 0 )
{
2018-03-08 19:47:20 +01:00
Write-Log -Error " TEST FAILURES "
2017-08-26 17:26:06 +02:00
# switch between methods, SelectNode is not available on dotnet core
2017-09-11 21:16:58 +02:00
if ( " System.Xml.XmlDocumentXPathExtensions " -as [ Type ] )
2017-08-26 17:26:06 +02:00
{
$failures = [ System.Xml.XmlDocumentXPathExtensions ] :: SelectNodes ( $x . " test-results " , './/test-case[@result = "Failure"]' )
}
2017-09-11 21:16:58 +02:00
else
2017-08-26 17:26:06 +02:00
{
$failures = $x . SelectNodes ( './/test-case[@result = "Failure"]' )
}
foreach ( $testfail in $failures )
{
Show-PSPesterError -testFailure $testfail
}
throw " $( $x . 'test-results' . failures ) tests in $TestArea failed "
2017-01-10 23:42:27 +01:00
}
2017-08-26 17:26:06 +02:00
}
elseif ( $PSCmdLet . ParameterSetName -eq 'PesterPassThruObject' )
{
2018-08-03 20:20:14 +02:00
if ( $ResultObject . TotalCount -le 0 -and -not $CanHaveNoResult )
2017-08-26 17:26:06 +02:00
{
2017-09-13 23:14:08 +02:00
throw 'NO TESTS RUN'
2017-01-10 23:42:27 +01:00
}
2017-08-26 17:26:06 +02:00
elseif ( $ResultObject . FailedCount -gt 0 )
2016-12-01 20:23:14 +01:00
{
2018-03-08 19:47:20 +01:00
Write-Log -Error 'TEST FAILURES'
2017-09-13 23:14:08 +02:00
2017-08-26 17:26:06 +02:00
$ResultObject . TestResult | Where-Object { $_ . Passed -eq $false } | ForEach-Object {
Show-PSPesterError -testFailureObject $_
}
2017-09-13 23:14:08 +02:00
throw " $( $ResultObject . FailedCount ) tests in $TestArea failed "
2016-12-01 20:23:14 +01:00
}
2016-04-02 07:01:45 +02:00
}
}
2016-04-02 05:45:32 +02:00
function Start-PSxUnit {
2017-11-30 23:44:41 +01:00
[ CmdletBinding ( ) ] param (
2018-01-03 21:43:28 +01:00
[ string ] $SequentialTestResultsFile = " SequentialXUnitResults.xml " ,
[ string ] $ParallelTestResultsFile = " ParallelXUnitResults.xml "
2017-11-30 23:44:41 +01:00
)
2016-04-15 00:09:35 +02:00
2016-06-20 20:59:14 +02:00
# Add .NET CLI tools to PATH
Find-Dotnet
2016-04-02 07:01:37 +02:00
$Content = Split-Path -Parent ( Get-PSOutput )
2016-06-01 20:10:29 +02:00
if ( -not ( Test-Path $Content ) ) {
throw " PowerShell must be built before running tests! "
}
2016-04-02 05:45:32 +02:00
try {
Push-Location $PSScriptRoot / test / csharp
2017-11-30 23:44:41 +01:00
2016-04-25 23:11:13 +02:00
# Path manipulation to obtain test project output directory
2017-11-30 23:44:41 +01:00
dotnet restore
2018-01-03 21:43:28 +01:00
if ( -not $Environment . IsWindows )
2017-11-30 23:44:41 +01:00
{
if ( $Environment . IsMacOS )
{
$nativeLib = " $Content /libpsl-native.dylib "
}
else
{
$nativeLib = " $Content /libpsl-native.so "
}
$requiredDependencies = @ (
$nativeLib ,
" $Content /Microsoft.Management.Infrastructure.dll " ,
" $Content /System.Text.Encoding.CodePages.dll "
)
2016-04-19 05:22:20 +02:00
2017-11-30 23:44:41 +01:00
if ( ( Test-Path $requiredDependencies ) -notcontains $false )
{
2018-05-15 00:01:44 +02:00
$options = Get-PSOptions -DefaultToNew
2017-11-30 23:44:41 +01:00
$Destination = " bin/ $( $options . configuration ) / $( $options . framework ) "
New-Item $Destination -ItemType Directory -Force > $null
Copy-Item -Path $requiredDependencies -Destination $Destination -Force
}
else
{
throw " Dependencies $requiredDependencies not met. "
}
2016-04-02 05:45:32 +02:00
}
2018-01-03 21:43:28 +01:00
# Run sequential tests first, and then run the tests that can execute in parallel
2018-10-10 05:55:41 +02:00
if ( Test-Path $SequentialTestResultsFile ) {
Remove-Item $SequentialTestResultsFile -Force -ErrorAction SilentlyContinue
}
dotnet test - -configuration $Options . configuration - -filter FullyQualifiedName ~ PSTests . Sequential -p: ParallelizeTestCollections = false - -test -adapter -path: . " --logger:xunit;LogFilePath= $SequentialTestResultsFile "
2018-08-15 01:23:50 +02:00
Publish-TestResults -Path $SequentialTestResultsFile -Type 'XUnit' -Title 'Xunit Sequential'
2018-08-14 01:18:49 +02:00
$extraParams = @ ( )
# we are having intermittent issues on macOS with these tests failing.
# VSTS has suggested forcing them to be sequential
if ( $env:TF_BUILD -and $IsMacOS )
{
Write-Log 'Forcing parallel xunit tests to run sequentially.'
$extraParams + = @ (
'-parallel'
'none'
)
}
2018-10-10 05:55:41 +02:00
if ( Test-Path $ParallelTestResultsFile ) {
Remove-Item $ParallelTestResultsFile -Force -ErrorAction SilentlyContinue
}
dotnet test - -configuration $Options . configuration - -filter FullyQualifiedName ~ PSTests . Parallel - -no -build - -test -adapter -path: . " --logger:xunit;LogFilePath= $ParallelTestResultsFile "
2018-08-15 01:23:50 +02:00
Publish-TestResults -Path $ParallelTestResultsFile -Type 'XUnit' -Title 'Xunit Parallel'
2017-11-30 23:44:41 +01:00
}
finally {
2016-04-02 05:45:32 +02:00
Pop-Location
}
}
2016-09-09 23:35:54 +02:00
function Install-Dotnet {
[ CmdletBinding ( ) ]
param (
2017-06-21 18:32:06 +02:00
[ string ] $Channel = $dotnetCLIChannel ,
[ string ] $Version = $dotnetCLIRequiredVersion ,
2016-09-09 23:35:54 +02:00
[ switch ] $NoSudo
)
# 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 " }
Move powershell to .NET Core 2.0 (#3556)
This change moves powershell to .NET Core 2.0. Major changes are:
1. PowerShell assemblies are now targeting `netcoreapp2.0`. We are using `microsoft.netcore.app-2.0.0-preview1-001913-00`, which is from dotnet-core build 4/4/17. We cannot target `netstandard2.0` because the packages `System.Reflection.Emit` and `System.Reflection.Emit.Lightweight`, which are needed for powershell class, cannot be referenced when targeting `netstandard2.0`.
2. Refactor code to remove most CLR stub types and extension types.
3. Update build scripts to enable CI builds. The `-cache` section is specified to depend on `appveyor.yml`, so the cache will be invalidated if `appveyor.yml` is changed.
4. Ship `netcoreapp` reference assemblies with powershell to fix the issues in `Add-Type` (#2764). By default `Add-Type` will reference all those reference assemblies when compiling C# code. If `-ReferenceAssembly` is specified, then we search reference assemblies first, then the framework runtime assemblies, and lastly the loaded assemblies (possibly a third-party one that was already loaded).
5. `dotnet publish` generates executable on Unix platforms, but doesn't set "x" permission and thus it cannot execute. Currently, the "x" permission is set in the build script, `dotnet/cli` issue [#6286](https://github.com/dotnet/cli/issues/6286) is tracking this.
6. Replace the use of some APIs with the ones that take `SecureString`.
7. osx.10.12 is required to update to `netcoreapp2.0` because `dotnet-cli` 2.0.0-preview only works on osx.10.12.
8. Add dependency to `System.ValueTuple` to work around a ambiguous type identity issue in coreclr. The issue is tracked by `dotnet/corefx` [#17797](https://github.com/dotnet/corefx/issues/17797). When moving to newer version of `netcoreapp2.0`, we need to verify if this dependency is still needed.
2017-04-17 20:52:38 +02:00
$obtainUrl = " https://raw.githubusercontent.com/dotnet/cli/master/scripts/obtain "
2016-09-09 23:35:54 +02:00
# Install for Linux and OS X
2017-09-07 19:34:40 +02:00
if ( $Environment . IsLinux -or $Environment . IsMacOS ) {
2016-09-09 23:35:54 +02:00
# Uninstall all previous dotnet packages
2017-07-23 21:29:33 +02:00
$uninstallScript = if ( $Environment . IsUbuntu ) {
2016-09-09 23:35:54 +02:00
" dotnet-uninstall-debian-packages.sh "
2017-09-07 19:34:40 +02:00
} elseif ( $Environment . IsMacOS ) {
2016-09-09 23:35:54 +02:00
" dotnet-uninstall-pkgs.sh "
}
if ( $uninstallScript ) {
Start-NativeExecution {
curl -sO $obtainUrl / uninstall / $uninstallScript
Invoke-Expression " $sudo bash ./ $uninstallScript "
}
} else {
Write-Warning " This script only removes prior versions of dotnet for Ubuntu 14.04 and OS X "
}
2016-12-08 03:29:21 +01:00
# Install new dotnet 1.1.0 preview packages
2016-09-09 23:35:54 +02:00
$installScript = " dotnet-install.sh "
Start-NativeExecution {
curl -sO $obtainUrl / $installScript
bash . / $installScript -c $Channel -v $Version
}
2017-07-23 21:29:33 +02:00
} elseif ( $Environment . IsWindows ) {
2016-09-09 23:35:54 +02:00
Remove-Item -ErrorAction SilentlyContinue -Recurse -Force ~ \ AppData \ Local \ Microsoft \ dotnet
$installScript = " dotnet-install.ps1 "
Invoke-WebRequest -Uri $obtainUrl / $installScript -OutFile $installScript
2017-06-30 22:07:14 +02:00
2017-07-23 21:29:33 +02:00
if ( -not $Environment . IsCoreCLR ) {
2017-06-30 22:07:14 +02:00
& . / $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
$fullPSPath = Join-Path -Path $env:windir -ChildPath " System32\WindowsPowerShell\v1.0\powershell.exe "
$fullDotnetInstallPath = Join-Path -Path $pwd . Path -ChildPath $installScript
Start-NativeExecution { & $fullPSPath -NoLogo -NoProfile -File $fullDotnetInstallPath -Channel $Channel -Version $Version }
}
2016-09-09 23:35:54 +02:00
}
}
2017-01-06 19:04:18 +01:00
function Get-RedHatPackageManager {
2017-07-23 21:29:33 +02:00
if ( $Environment . IsCentOS ) {
2017-03-08 00:03:38 +01:00
" yum install -y -q "
2017-07-23 21:29:33 +02:00
} elseif ( $Environment . IsFedora ) {
2017-03-08 00:03:38 +01:00
" dnf install -y -q "
2017-01-06 19:04:18 +01:00
} else {
throw " Error determining package manager for this distribution. "
}
}
2016-09-09 23:35:54 +02:00
2016-04-14 00:11:38 +02:00
function Start-PSBootstrap {
2016-07-22 23:31:32 +02:00
[ CmdletBinding (
SupportsShouldProcess = $true ,
ConfirmImpact = " High " ) ]
param (
2017-06-21 18:32:06 +02:00
[ string ] $Channel = $dotnetCLIChannel ,
Move powershell to .NET Core 2.0 (#3556)
This change moves powershell to .NET Core 2.0. Major changes are:
1. PowerShell assemblies are now targeting `netcoreapp2.0`. We are using `microsoft.netcore.app-2.0.0-preview1-001913-00`, which is from dotnet-core build 4/4/17. We cannot target `netstandard2.0` because the packages `System.Reflection.Emit` and `System.Reflection.Emit.Lightweight`, which are needed for powershell class, cannot be referenced when targeting `netstandard2.0`.
2. Refactor code to remove most CLR stub types and extension types.
3. Update build scripts to enable CI builds. The `-cache` section is specified to depend on `appveyor.yml`, so the cache will be invalidated if `appveyor.yml` is changed.
4. Ship `netcoreapp` reference assemblies with powershell to fix the issues in `Add-Type` (#2764). By default `Add-Type` will reference all those reference assemblies when compiling C# code. If `-ReferenceAssembly` is specified, then we search reference assemblies first, then the framework runtime assemblies, and lastly the loaded assemblies (possibly a third-party one that was already loaded).
5. `dotnet publish` generates executable on Unix platforms, but doesn't set "x" permission and thus it cannot execute. Currently, the "x" permission is set in the build script, `dotnet/cli` issue [#6286](https://github.com/dotnet/cli/issues/6286) is tracking this.
6. Replace the use of some APIs with the ones that take `SecureString`.
7. osx.10.12 is required to update to `netcoreapp2.0` because `dotnet-cli` 2.0.0-preview only works on osx.10.12.
8. Add dependency to `System.ValueTuple` to work around a ambiguous type identity issue in coreclr. The issue is tracked by `dotnet/corefx` [#17797](https://github.com/dotnet/corefx/issues/17797). When moving to newer version of `netcoreapp2.0`, we need to verify if this dependency is still needed.
2017-04-17 20:52:38 +02:00
# we currently pin dotnet-cli version, and will
# update it when more stable version comes out.
2017-06-21 18:32:06 +02:00
[ string ] $Version = $dotnetCLIRequiredVersion ,
2016-07-22 23:31:32 +02:00
[ switch ] $Package ,
2016-09-09 01:21:20 +02:00
[ switch ] $NoSudo ,
2017-09-11 21:16:58 +02:00
[ switch ] $BuildLinuxArm ,
2016-07-22 23:31:32 +02:00
[ switch ] $Force
2016-06-14 22:33:19 +02:00
)
2016-04-14 00:11:38 +02:00
2018-03-08 19:47:20 +01:00
Write-Log " Installing PowerShell build dependencies "
2016-04-14 00:11:38 +02:00
2016-06-10 23:02:25 +02:00
Push-Location $PSScriptRoot / tools
2016-04-14 00:11:38 +02:00
2016-06-10 23:02:25 +02:00
try {
2017-09-07 19:34:40 +02:00
if ( $Environment . IsLinux -or $Environment . IsMacOS ) {
2017-07-26 01:50:27 +02:00
# 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 " }
2017-09-11 21:16:58 +02:00
if ( $BuildLinuxArm -and -not $Environment . IsUbuntu ) {
Write-Error " Cross compiling for linux-arm is only supported on Ubuntu environment "
return
}
2017-07-26 01:50:27 +02:00
# Install ours and .NET's dependencies
$Deps = @ ( )
if ( $Environment . IsUbuntu ) {
# Build tools
$Deps + = " curl " , " g++ " , " cmake " , " make "
2016-08-11 22:38:35 +02:00
2017-09-11 21:16:58 +02:00
if ( $BuildLinuxArm ) {
$Deps + = " gcc-arm-linux-gnueabihf " , " g++-arm-linux-gnueabihf "
}
2017-07-26 01:50:27 +02:00
# .NET Core required runtime libraries
$Deps + = " libunwind8 "
if ( $Environment . IsUbuntu14 ) { $Deps + = " libicu52 " }
elseif ( $Environment . IsUbuntu16 ) { $Deps + = " libicu55 " }
2016-08-11 22:38:35 +02:00
2017-07-26 01:50:27 +02:00
# Packaging tools
2018-03-12 23:59:58 +01:00
if ( $Package ) { $Deps + = " ruby-dev " , " groff " , " libffi-dev " }
2016-08-11 22:38:35 +02:00
2017-07-26 01:50:27 +02:00
# Install dependencies
2018-03-12 23:59:58 +01:00
# change the fontend from apt-get to noninteractive
$originalDebianFrontEnd = $env:DEBIAN_FRONTEND
$env:DEBIAN_FRONTEND = 'noninteractive'
try {
Start-NativeExecution {
Invoke-Expression " $sudo apt-get update -qq "
Invoke-Expression " $sudo apt-get install -y -qq $Deps "
}
}
finally {
# change the apt frontend back to the original
$env:DEBIAN_FRONTEND = $originalDebianFrontEnd
2017-07-26 01:50:27 +02:00
}
} elseif ( $Environment . IsRedHatFamily ) {
# Build tools
$Deps + = " which " , " curl " , " gcc-c++ " , " cmake " , " make "
2016-08-11 22:38:35 +02:00
2017-07-26 01:50:27 +02:00
# .NET Core required runtime libraries
$Deps + = " libicu " , " libunwind "
2016-08-11 22:38:35 +02:00
2017-07-26 01:50:27 +02:00
# Packaging tools
2018-03-12 23:59:58 +01:00
if ( $Package ) { $Deps + = " ruby-devel " , " rpm-build " , " groff " , 'libffi-devel' }
2016-08-11 22:38:35 +02:00
2017-07-26 01:50:27 +02:00
$PackageManager = Get-RedHatPackageManager
2017-01-06 19:04:18 +01:00
2017-07-26 01:50:27 +02:00
$baseCommand = " $sudo $PackageManager "
2017-03-08 00:03:38 +01:00
2017-07-26 01:50:27 +02:00
# On OpenSUSE 13.2 container, sudo does not exist, so don't use it if not needed
if ( $NoSudo )
{
$baseCommand = $PackageManager
}
2017-03-08 00:03:38 +01:00
2018-01-22 23:58:17 +01:00
# Install dependencies
Start-NativeExecution {
Invoke-Expression " $baseCommand $Deps "
}
} elseif ( $Environment . IsSUSEFamily ) {
# Build tools
$Deps + = " gcc " , " cmake " , " make "
# Packaging tools
2018-03-12 23:59:58 +01:00
if ( $Package ) { $Deps + = " ruby-devel " , " rpmbuild " , " groff " , 'libffi-devel' }
2018-01-22 23:58:17 +01:00
$PackageManager = " zypper --non-interactive install "
$baseCommand = " $sudo $PackageManager "
# On OpenSUSE 13.2 container, sudo does not exist, so don't use it if not needed
if ( $NoSudo )
{
$baseCommand = $PackageManager
}
2017-07-26 01:50:27 +02:00
# Install dependencies
Start-NativeExecution {
Invoke-Expression " $baseCommand $Deps "
}
2017-09-07 19:34:40 +02:00
} elseif ( $Environment . IsMacOS ) {
2017-07-26 01:50:27 +02:00
precheck 'brew' " Bootstrap dependency 'brew' not found, must install Homebrew! See http://brew.sh/ "
2016-08-11 22:38:35 +02:00
2017-07-26 01:50:27 +02:00
# Build tools
$Deps + = " cmake "
2016-08-11 22:38:35 +02:00
2017-07-26 01:50:27 +02:00
# .NET Core required runtime libraries
$Deps + = " openssl "
2016-08-11 22:38:35 +02:00
2017-07-26 01:50:27 +02:00
# Install dependencies
# ignore exitcode, because they may be already installed
Start-NativeExecution { brew install $Deps } -IgnoreExitcode
2016-09-20 23:26:07 +02:00
2017-07-26 01:50:27 +02:00
# Install patched version of curl
2017-12-06 21:20:44 +01:00
Start-NativeExecution { brew install curl - -with -openssl - -with -gssapi } -IgnoreExitcode
2018-11-27 19:45:49 +01:00
} elseif ( $Environment . IsAlpine ) {
$Deps + = 'libunwind' , 'libcurl' , 'bash' , 'cmake' , 'clang' , 'build-base' , 'git' , 'curl'
Start-NativeExecution {
Invoke-Expression " apk add $Deps "
}
2017-07-26 01:50:27 +02:00
}
2016-04-14 00:11:38 +02:00
2017-07-26 01:50:27 +02:00
# Install [fpm](https://github.com/jordansissel/fpm) and [ronn](https://github.com/rtomayko/ronn)
if ( $Package ) {
try {
2018-04-10 21:18:11 +02:00
# We cannot guess if the user wants to run gem install as root on linux and windows,
# but macOs usually requires sudo
$gemsudo = ''
2018-09-06 21:27:49 +02:00
if ( $Environment . IsMacOS -or $env:TF_BUILD ) {
2018-04-10 21:18:11 +02:00
$gemsudo = $sudo
}
2018-06-06 21:15:33 +02:00
Start-NativeExecution ( [ ScriptBlock ] :: Create ( " $gemsudo gem install fpm -v 1.10.0 " ) )
2018-04-10 21:18:11 +02:00
Start-NativeExecution ( [ ScriptBlock ] :: Create ( " $gemsudo gem install ronn -v 0.7.3 " ) )
2017-07-26 01:50:27 +02:00
} catch {
Write-Warning " Installation of fpm and ronn gems failed! Must resolve manually. "
}
2016-09-09 01:21:43 +02:00
}
2016-07-05 20:03:08 +02:00
}
2017-10-10 23:01:01 +02:00
# Try to locate dotnet-SDK before installing it
Find-Dotnet
2017-07-26 01:50:27 +02:00
# Install dotnet-SDK
2017-07-18 20:39:04 +02:00
$dotNetExists = precheck 'dotnet' $null
$dotNetVersion = [ string ] :: Empty
if ( $dotNetExists ) {
$dotNetVersion = ( dotnet - -version )
}
if ( ! $dotNetExists -or $dotNetVersion -ne $dotnetCLIRequiredVersion -or $Force . IsPresent ) {
if ( $Force . IsPresent ) {
2018-03-08 19:47:20 +01:00
Write-Log " Installing dotnet due to -Force. "
2017-07-18 20:39:04 +02:00
}
elseif ( ! $dotNetExistis ) {
2018-03-08 19:47:20 +01:00
Write-Log " dotnet not present. Installing dotnet. "
2017-07-18 20:39:04 +02:00
}
else {
2018-03-08 19:47:20 +01:00
Write-Log " dotnet out of date ( $dotNetVersion ). Updating dotnet. "
2017-07-18 20:39:04 +02:00
}
$DotnetArguments = @ { Channel = $Channel ; Version = $Version ; NoSudo = $NoSudo }
Install-Dotnet @DotnetArguments
}
2017-07-26 01:50:27 +02:00
else {
2018-03-08 19:47:20 +01:00
Write-Log " dotnet is already installed. Skipping installation. "
2017-07-18 20:39:04 +02:00
}
2016-04-14 01:41:50 +02:00
2017-07-26 01:50:27 +02:00
# Install Windows dependencies if `-Package` or `-BuildWindowsNative` is specified
2017-07-23 21:29:33 +02:00
if ( $Environment . IsWindows ) {
2017-11-10 17:28:02 +01:00
## The VSCode build task requires 'pwsh.exe' to be found in Path
if ( -not ( Get-Command -Name pwsh . exe -CommandType Application -ErrorAction SilentlyContinue ) )
{
2018-03-08 19:47:20 +01:00
Write-Log " pwsh.exe not found. Install latest PowerShell Core release and add it to Path "
2017-11-10 17:28:02 +01:00
$psInstallFile = [ System.IO.Path ] :: Combine ( $PSScriptRoot , " tools " , " install-powershell.ps1 " )
& $psInstallFile -AddToPath
}
2017-10-27 06:52:08 +02:00
## need RCEdit to modify the binaries embedded resources
if ( -not ( Test-Path " ~/.rcedit/rcedit-x64.exe " ) )
{
2018-03-08 19:47:20 +01:00
Write-Log " Install RCEdit for modifying exe resources "
2017-10-27 06:52:08 +02:00
$rceditUrl = " https://github.com/electron/rcedit/releases/download/v1.0.0/rcedit-x64.exe "
New-Item -Path " ~/.rcedit " -Type Directory -Force > $null
2018-02-24 20:01:37 +01:00
## need to specify TLS version 1.2 since GitHub API requires it
2018-02-24 21:05:49 +01:00
[ Net.ServicePointManager ] :: SecurityProtocol = [ Net.ServicePointManager ] :: SecurityProtocol -bor [ Net.SecurityProtocolType ] :: Tls12
2018-02-24 20:01:37 +01:00
2017-10-27 06:52:08 +02:00
Invoke-WebRequest -OutFile " ~/.rcedit/rcedit-x64.exe " -Uri $rceditUrl
}
2016-06-10 23:02:25 +02:00
}
} finally {
Pop-Location
2016-04-14 00:11:38 +02:00
}
}
2016-05-20 04:03:43 +02:00
function Publish-NuGetFeed
{
param (
[ string ] $OutputPath = " $PSScriptRoot /nuget-artifacts " ,
2017-11-16 01:55:13 +01:00
[ ValidatePattern ( " ^v\d+\.\d+\.\d+(-\w+(\.\d+)?)? $ " ) ]
2017-09-07 00:20:25 +02:00
[ ValidateNotNullOrEmpty ( ) ]
[ string ] $ReleaseTag
2016-05-20 04:03:43 +02:00
)
2016-06-20 20:59:14 +02:00
# Add .NET CLI tools to PATH
Find-Dotnet
2017-09-07 00:20:25 +02:00
## We update 'project.assets.json' files with new version tag value by 'GetPSCoreVersionFromGit' target.
$TopProject = ( New-PSOptions ) . Top
if ( $ReleaseTag ) {
$ReleaseTagToUse = $ReleaseTag -Replace '^v'
dotnet restore $TopProject " /property:ReleaseTag= $ReleaseTagToUse "
} else {
dotnet restore $TopProject
2017-05-02 07:34:25 +02:00
}
2017-03-23 21:04:52 +01:00
try {
Push-Location $PSScriptRoot
@ (
2016-05-20 04:03:43 +02:00
'Microsoft.PowerShell.Commands.Management' ,
'Microsoft.PowerShell.Commands.Utility' ,
2017-01-31 01:44:23 +01:00
'Microsoft.PowerShell.Commands.Diagnostics' ,
2016-05-20 04:03:43 +02:00
'Microsoft.PowerShell.ConsoleHost' ,
'Microsoft.PowerShell.Security' ,
2016-06-25 02:25:56 +02:00
'System.Management.Automation' ,
2016-07-27 01:43:32 +02:00
'Microsoft.PowerShell.CoreCLR.Eventing' ,
2016-08-30 01:01:38 +02:00
'Microsoft.WSMan.Management' ,
'Microsoft.WSMan.Runtime' ,
2016-07-27 01:43:32 +02:00
'Microsoft.PowerShell.SDK'
2017-07-22 06:03:49 +02:00
) | ForEach-Object {
2017-09-07 00:20:25 +02:00
if ( $ReleaseTag ) {
dotnet pack " src/ $_ " - -output $OutputPath " /property:IncludeSymbols=true;ReleaseTag= $ReleaseTagToUse "
2017-03-23 21:04:52 +01:00
} else {
dotnet pack " src/ $_ " - -output $OutputPath
}
2016-05-20 04:03:43 +02:00
}
2017-03-23 21:04:52 +01:00
} finally {
Pop-Location
2016-05-20 04:03:43 +02:00
}
}
2016-07-01 21:09:50 +02:00
function Start-DevPowerShell {
2018-07-26 19:30:05 +02:00
[ CmdletBinding ( DefaultParameterSetName = 'ConfigurationParamSet' ) ]
2016-02-03 19:53:43 +01:00
param (
2018-07-26 19:30:05 +02:00
[ string[] ] $ArgumentList = @ ( ) ,
2016-02-03 19:53:43 +01:00
[ switch ] $LoadProfile ,
2018-07-26 19:30:05 +02:00
[ Parameter ( ParameterSetName = 'ConfigurationParamSet' ) ]
[ ValidateSet ( " Debug " , " Release " , " CodeCoverage " , '' ) ] # should match New-PSOptions -Configuration values
[ string ] $Configuration ,
[ Parameter ( ParameterSetName = 'BinDirParamSet' ) ]
[ string ] $BinDir ,
2016-07-01 21:09:50 +02:00
[ switch ] $NoNewWindow ,
[ string ] $Command ,
[ switch ] $KeepPSModulePath
2016-02-03 19:53:43 +01:00
)
2016-04-02 07:01:05 +02:00
try {
2018-07-26 19:30:05 +02:00
if ( -not $BinDir ) {
$BinDir = Split-Path ( New-PSOptions -Configuration $Configuration ) . Output
}
2017-07-23 21:29:33 +02:00
if ( ( -not $NoNewWindow ) -and ( $Environment . IsCoreCLR ) ) {
2016-07-27 21:27:41 +02:00
Write-Warning " Start-DevPowerShell -NoNewWindow is currently implied in PowerShellCore edition https://github.com/PowerShell/PowerShell/issues/1543 "
2016-07-27 19:55:42 +02:00
$NoNewWindow = $true
}
2016-07-01 21:09:50 +02:00
if ( -not $LoadProfile ) {
2016-02-04 02:25:51 +01:00
$ArgumentList = @ ( '-noprofile' ) + $ArgumentList
2016-02-03 19:53:43 +01:00
}
2016-08-03 22:23:21 +02:00
if ( -not $KeepPSModulePath ) {
if ( -not $Command ) {
2016-07-01 21:09:50 +02:00
$ArgumentList = @ ( '-NoExit' ) + $ArgumentList
}
2017-03-15 20:04:28 +01:00
$Command = '$env:PSModulePath = Join-Path $env:DEVPATH Modules; ' + $Command
2016-08-03 22:23:21 +02:00
}
2016-07-01 21:09:50 +02:00
2016-08-03 22:23:21 +02:00
if ( $Command ) {
2016-07-01 21:09:50 +02:00
$ArgumentList = $ArgumentList + @ ( " -command $Command " )
}
2018-07-26 19:30:05 +02:00
$env:DEVPATH = $BinDir
2016-02-22 00:06:39 +01:00
# splatting for the win
2016-02-04 02:25:51 +01:00
$startProcessArgs = @ {
2018-07-26 19:30:05 +02:00
FilePath = " $BinDir \pwsh "
}
if ( $ArgumentList ) {
$startProcessArgs . ArgumentList = $ArgumentList
2016-02-22 00:06:39 +01:00
}
2016-02-04 22:19:24 +01:00
if ( $NoNewWindow ) {
$startProcessArgs . NoNewWindow = $true
$startProcessArgs . Wait = $true
}
2016-02-22 00:06:39 +01:00
2016-02-04 02:25:51 +01:00
Start-Process @startProcessArgs
2016-04-02 07:01:05 +02:00
} finally {
2016-08-30 20:47:27 +02:00
if ( $env:DevPath )
{
Remove-Item env : DEVPATH
}
2017-01-16 22:31:14 +01:00
2016-04-02 07:01:05 +02:00
if ( $ZapDisable ) {
2016-08-30 20:47:27 +02:00
Remove-Item env : COMPLUS_ZapDisable
2016-02-03 19:53:43 +01:00
}
}
2016-02-20 23:35:46 +01:00
}
2016-03-23 00:00:20 +01:00
2016-06-17 20:23:29 +02:00
function Start-TypeGen
{
[ CmdletBinding ( ) ]
2017-11-15 19:52:06 +01:00
param
(
[ ValidateNotNullOrEmpty ( ) ]
$IncFileName = 'powershell.inc'
)
2016-06-17 20:23:29 +02:00
2016-06-20 20:59:14 +02:00
# Add .NET CLI tools to PATH
Find-Dotnet
2018-05-03 01:58:39 +02:00
# This custom target depends on 'ResolveAssemblyReferencesDesignTime', whose definition can be found in the sdk folder.
# To find the available properties of '_ReferencesFromRAR' when switching to a new dotnet sdk, follow the steps below:
# 1. create a dummy project using the new dotnet sdk.
# 2. build the dummy project with this command:
# dotnet msbuild .\dummy.csproj /t:ResolveAssemblyReferencesDesignTime /fileLogger /noconsolelogger /v:diag
# 3. search '_ReferencesFromRAR' in the produced 'msbuild.log' file. You will find the properties there.
2017-03-23 21:04:52 +01:00
$GetDependenciesTargetPath = " $PSScriptRoot /src/Microsoft.PowerShell.SDK/obj/Microsoft.PowerShell.SDK.csproj.TypeCatalog.targets "
$GetDependenciesTargetValue = @ '
< Project >
< Target Name = " _GetDependencies "
Move powershell to .NET Core 2.0 (#3556)
This change moves powershell to .NET Core 2.0. Major changes are:
1. PowerShell assemblies are now targeting `netcoreapp2.0`. We are using `microsoft.netcore.app-2.0.0-preview1-001913-00`, which is from dotnet-core build 4/4/17. We cannot target `netstandard2.0` because the packages `System.Reflection.Emit` and `System.Reflection.Emit.Lightweight`, which are needed for powershell class, cannot be referenced when targeting `netstandard2.0`.
2. Refactor code to remove most CLR stub types and extension types.
3. Update build scripts to enable CI builds. The `-cache` section is specified to depend on `appveyor.yml`, so the cache will be invalidated if `appveyor.yml` is changed.
4. Ship `netcoreapp` reference assemblies with powershell to fix the issues in `Add-Type` (#2764). By default `Add-Type` will reference all those reference assemblies when compiling C# code. If `-ReferenceAssembly` is specified, then we search reference assemblies first, then the framework runtime assemblies, and lastly the loaded assemblies (possibly a third-party one that was already loaded).
5. `dotnet publish` generates executable on Unix platforms, but doesn't set "x" permission and thus it cannot execute. Currently, the "x" permission is set in the build script, `dotnet/cli` issue [#6286](https://github.com/dotnet/cli/issues/6286) is tracking this.
6. Replace the use of some APIs with the ones that take `SecureString`.
7. osx.10.12 is required to update to `netcoreapp2.0` because `dotnet-cli` 2.0.0-preview only works on osx.10.12.
8. Add dependency to `System.ValueTuple` to work around a ambiguous type identity issue in coreclr. The issue is tracked by `dotnet/corefx` [#17797](https://github.com/dotnet/corefx/issues/17797). When moving to newer version of `netcoreapp2.0`, we need to verify if this dependency is still needed.
2017-04-17 20:52:38 +02:00
DependsOnTargets = " ResolveAssemblyReferencesDesignTime " >
2017-03-23 21:04:52 +01:00
< ItemGroup >
2018-05-03 01:58:39 +02:00
< _RefAssemblyPath Include = " %(_ReferencesFromRAR.HintPath)%3B " Condition = " '%(_ReferencesFromRAR.NuGetPackageId)' != 'Microsoft.Management.Infrastructure' " / >
2017-03-23 21:04:52 +01:00
< / ItemGroup >
Move powershell to .NET Core 2.0 (#3556)
This change moves powershell to .NET Core 2.0. Major changes are:
1. PowerShell assemblies are now targeting `netcoreapp2.0`. We are using `microsoft.netcore.app-2.0.0-preview1-001913-00`, which is from dotnet-core build 4/4/17. We cannot target `netstandard2.0` because the packages `System.Reflection.Emit` and `System.Reflection.Emit.Lightweight`, which are needed for powershell class, cannot be referenced when targeting `netstandard2.0`.
2. Refactor code to remove most CLR stub types and extension types.
3. Update build scripts to enable CI builds. The `-cache` section is specified to depend on `appveyor.yml`, so the cache will be invalidated if `appveyor.yml` is changed.
4. Ship `netcoreapp` reference assemblies with powershell to fix the issues in `Add-Type` (#2764). By default `Add-Type` will reference all those reference assemblies when compiling C# code. If `-ReferenceAssembly` is specified, then we search reference assemblies first, then the framework runtime assemblies, and lastly the loaded assemblies (possibly a third-party one that was already loaded).
5. `dotnet publish` generates executable on Unix platforms, but doesn't set "x" permission and thus it cannot execute. Currently, the "x" permission is set in the build script, `dotnet/cli` issue [#6286](https://github.com/dotnet/cli/issues/6286) is tracking this.
6. Replace the use of some APIs with the ones that take `SecureString`.
7. osx.10.12 is required to update to `netcoreapp2.0` because `dotnet-cli` 2.0.0-preview only works on osx.10.12.
8. Add dependency to `System.ValueTuple` to work around a ambiguous type identity issue in coreclr. The issue is tracked by `dotnet/corefx` [#17797](https://github.com/dotnet/corefx/issues/17797). When moving to newer version of `netcoreapp2.0`, we need to verify if this dependency is still needed.
2017-04-17 20:52:38 +02:00
< WriteLinesToFile File = " $( _DependencyFile ) " Lines = " @(_RefAssemblyPath) " Overwrite = " true " / >
2017-03-23 21:04:52 +01:00
< / Target >
< / Project >
' @
Set-Content -Path $GetDependenciesTargetPath -Value $GetDependenciesTargetValue -Force -Encoding Ascii
Push-Location " $PSScriptRoot /src/Microsoft.PowerShell.SDK "
2016-08-03 22:23:21 +02:00
try {
2017-11-15 19:52:06 +01:00
$ps_inc_file = " $PSScriptRoot /src/TypeCatalogGen/ $IncFileName "
2017-03-23 21:04:52 +01:00
dotnet msbuild . \ Microsoft . PowerShell . SDK . csproj / t: _GetDependencies " /property:DesignTimeBuild=true;_DependencyFile= $ps_inc_file " / nologo
2016-08-03 22:23:21 +02:00
} finally {
2016-06-17 20:23:29 +02:00
Pop-Location
}
Push-Location " $PSScriptRoot /src/TypeCatalogGen "
2016-08-03 22:23:21 +02:00
try {
2017-11-15 19:52:06 +01:00
dotnet run . . / System . Management . Automation / CoreCLR / CorePsTypeCatalog . cs $IncFileName
2016-08-03 22:23:21 +02:00
} finally {
2016-06-17 20:23:29 +02:00
Pop-Location
}
}
2016-04-04 23:20:34 +02:00
function Start-ResGen
{
2016-06-15 12:42:54 +02:00
[ CmdletBinding ( ) ]
param ( )
2016-08-02 20:47:53 +02:00
# Add .NET CLI tools to PATH
Find-Dotnet
Push-Location " $PSScriptRoot /src/ResGen "
2016-08-03 22:23:21 +02:00
try {
2016-08-02 20:47:53 +02:00
Start-NativeExecution { dotnet run } | Write-Verbose
2016-08-03 22:23:21 +02:00
} finally {
2016-08-02 20:47:53 +02:00
Pop-Location
2016-04-04 23:20:34 +02:00
}
}
2016-06-20 20:59:14 +02:00
function Find-Dotnet ( ) {
$originalPath = $env:PATH
2017-11-10 01:28:39 +01:00
$dotnetPath = if ( $Environment . IsWindows ) { " $env:LocalAppData \Microsoft\dotnet " } else { " $env:HOME /.dotnet " }
2016-06-20 20:59:14 +02:00
2017-11-10 01:28:39 +01:00
# If there dotnet is already in the PATH, check to see if that version of dotnet can find the required SDK
# This is "typically" the globally installed dotnet
if ( precheck dotnet ) {
# Must run from within repo to ensure global.json can specify the required SDK version
Push-Location $PSScriptRoot
$dotnetCLIInstalledVersion = ( dotnet - -version )
Pop-Location
if ( $dotnetCLIInstalledVersion -ne $dotnetCLIRequiredVersion ) {
Write-Warning " The 'dotnet' in the current path can't find SDK version ${dotnetCLIRequiredVersion} , prepending $dotnetPath to PATH. "
# Globally installed dotnet doesn't have the required SDK version, prepend the user local dotnet location
$env:PATH = $dotnetPath + [ IO.Path ] :: PathSeparator + $env:PATH
}
}
else {
Write-Warning " Could not find 'dotnet', appending $dotnetPath to PATH. "
2016-06-20 20:59:14 +02:00
$env:PATH + = [ IO.Path ] :: PathSeparator + $dotnetPath
}
if ( -not ( precheck 'dotnet' " Still could not find 'dotnet', restoring PATH. " ) ) {
$env:PATH = $originalPath
}
}
2016-07-01 01:02:07 +02:00
<#
2016-07-01 22:51:14 +02:00
This is one-time conversion . We use it for to turn GetEventResources . txt into GetEventResources . resx
2016-07-01 01:02:07 +02:00
. EXAMPLE Convert-TxtResourceToXml -Path Microsoft . PowerShell . Commands . Diagnostics \ resources
#>
function Convert-TxtResourceToXml
{
param (
[ string[] ] $Path
)
2016-08-03 22:23:21 +02:00
process {
2017-07-22 06:03:49 +02:00
$Path | ForEach-Object {
Get-ChildItem $_ -Filter " *.txt " | ForEach-Object {
2016-07-01 01:02:07 +02:00
$txtFile = $_ . FullName
$resxFile = Join-Path ( Split-Path $txtFile ) " $( $_ . BaseName ) .resx "
$resourceHashtable = ConvertFrom-StringData ( Get-Content -Raw $txtFile )
2017-07-22 06:03:49 +02:00
$resxContent = $resourceHashtable . GetEnumerator ( ) | ForEach-Object {
2016-07-01 01:02:07 +02:00
@ '
< data name = " {0} " xml : space = " preserve " >
< value > { 1 } < / value >
< / data >
' @ -f $_ . Key , $_ . Value
} | Out-String
Set-Content -Path $resxFile -Value ( $script:RESX_TEMPLATE -f $resxContent )
}
}
}
}
2016-06-22 01:54:57 +02:00
function script: Use-MSBuild {
2016-06-24 23:52:04 +02:00
# TODO: we probably should require a particular version of msbuild, if we are taking this dependency
# msbuild v14 and msbuild v4 behaviors are different for XAML generation
$frameworkMsBuildLocation = " ${env:SystemRoot} \Microsoft.Net\Framework\v4.0.30319\msbuild "
2016-06-23 04:46:16 +02:00
2016-06-24 23:52:04 +02:00
$msbuild = get-command msbuild -ErrorAction SilentlyContinue
2016-08-03 22:23:21 +02:00
if ( $msbuild ) {
2016-06-24 23:52:04 +02:00
# all good, nothing to do
return
2016-06-22 01:54:57 +02:00
}
2016-06-23 04:46:16 +02:00
2016-08-03 22:23:21 +02:00
if ( -not ( Test-Path $frameworkMsBuildLocation ) ) {
2016-06-24 23:52:04 +02:00
throw " msbuild not found in ' $frameworkMsBuildLocation '. Install Visual Studio 2015. "
2016-06-23 04:46:16 +02:00
}
2016-06-24 23:52:04 +02:00
Set-Alias msbuild $frameworkMsBuildLocation -Scope Script
2016-06-22 01:54:57 +02:00
}
2018-03-08 19:47:20 +01:00
function script: Write-Log
{
param
(
[ Parameter ( Position = 0 , Mandatory ) ]
[ ValidateNotNullOrEmpty ( ) ]
[ string ] $message ,
2016-04-02 07:01:05 +02:00
2018-03-08 19:47:20 +01:00
[ switch ] $error
)
if ( $error )
{
Write-Host -Foreground Red $message
}
else
{
Write-Host -Foreground Green $message
}
2016-12-01 20:23:14 +01:00
#reset colors for older package to at return to default after error message on a compilation error
[ console ] :: ResetColor ( )
}
2016-04-02 07:01:05 +02:00
function script: precheck([string]$command , [ string ] $missedMessage ) {
$c = Get-Command $command -ErrorAction SilentlyContinue
if ( -not $c ) {
2017-07-26 01:50:27 +02:00
if ( -not [ string ] :: IsNullOrEmpty ( $missedMessage ) )
2016-07-22 00:53:36 +02:00
{
Write-Warning $missedMessage
}
2016-04-02 07:01:05 +02:00
return $false
} else {
return $true
}
}
2016-04-20 01:59:03 +02:00
# this function wraps native command Execution
# for more information, read https://mnaoumov.wordpress.com/2015/01/11/execution-of-external-commands-in-powershell-done-right/
2018-02-27 00:45:15 +01:00
function script: Start-NativeExecution
2016-04-20 01:59:03 +02:00
{
2018-02-27 00:45:15 +01:00
param (
[ scriptblock ] $sb ,
[ switch ] $IgnoreExitcode ,
[ switch ] $VerboseOutputOnError
)
2016-04-20 01:59:03 +02:00
$backupEAP = $script:ErrorActionPreference
$script:ErrorActionPreference = " Continue "
2016-08-03 22:23:21 +02:00
try {
2018-02-27 00:45:15 +01:00
if ( $VerboseOutputOnError . IsPresent )
{
2018-06-09 01:31:38 +02:00
$output = & $sb 2 > & 1
2018-02-27 00:45:15 +01:00
}
else
{
& $sb
}
2016-08-26 22:46:03 +02:00
# note, if $sb doesn't have a native invocation, $LASTEXITCODE will
2016-04-20 01:59:03 +02:00
# point to the obsolete value
2016-11-20 08:32:42 +01:00
if ( $LASTEXITCODE -ne 0 -and -not $IgnoreExitcode ) {
2018-02-27 00:45:15 +01:00
if ( $VerboseOutputOnError . IsPresent -and $output )
{
$output | Out-String | Write-Verbose -Verbose
}
# Get caller location for easier debugging
$caller = Get-PSCallStack -ErrorAction SilentlyContinue
if ( $caller )
{
$callerLocationParts = $caller [ 1 ] . Location -split " :\s*line\s* "
$callerFile = $callerLocationParts [ 0 ]
$callerLine = $callerLocationParts [ 1 ]
$errorMessage = " Execution of { $sb } by ${callerFile} : line $callerLine failed with exit code $LASTEXITCODE "
throw $errorMessage
}
2016-05-18 23:30:24 +02:00
throw " Execution of { $sb } failed with exit code $LASTEXITCODE "
2016-04-20 01:59:03 +02:00
}
2016-08-03 22:23:21 +02:00
} finally {
2016-04-20 01:59:03 +02:00
$script:ErrorActionPreference = $backupEAP
}
}
2016-04-04 23:20:34 +02:00
2016-08-02 22:33:51 +02:00
function Start-CrossGen {
[ CmdletBinding ( ) ]
param (
[ Parameter ( Mandatory = $true ) ]
[ ValidateNotNullOrEmpty ( ) ]
[ String ]
2016-11-20 21:59:50 +01:00
$PublishPath ,
[ Parameter ( Mandatory = $true ) ]
2018-12-05 19:55:09 +01:00
[ ValidateSet ( " alpine-x64 " ,
" linux-arm " ,
" linux-arm64 " ,
2017-09-18 18:31:07 +02:00
" linux-x64 " ,
2018-06-28 00:30:38 +02:00
" osx-x64 " ,
2017-12-09 00:43:53 +01:00
" win-arm " ,
2018-06-28 00:30:38 +02:00
" win-arm64 " ,
" win7-x64 " ,
" win7-x86 " ) ]
2016-11-20 21:59:50 +01:00
[ string ]
$Runtime
2016-08-02 22:33:51 +02:00
)
2016-08-03 21:35:58 +02:00
2018-10-31 11:45:33 +01:00
function New-CrossGenAssembly {
2016-08-02 22:33:51 +02:00
param (
[ Parameter ( Mandatory = $true ) ]
[ ValidateNotNullOrEmpty ( ) ]
[ String ]
$AssemblyPath ,
[ Parameter ( Mandatory = $true ) ]
[ ValidateNotNullOrEmpty ( ) ]
[ String ]
$CrossgenPath
)
$outputAssembly = $AssemblyPath . Replace ( " .dll " , " .ni.dll " )
$platformAssembliesPath = Split-Path $AssemblyPath -Parent
$crossgenFolder = Split-Path $CrossgenPath
$niAssemblyName = Split-Path $outputAssembly -Leaf
2016-08-03 21:35:58 +02:00
try {
2016-08-02 22:33:51 +02:00
Push-Location $crossgenFolder
# Generate the ngen assembly
2016-08-03 21:35:58 +02:00
Write-Verbose " Generating assembly $niAssemblyName "
2016-08-02 22:33:51 +02:00
Start-NativeExecution {
2016-08-03 09:19:39 +02:00
& $CrossgenPath / MissingDependenciesOK / in $AssemblyPath / out $outputAssembly / Platform_Assemblies_Paths $platformAssembliesPath
} | Write-Verbose
2016-08-03 21:35:58 +02:00
2016-08-02 22:33:51 +02:00
<#
# TODO: Generate the pdb for the ngen binary - currently, there is a hard dependency on diasymreader.dll, which is available at %windir%\Microsoft.NET\Framework\v4.0.30319.
# However, we still need to figure out the prerequisites on Linux.
Start-NativeExecution {
2016-08-03 09:19:39 +02:00
& $CrossgenPath / Platform_Assemblies_Paths $platformAssembliesPath / CreatePDB $platformAssembliesPath / lines $platformAssembliesPath $niAssemblyName
} | Write-Verbose
2016-08-02 22:33:51 +02:00
#>
2016-08-03 21:35:58 +02:00
} finally {
2016-08-02 22:33:51 +02:00
Pop-Location
}
}
2016-08-03 21:35:58 +02:00
if ( -not ( Test-Path $PublishPath ) ) {
2016-08-02 22:33:51 +02:00
throw " Path ' $PublishPath ' does not exist. "
}
2016-08-03 21:35:58 +02:00
# Get the path to crossgen
2017-07-23 21:29:33 +02:00
$crossGenExe = if ( $Environment . IsWindows ) { " crossgen.exe " } else { " crossgen " }
2016-08-02 22:33:51 +02:00
2016-08-03 21:05:26 +02:00
# The crossgen tool is only published for these particular runtimes
2017-07-23 21:29:33 +02:00
$crossGenRuntime = if ( $Environment . IsWindows ) {
2016-11-20 21:59:50 +01:00
if ( $Runtime -match " -x86 " ) {
2017-05-05 02:09:03 +02:00
" win-x86 "
2017-12-09 00:43:53 +01:00
} elseif ( $Runtime -match " -x64 " ) {
2017-05-05 02:09:03 +02:00
" win-x64 "
2017-12-09 00:43:53 +01:00
} elseif ( ! ( $env:PROCESSOR_ARCHITECTURE -match " arm " ) ) {
throw " crossgen for 'win-arm' and 'win-arm64' must be run on that platform "
2016-11-20 21:59:50 +01:00
}
2017-09-11 21:16:58 +02:00
} elseif ( $Runtime -eq " linux-arm " ) {
throw " crossgen is not available for 'linux-arm' "
2018-06-28 00:30:38 +02:00
} else {
$Runtime
2016-08-03 21:05:26 +02:00
}
if ( -not $crossGenRuntime ) {
throw " crossgen is not available for this platform "
}
2018-05-03 01:58:39 +02:00
$dotnetRuntimeVersion = $script:Options . Framework -replace 'netcoreapp'
2016-10-12 19:24:08 +02:00
# Get the CrossGen.exe for the correct runtime with the latest version
2017-07-23 21:29:33 +02:00
$crossGenPath = Get-ChildItem $script:Environment . nugetPackagesRoot $crossGenExe -Recurse | `
2016-10-12 19:24:08 +02:00
Where-Object { $_ . FullName -match $crossGenRuntime } | `
2018-05-03 01:58:39 +02:00
Where-Object { $_ . FullName -match $dotnetRuntimeVersion } | `
2016-10-12 19:24:08 +02:00
Sort-Object -Property FullName -Descending | `
Select-Object -First 1 | `
ForEach-Object { $_ . FullName }
2016-08-03 21:05:26 +02:00
if ( -not $crossGenPath ) {
2016-08-02 22:33:51 +02:00
throw " Unable to find latest version of crossgen.exe. 'Please run Start-PSBuild -Clean' first, and then try again. "
}
2016-11-20 21:59:50 +01:00
Write-Verbose " Matched CrossGen.exe: $crossGenPath " -Verbose
2016-08-02 22:33:51 +02:00
# Crossgen.exe requires the following assemblies:
# mscorlib.dll
# System.Private.CoreLib.dll
2016-08-03 21:35:58 +02:00
# clrjit.dll on Windows or libclrjit.so/dylib on Linux/OS X
2016-08-02 22:33:51 +02:00
$crossGenRequiredAssemblies = @ ( " mscorlib.dll " , " System.Private.CoreLib.dll " )
2016-08-03 21:35:58 +02:00
2017-07-23 21:29:33 +02:00
$crossGenRequiredAssemblies + = if ( $Environment . IsWindows ) {
2016-08-03 21:35:58 +02:00
" clrjit.dll "
2017-07-23 21:29:33 +02:00
} elseif ( $Environment . IsLinux ) {
2016-08-03 21:35:58 +02:00
" libclrjit.so "
2017-09-07 19:34:40 +02:00
} elseif ( $Environment . IsMacOS ) {
2016-08-03 21:35:58 +02:00
" libclrjit.dylib "
2016-08-02 22:33:51 +02:00
}
2016-08-03 21:35:58 +02:00
# Make sure that all dependencies required by crossgen are at the directory.
2016-08-02 22:33:51 +02:00
$crossGenFolder = Split-Path $crossGenPath
2016-08-03 21:35:58 +02:00
foreach ( $assemblyName in $crossGenRequiredAssemblies ) {
if ( -not ( Test-Path " $crossGenFolder \ $assemblyName " ) ) {
2016-08-02 22:33:51 +02:00
Copy-Item -Path " $PublishPath \ $assemblyName " -Destination $crossGenFolder -Force -ErrorAction Stop
}
}
2017-05-16 08:34:32 +02:00
# Common assemblies used by Add-Type or assemblies with high JIT and no pdbs to crossgen
2017-02-04 00:19:05 +01:00
$commonAssembliesForAddType = @ (
" Microsoft.CodeAnalysis.CSharp.dll "
" Microsoft.CodeAnalysis.dll "
2017-05-16 08:34:32 +02:00
" System.Linq.Expressions.dll "
" Microsoft.CSharp.dll "
" System.Runtime.Extensions.dll "
" System.Linq.dll "
" System.Collections.Concurrent.dll "
" System.Collections.dll "
" Newtonsoft.Json.dll "
" System.IO.FileSystem.dll "
" System.Diagnostics.Process.dll "
" System.Threading.Tasks.Parallel.dll "
" System.Security.AccessControl.dll "
" System.Text.Encoding.CodePages.dll "
" System.Private.Uri.dll "
" System.Threading.dll "
" System.Security.Principal.Windows.dll "
" System.Console.dll "
" Microsoft.Win32.Registry.dll "
" System.IO.Pipes.dll "
" System.Diagnostics.FileVersionInfo.dll "
" System.Collections.Specialized.dll "
2018-12-02 21:52:39 +01:00
" Microsoft.ApplicationInsights.dll "
2017-02-04 00:19:05 +01:00
)
2016-08-03 21:35:58 +02:00
# Common PowerShell libraries to crossgen
2016-08-03 09:19:39 +02:00
$psCoreAssemblyList = @ (
" Microsoft.PowerShell.Commands.Utility.dll " ,
" Microsoft.PowerShell.Commands.Management.dll " ,
" Microsoft.PowerShell.Security.dll " ,
" Microsoft.PowerShell.CoreCLR.Eventing.dll " ,
" Microsoft.PowerShell.ConsoleHost.dll " ,
" System.Management.Automation.dll "
)
2016-08-02 22:33:51 +02:00
2016-08-03 21:35:58 +02:00
# Add Windows specific libraries
2017-07-23 21:29:33 +02:00
if ( $Environment . IsWindows ) {
2016-08-03 09:19:39 +02:00
$psCoreAssemblyList + = @ (
" Microsoft.WSMan.Management.dll " ,
" Microsoft.WSMan.Runtime.dll " ,
" Microsoft.PowerShell.Commands.Diagnostics.dll " ,
" Microsoft.Management.Infrastructure.CimCmdlets.dll "
)
}
2017-02-04 00:19:05 +01:00
$fullAssemblyList = $commonAssembliesForAddType + $psCoreAssemblyList
foreach ( $assemblyName in $fullAssemblyList ) {
2016-08-02 22:33:51 +02:00
$assemblyPath = Join-Path $PublishPath $assemblyName
2018-10-31 11:45:33 +01:00
New-CrossGenAssembly -CrossgenPath $crossGenPath -AssemblyPath $assemblyPath
2016-08-03 09:19:39 +02:00
}
2016-08-02 22:33:51 +02:00
2016-10-18 22:12:51 +02:00
#
# With the latest dotnet.exe, the default load context is only able to load TPAs, and TPA
# only contains IL assembly names. In order to make the default load context able to load
# the NI PS assemblies, we need to replace the IL PS assemblies with the corresponding NI
# PS assemblies, but with the same IL assembly names.
#
Write-Verbose " PowerShell Ngen assemblies have been generated. Deploying ... " -Verbose
2017-02-04 00:19:05 +01:00
foreach ( $assemblyName in $fullAssemblyList ) {
2016-08-03 09:19:39 +02:00
# Remove the IL assembly and its symbols.
2016-08-04 02:35:05 +02:00
$assemblyPath = Join-Path $PublishPath $assemblyName
2016-10-18 22:12:51 +02:00
$symbolsPath = [ System.IO.Path ] :: ChangeExtension ( $assemblyPath , " .pdb " )
2017-02-04 00:19:05 +01:00
2016-10-18 22:12:51 +02:00
Remove-Item $assemblyPath -Force -ErrorAction Stop
2017-02-04 00:19:05 +01:00
# No symbols are available for Microsoft.CodeAnalysis.CSharp.dll, Microsoft.CodeAnalysis.dll,
# Microsoft.CodeAnalysis.VisualBasic.dll, and Microsoft.CSharp.dll.
if ( $commonAssembliesForAddType -notcontains $assemblyName ) {
Remove-Item $symbolsPath -Force -ErrorAction Stop
}
2016-10-18 22:12:51 +02:00
# Rename the corresponding ni.dll assembly to be the same as the IL assembly
$niAssemblyPath = [ System.IO.Path ] :: ChangeExtension ( $assemblyPath , " ni.dll " )
Rename-Item $niAssemblyPath $assemblyPath -Force -ErrorAction Stop
2016-08-02 22:33:51 +02:00
}
}
2016-07-01 01:02:07 +02:00
2017-01-17 22:00:52 +01:00
# Cleans the PowerShell repo - everything but the root folder
2016-08-31 21:13:05 +02:00
function Clear-PSRepo
{
[ CmdletBinding ( ) ]
2017-01-17 22:00:52 +01:00
param ( )
Get-ChildItem $PSScriptRoot \ * -Directory | ForEach-Object {
2017-01-16 22:31:14 +01:00
Write-Verbose " Cleaning $_ ... "
2016-08-31 21:13:05 +02:00
git clean -fdX $_
}
}
2016-11-17 22:54:38 +01:00
# Install PowerShell modules such as PackageManagement, PowerShellGet
2018-02-27 22:36:54 +01:00
function Copy-PSGalleryModules
2016-11-17 22:54:38 +01:00
{
[ CmdletBinding ( ) ]
param (
[ Parameter ( Mandatory = $true ) ]
2018-11-30 20:49:38 +01:00
[ string ] $CsProjPath ,
[ Parameter ( Mandatory = $true ) ]
[ string ] $Destination ,
[ Parameter ( ) ]
[ switch ] $Force
2018-02-27 22:36:54 +01:00
)
2018-01-23 01:09:35 +01:00
2018-02-27 22:36:54 +01:00
if ( ! $Destination . EndsWith ( " Modules " ) ) {
throw " Installing to an unexpected location "
}
2016-11-17 22:54:38 +01:00
2018-03-22 19:22:16 +01:00
Find-DotNet
2018-11-30 20:49:38 +01:00
Restore-PSPackage -ProjectDirs ( Split-Path $CsProjPath ) -Force: $Force . IsPresent
2018-03-22 19:22:16 +01:00
2018-02-27 22:36:54 +01:00
$cache = dotnet nuget locals global-packages -l
if ( $cache -match " info : global-packages: (.*) " ) {
$nugetCache = $matches [ 1 ]
}
else {
throw " Can't find nuget global cache "
}
2016-11-17 22:54:38 +01:00
2018-11-30 20:49:38 +01:00
$psGalleryProj = [ xml ] ( Get-Content -Raw $CsProjPath )
2018-01-23 01:09:35 +01:00
2018-02-27 22:36:54 +01:00
foreach ( $m in $psGalleryProj . Project . ItemGroup . PackageReference ) {
$name = $m . Include
$version = $m . Version
2018-03-08 19:47:20 +01:00
Write-Log " Name=' $Name ', Version=' $version ', Destination=' $Destination ' "
2016-11-17 22:54:38 +01:00
2018-02-27 22:36:54 +01:00
# Remove the build revision from the src (nuget drops it).
2018-08-16 06:53:00 +02:00
$srcVer = if ( $version -match " (\d+.\d+.\d+).0 " ) {
2018-02-27 22:36:54 +01:00
$matches [ 1 ]
} else {
$version
2018-01-23 01:09:35 +01:00
}
2016-11-17 22:54:38 +01:00
2018-02-27 22:36:54 +01:00
# Nuget seems to always use lowercase in the cache
$src = " $nugetCache / $( $name . ToLower ( ) ) / $srcVer "
2018-08-14 19:15:05 +02:00
$dest = " $Destination / $name "
2018-01-23 01:09:35 +01:00
2018-02-27 22:36:54 +01:00
Remove-Item -Force -ErrorAction Ignore -Recurse " $Destination / $name "
New-Item -Path $dest -ItemType Directory -Force -ErrorAction Stop > $null
$dontCopy = '*.nupkg' , '*.nupkg.sha512' , '*.nuspec' , 'System.Runtime.InteropServices.RuntimeInformation.dll'
2018-03-01 06:08:17 +01:00
Copy-Item -Exclude $dontCopy -Recurse $src / * $dest
2016-11-17 22:54:38 +01:00
}
}
2018-05-11 02:46:45 +02:00
function Merge-TestLogs
{
[ CmdletBinding ( ) ]
param (
[ Parameter ( Mandatory = $true ) ]
[ ValidateScript ( { Test-Path $_ } ) ]
[ string ] $XUnitLogPath ,
[ Parameter ( Mandatory = $true ) ]
[ ValidateScript ( { Test-Path $_ } ) ]
[ string[] ] $NUnitLogPath ,
[ Parameter ( ) ]
[ ValidateScript ( { Test-Path $_ } ) ]
[ string[] ] $AdditionalXUnitLogPath ,
[ Parameter ( ) ]
[ string ] $OutputLogPath
)
# Convert all the NUnit logs into single object
$convertedNUnit = ConvertFrom-PesterLog -logFile $NUnitLogPath
$xunit = [ xml ] ( Get-Content $XUnitLogPath -ReadCount 0 -Raw )
$strBld = [ System.Text.StringBuilder ] :: new ( $xunit . assemblies . InnerXml )
foreach ( $assembly in $convertedNUnit . assembly )
{
$strBld . Append ( $assembly . ToString ( ) ) | Out-Null
}
foreach ( $path in $AdditionalXUnitLogPath )
{
$addXunit = [ xml ] ( Get-Content $path -ReadCount 0 -Raw )
$strBld . Append ( $addXunit . assemblies . InnerXml ) | Out-Null
}
$xunit . assemblies . InnerXml = $strBld . ToString ( )
$xunit . Save ( $OutputLogPath )
}
function ConvertFrom-PesterLog {
[ CmdletBinding ( ) ]
param (
[ Parameter ( ValueFromPipeline = $true , Mandatory = $true , Position = 0 ) ]
[ string[] ] $Logfile ,
[ Parameter ( ) ] [ switch ] $IncludeEmpty ,
[ Parameter ( ) ] [ switch ] $MultipleLog
)
<#
Convert our test logs to
xunit schema - top level assemblies
Pester conversion
foreach $r in " test-results " . " test-suite " . results . " test-suite "
assembly
name = $r . Description
config-file = log file ( this is the only way we can determine between admin / nonadmin log )
test-framework = Pester
environment = top-level " test-results.environment.platform
run-date = date ( doesn ' t exist in pester except for beginning )
run-time = time
time =
#>
BEGIN {
# CLASSES
class assemblies {
# attributes
[ datetime ] $timestamp
# child elements
[ System.Collections.Generic.List[testAssembly] ] $assembly
assemblies ( ) {
$this . timestamp = [ datetime ] :: now
$this . assembly = [ System.Collections.Generic.List[testAssembly] ] :: new ( )
}
static [ assemblies ] op_Addition ( [ assemblies ] $ls , [ assemblies ] $rs ) {
$newAssembly = [ assemblies ] :: new ( )
$newAssembly . assembly . AddRange ( $ls . assembly )
$newAssembly . assembly . AddRange ( $rs . assembly )
return $newAssembly
}
[ string ] ToString ( ) {
$sb = [ text.stringbuilder ] :: new ( )
$sb . AppendLine ( '<assemblies timestamp="{0:MM}/{0:dd}/{0:yyyy} {0:HH}:{0:mm}:{0:ss}">' -f $this . timestamp )
foreach ( $a in $this . assembly ) {
$sb . Append ( " $a " )
}
$sb . AppendLine ( " </assemblies> " ) ;
return $sb . ToString ( )
}
# use Write-Output to emit these into the pipeline
[ array ] GetTests ( ) {
return $this . Assembly . collection . test
}
}
class testAssembly {
# attributes
[ string ] $name # path to pester file
[ string ] $ { config-file }
[ string ] $ { test-framework } # Pester
[ string ] $environment
[ string ] $ { run-date }
[ string ] $ { run-time }
[ decimal ] $time
[ int ] $total
[ int ] $passed
[ int ] $failed
[ int ] $skipped
[ int ] $errors
testAssembly ( ) {
$this . " config-file " = " no config "
$this . " test-framework " = " Pester "
$this . environment = $script:environment
$this . " run-date " = $script:rundate
$this . " run-time " = $script:runtime
$this . collection = [ System.Collections.Generic.List[collection] ] :: new ( )
}
# child elements
[ error[] ] $error
[ System.Collections.Generic.List[collection] ] $collection
[ string ] ToString ( ) {
$sb = [ System.Text.StringBuilder ] :: new ( )
$sb . AppendFormat ( ' <assembly name="{0}" ' , $this . name )
$sb . AppendFormat ( 'environment="{0}" ' , [ security.securityelement ] :: escape ( $this . environment ) )
$sb . AppendFormat ( 'test-framework="{0}" ' , $this . " test-framework " )
$sb . AppendFormat ( 'run-date="{0}" ' , $this . " run-date " )
$sb . AppendFormat ( 'run-time="{0}" ' , $this . " run-time " )
$sb . AppendFormat ( 'total="{0}" ' , $this . total )
$sb . AppendFormat ( 'passed="{0}" ' , $this . passed )
$sb . AppendFormat ( 'failed="{0}" ' , $this . failed )
$sb . AppendFormat ( 'skipped="{0}" ' , $this . skipped )
$sb . AppendFormat ( 'time="{0}" ' , $this . time )
$sb . AppendFormat ( 'errors="{0}" ' , $this . errors )
$sb . AppendLine ( " > " )
if ( $this . error ) {
$sb . AppendLine ( " <errors> " )
foreach ( $e in $this . error ) {
$sb . AppendLine ( $e . ToString ( ) )
}
$sb . AppendLine ( " </errors> " )
} else {
$sb . AppendLine ( " <errors /> " )
}
foreach ( $col in $this . collection ) {
$sb . AppendLine ( $col . ToString ( ) )
}
$sb . AppendLine ( " </assembly> " )
return $sb . ToString ( )
}
}
class collection {
# attributes
[ string ] $name
[ decimal ] $time
[ int ] $total
[ int ] $passed
[ int ] $failed
[ int ] $skipped
# child element
[ System.Collections.Generic.List[test] ] $test
# constructor
collection ( ) {
$this . test = [ System.Collections.Generic.List[test] ] :: new ( )
}
[ string ] ToString ( ) {
$sb = [ Text.StringBuilder ] :: new ( )
if ( $this . test . count -eq 0 ) {
$sb . AppendLine ( " <collection /> " )
} else {
$sb . AppendFormat ( ' <collection total="{0}" passed="{1}" failed="{2}" skipped="{3}" name="{4}" time="{5}">' + " `n " ,
$this . total , $this . passed , $this . failed , $this . skipped , [ security.securityelement ] :: escape ( $this . name ) , $this . time )
foreach ( $t in $this . test ) {
$sb . AppendLine ( " " + $t . ToString ( ) ) ;
}
$sb . Append ( " </collection> " )
}
return $sb . ToString ( )
}
}
class errors {
[ error[] ] $error
}
class error {
# attributes
[ string ] $type
[ string ] $name
# child elements
[ failure ] $failure
[ string ] ToString ( ) {
$sb = [ system.text.stringbuilder ] :: new ( )
$sb . AppendLine ( '<error type="{0}" name="{1}" >' -f $this . type , [ security.securityelement ] :: escape ( $this . Name ) )
$sb . AppendLine ( $this . failure -as [ string ] )
$sb . AppendLine ( " </error> " )
return $sb . ToString ( )
}
}
class cdata {
[ string ] $text
cdata ( [ string ] $s ) { $this . text = $s }
[ string ] ToString ( ) {
return '<![CDATA[' + [ security.securityelement ] :: escape ( $this . text ) + ']]>'
}
}
class failure {
[ string ] $ { exception-type }
[ cdata ] $message
[ cdata ] $ { stack-trace }
failure ( [ string ] $message , [ string ] $stack ) {
$this . " exception-type " = " Pester "
$this . Message = [ cdata ] :: new ( $message )
$this . " stack-trace " = [ cdata ] :: new ( $stack )
}
[ string ] ToString ( ) {
$sb = [ text.stringbuilder ] :: new ( )
$sb . AppendLine ( " <failure> " )
$sb . AppendLine ( " <message> " + ( $this . message -as [ string ] ) + " </message> " )
$sb . AppendLine ( " <stack-trace> " + ( $this . " stack-trace " -as [ string ] ) + " </stack-trace> " )
$sb . Append ( " </failure> " )
return $sb . ToString ( )
}
}
enum resultenum {
Pass
Fail
Skip
}
class trait {
# attributes
[ string ] $name
[ string ] $value
}
class traits {
[ trait[] ] $trait
}
class test {
# attributes
[ string ] $name
[ string ] $type
[ string ] $method
[ decimal ] $time
[ resultenum ] $result
# child elements
[ trait[] ] $traits
[ failure ] $failure
[ cdata ] $reason # skip reason
[ string ] ToString ( ) {
$sb = [ text.stringbuilder ] :: new ( )
$sb . appendformat ( ' <test name="{0}" type="{1}" method="{2}" time="{3}" result="{4}"' ,
[ security.securityelement ] :: escape ( $this . name ) , [ security.securityelement ] :: escape ( $this . type ) ,
[ security.securityelement ] :: escape ( $this . method ) , $this . time , $this . result )
if ( $this . failure ) {
$sb . AppendLine ( " > " )
$sb . AppendLine ( $this . failure -as [ string ] )
$sb . append ( ' </test>' )
} else {
$sb . Append ( " /> " )
}
return $sb . ToString ( )
}
}
function convert-pesterlog ( [ xml ] $x , $logpath , [ switch ] $includeEmpty ) {
<# $resultMap = @{
Success = " Pass "
Ignored = " Skip "
Failure = " Fail "
} #>
$resultMap = @ {
Success = " Pass "
Ignored = " Skip "
Failure = " Fail "
Inconclusive = " Skip "
}
$configfile = $logpath
$runtime = $x . " test-results " . time
$environment = $x . " test-results " . environment . platform + " - " + $x . " test-results " . environment . " os-version "
$rundate = $x . " test-results " . date
$suites = $x . " test-results " . " test-suite " . results . " test-suite "
$assemblies = [ assemblies ] :: new ( )
foreach ( $suite in $suites ) {
$tCases = $suite . SelectNodes ( " .//test-case " )
# only create an assembly group if we have tests
if ( $tCases . count -eq 0 -and ! $includeEmpty ) { continue }
$tGroup = $tCases | Group-Object result
$total = $tCases . Count
$asm = [ testassembly ] :: new ( )
$asm . environment = $environment
$asm . " run-date " = $rundate
$asm . " run-time " = $runtime
$asm . Name = $suite . name
$asm . " config-file " = $configfile
$asm . time = $suite . time
$asm . total = $suite . SelectNodes ( " .//test-case " ) . Count
2018-10-09 08:16:50 +02:00
$asm . Passed = $tGroup | Where-Object -FilterScript { $_ . Name -eq " Success " } | ForEach-Object -Process { $_ . Count }
$asm . Failed = $tGroup | Where-Object -FilterScript { $_ . Name -eq " Failure " } | ForEach-Object -Process { $_ . Count }
$asm . Skipped = $tGroup | Where-Object -FilterScript { $_ . Name -eq " Ignored " } | ForEach-Object -Process { $_ . Count }
$asm . Skipped + = $tGroup | Where-Object -FilterScript { $_ . Name -eq " Inconclusive " } | ForEach-Object -Process { $_ . Count }
2018-05-11 02:46:45 +02:00
$c = [ collection ] :: new ( )
$c . passed = $asm . Passed
$c . failed = $asm . failed
$c . skipped = $asm . skipped
$c . total = $asm . total
$c . time = $asm . time
$c . name = $asm . name
foreach ( $tc in $suite . SelectNodes ( " .//test-case " ) ) {
if ( $tc . result -match " Success|Ignored|Failure " ) {
$t = [ test ] :: new ( )
$t . name = $tc . Name
$t . time = $tc . time
$t . method = $tc . description # the pester actually puts the name of the "it" as description
$t . type = $suite . results . " test-suite " . description | Select-Object -First 1
$t . result = $resultMap [ $tc . result ]
if ( $tc . failure ) {
$t . failure = [ failure ] :: new ( $tc . failure . message , $tc . failure . " stack-trace " )
}
$null = $c . test . Add ( $t )
}
}
$null = $asm . collection . add ( $c )
$assemblies . assembly . Add ( $asm )
}
$assemblies
}
# convert it to our object model
# a simple conversion
function convert-xunitlog {
param ( $x , $logpath )
$asms = [ assemblies ] :: new ( )
$asms . timestamp = $x . assemblies . timestamp
foreach ( $assembly in $x . assemblies . assembly ) {
$asm = [ testAssembly ] :: new ( )
$asm . environment = $assembly . environment
$asm . " test-framework " = $assembly . " test-framework "
$asm . " run-date " = $assembly . " run-date "
$asm . " run-time " = $assembly . " run-time "
$asm . total = $assembly . total
$asm . passed = $assembly . passed
$asm . failed = $assembly . failed
$asm . skipped = $assembly . skipped
$asm . time = $assembly . time
$asm . name = $assembly . name
foreach ( $coll in $assembly . collection ) {
$c = [ collection ] :: new ( )
$c . name = $coll . name
$c . total = $coll . total
$c . passed = $coll . passed
$c . failed = $coll . failed
$c . skipped = $coll . skipped
$c . time = $coll . time
foreach ( $t in $coll . test ) {
$test = [ test ] :: new ( )
$test . name = $t . name
$test . type = $t . type
$test . method = $t . method
$test . time = $t . time
$test . result = $t . result
$c . test . Add ( $test )
}
$null = $asm . collection . add ( $c )
}
$null = $asms . assembly . add ( $asm )
}
$asms
}
$Logs = @ ( )
}
PROCESS {
#### MAIN ####
foreach ( $log in $Logfile ) {
foreach ( $logpath in ( resolve-path $log ) . path ) {
write-progress " converting file $logpath "
if ( ! $logpath ) { throw " Cannot resolve $Logfile " }
$x = [ xml ] ( get-content -raw -readcount 0 $logpath )
if ( $x . psobject . properties [ 'test-results' ] ) {
$Logs + = convert-pesterlog $x $logpath -includeempty: $includeempty
} elseif ( $x . psobject . properties [ 'assemblies' ] ) {
$Logs + = convert-xunitlog $x $logpath -includeEmpty: $includeEmpty
} else {
write-error " Cannot determine log type "
}
}
}
}
END {
if ( $MultipleLog ) {
$Logs
} else {
$combinedLog = $Logs [ 0 ]
for ( $i = 1 ; $i -lt $logs . count ; $i + + ) {
$combinedLog + = $Logs [ $i ]
}
$combinedLog
}
}
}
2018-05-18 04:04:20 +02:00
# Save PSOptions to be restored by Restore-PSOptions
function Save-PSOptions {
param (
[ ValidateScript ( { $parent = Split-Path $_ ; if ( $parent ) { Test-Path $parent } else { return $true } } ) ]
[ ValidateNotNullOrEmpty ( ) ]
[ string ]
$PSOptionsPath = ( Join-Path -Path $PSScriptRoot -ChildPath 'psoptions.json' ) ,
[ ValidateNotNullOrEmpty ( ) ]
[ object ]
$Options = ( Get-PSOptions -DefaultToNew )
)
$Options | ConvertTo-Json -Depth 3 | Out-File -Encoding utf8 -FilePath $PSOptionsPath
}
# Restore PSOptions
# Optionally remove the PSOptions file
function Restore-PSOptions {
param (
[ ValidateScript ( { Test-Path $_ } ) ]
[ string ]
$PSOptionsPath = ( Join-Path -Path $PSScriptRoot -ChildPath 'psoptions.json' ) ,
[ switch ]
$Remove
)
$options = Get-Content -Path $PSOptionsPath | ConvertFrom-Json
if ( $Remove )
{
# Remove PSOptions.
# The file is only used to set the PSOptions.
Remove-Item -Path $psOptionsPath
}
Set-PSOptions -Options $options
}
2018-08-18 07:06:51 +02:00
# Save PSOptions to be restored by Restore-PSOptions
function Save-PSOptions {
param (
[ ValidateScript ( { $parent = Split-Path $_ ; if ( $parent ) { Test-Path $parent } else { return $true } } ) ]
[ ValidateNotNullOrEmpty ( ) ]
[ string ]
$PSOptionsPath = ( Join-Path -Path $PSScriptRoot -ChildPath 'psoptions.json' ) ,
[ ValidateNotNullOrEmpty ( ) ]
[ object ]
$Options = ( Get-PSOptions -DefaultToNew )
)
$Options | ConvertTo-Json -Depth 3 | Out-File -Encoding utf8 -FilePath $PSOptionsPath
}
# Restore PSOptions
# Optionally remove the PSOptions file
function Restore-PSOptions {
param (
[ ValidateScript ( { Test-Path $_ } ) ]
[ string ]
$PSOptionsPath = ( Join-Path -Path $PSScriptRoot -ChildPath 'psoptions.json' ) ,
[ switch ]
$Remove
)
$options = Get-Content -Path $PSOptionsPath | ConvertFrom-Json
if ( $Remove )
{
# Remove PSOptions.
# The file is only used to set the PSOptions.
Remove-Item -Path $psOptionsPath -Force
}
$newOptions = New-PSOptionsObject `
-RootInfo $options . RootInfo `
-Top $options . Top `
-Runtime $options . Runtime `
-Crossgen $options . Crossgen `
-Configuration $options . Configuration `
-PSModuleRestore $options . PSModuleRestore `
-Framework $options . Framework `
-Output $options . Output
Set-PSOptions -Options $newOptions
}
function New-PSOptionsObject
{
param (
[ PSCustomObject ]
$RootInfo ,
[ Parameter ( Mandatory ) ]
[ String ]
$Top ,
[ Parameter ( Mandatory ) ]
[ String ]
$Runtime ,
[ Parameter ( Mandatory ) ]
[ Bool ]
$CrossGen ,
[ Parameter ( Mandatory ) ]
[ String ]
$Configuration ,
[ Parameter ( Mandatory ) ]
[ Bool ]
$PSModuleRestore ,
[ Parameter ( Mandatory ) ]
[ String ]
$Framework ,
[ Parameter ( Mandatory ) ]
[ String ]
$Output
)
return @ {
RootInfo = $RootInfo
Top = $Top
Configuration = $Configuration
Framework = $Framework
Runtime = $Runtime
Output = $Output
CrossGen = $CrossGen
PSModuleRestore = $PSModuleRestore
}
}
2016-07-01 01:02:07 +02:00
$script:RESX_TEMPLATE = @ '
< ? xml version = " 1.0 " encoding = " utf-8 " ? >
< root >
2016-08-03 22:23:21 +02:00
< ! - -
Microsoft ResX Schema
2016-07-01 01:02:07 +02:00
Version 2.0
2016-08-03 22:23:21 +02:00
The primary goals of this format is to allow a simple XML format
that is mostly human readable . The generation and parsing of the
various data types are done through the TypeConverter classes
2016-07-01 01:02:07 +02:00
associated with the data types .
2016-08-03 22:23:21 +02:00
2016-07-01 01:02:07 +02:00
Example :
2016-08-03 22:23:21 +02:00
2016-07-01 01:02:07 +02:00
. . . ado . net / XML headers & schema . . .
< resheader name = " resmimetype " > text / microsoft-resx < / resheader >
< resheader name = " version " > 2.0 < / resheader >
< resheader name = " reader " > System . Resources . ResXResourceReader , System . Windows . Forms , . . . < / resheader >
< resheader name = " writer " > System . Resources . ResXResourceWriter , System . Windows . Forms , . . . < / resheader >
< data name = " Name1 " > < value > this is my long string < / value > < comment > this is a comment < / comment > < / data >
< data name = " Color1 " type = " System.Drawing.Color, System.Drawing " > Blue < / data >
< data name = " Bitmap1 " mimetype = " application/x-microsoft.net.object.binary.base64 " >
< value > [ base64 mime encoded serialized .NET Framework object ] < / value >
< / data >
< data name = " Icon1 " type = " System.Drawing.Icon, System.Drawing " mimetype = " application/x-microsoft.net.object.bytearray.base64 " >
< value > [ base64 mime encoded string representing a byte array form of the .NET Framework object ] < / value >
< comment > This is a comment < / comment >
< / data >
2016-08-03 22:23:21 +02:00
There are any number of " resheader " rows that contain simple
2016-07-01 01:02:07 +02:00
name / value pairs .
2016-08-03 22:23:21 +02:00
Each data row contains a name , and value . The row also contains a
type or mimetype . Type corresponds to a . NET class that support
text / value conversion through the TypeConverter architecture .
Classes that don ' t support this are serialized and stored with the
2016-07-01 01:02:07 +02:00
mimetype set .
2016-08-03 22:23:21 +02:00
The mimetype is used for serialized objects , and tells the
ResXResourceReader how to depersist the object . This is currently not
2016-07-01 01:02:07 +02:00
extensible . For a given mimetype the value must be set accordingly :
2016-08-03 22:23:21 +02:00
Note - application / x-microsoft . net . object . binary . base64 is the format
that the ResXResourceWriter will generate , however the reader can
2016-07-01 01:02:07 +02:00
read any of the formats listed below .
2016-08-03 22:23:21 +02:00
2016-07-01 01:02:07 +02:00
mimetype : application / x-microsoft . net . object . binary . base64
2016-08-03 22:23:21 +02:00
value : The object must be serialized with
2016-07-01 01:02:07 +02:00
: System . Runtime . Serialization . Formatters . Binary . BinaryFormatter
: and then encoded with base64 encoding .
2016-08-03 22:23:21 +02:00
2016-07-01 01:02:07 +02:00
mimetype : application / x-microsoft . net . object . soap . base64
2016-08-03 22:23:21 +02:00
value : The object must be serialized with
2016-07-01 01:02:07 +02:00
: System . Runtime . Serialization . Formatters . Soap . SoapFormatter
: and then encoded with base64 encoding .
mimetype : application / x-microsoft . net . object . bytearray . base64
2016-08-03 22:23:21 +02:00
value : The object must be serialized into a byte array
2016-07-01 01:02:07 +02:00
: using a System . ComponentModel . TypeConverter
: and then encoded with base64 encoding .
- - >
< xsd : schema id = " root " xmlns = " " xmlns : xsd = " http://www.w3.org/2001/XMLSchema " xmlns : msdata = " urn:schemas-microsoft-com:xml-msdata " >
< xsd : import namespace = " http://www.w3.org/XML/1998/namespace " / >
< xsd : element name = " root " msdata : IsDataSet = " true " >
< xsd : complexType >
< xsd : choice maxOccurs = " unbounded " >
< xsd : element name = " metadata " >
< xsd : complexType >
< xsd : sequence >
< xsd : element name = " value " type = " xsd:string " minOccurs = " 0 " / >
< / xsd : sequence >
< xsd : attribute name = " name " use = " required " type = " xsd:string " / >
< xsd : attribute name = " type " type = " xsd:string " / >
< xsd : attribute name = " mimetype " type = " xsd:string " / >
< xsd : attribute ref = " xml:space " / >
< / xsd : complexType >
< / xsd : element >
< xsd : element name = " assembly " >
< xsd : complexType >
< xsd : attribute name = " alias " type = " xsd:string " / >
< xsd : attribute name = " name " type = " xsd:string " / >
< / xsd : complexType >
< / xsd : element >
< xsd : element name = " data " >
< xsd : complexType >
< xsd : sequence >
< xsd : element name = " value " type = " xsd:string " minOccurs = " 0 " msdata : Ordinal = " 1 " / >
< xsd : element name = " comment " type = " xsd:string " minOccurs = " 0 " msdata : Ordinal = " 2 " / >
< / xsd : sequence >
< xsd : attribute name = " name " type = " xsd:string " use = " required " msdata : Ordinal = " 1 " / >
< xsd : attribute name = " type " type = " xsd:string " msdata : Ordinal = " 3 " / >
< xsd : attribute name = " mimetype " type = " xsd:string " msdata : Ordinal = " 4 " / >
< xsd : attribute ref = " xml:space " / >
< / xsd : complexType >
< / xsd : element >
< xsd : element name = " resheader " >
< xsd : complexType >
< xsd : sequence >
< xsd : element name = " value " type = " xsd:string " minOccurs = " 0 " msdata : Ordinal = " 1 " / >
< / xsd : sequence >
< xsd : attribute name = " name " type = " xsd:string " use = " required " / >
< / xsd : complexType >
< / xsd : element >
< / xsd : choice >
< / xsd : complexType >
< / xsd : element >
< / xsd : schema >
< resheader name = " resmimetype " >
< value > text / microsoft-resx < / value >
< / resheader >
< resheader name = " version " >
< value > 2.0 < / value >
< / resheader >
< resheader name = " reader " >
< value > System . Resources . ResXResourceReader , System . Windows . Forms , Version = 2.0 . 0 . 0 , Culture = neutral , PublicKeyToken = b77a5c561934e089 < / value >
< / resheader >
< resheader name = " writer " >
< value > System . Resources . ResXResourceWriter , System . Windows . Forms , Version = 2.0 . 0 . 0 , Culture = neutral , PublicKeyToken = b77a5c561934e089 < / value >
< / resheader >
{ 0 }
< / root >
2016-07-05 19:36:38 +02:00
' @
2018-10-19 23:56:58 +02:00
2018-11-26 19:44:42 +01:00
function Get-UniquePackageFolderName {
param (
[ Parameter ( Mandatory ) ] $Root
)
$packagePath = Join-Path $Root 'TestPackage'
$triesLeft = 10
while ( Test-Path $packagePath ) {
$suffix = Get-Random
# Not using Guid to avoid maxpath problems as in example below.
# Example: 'TestPackage-ba0ae1db-8512-46c5-8b6c-1862d33a2d63\test\powershell\Modules\Microsoft.PowerShell.Security\TestData\CatalogTestData\UserConfigProv\DSCResources\UserConfigProviderModVersion1\UserConfigProviderModVersion1.schema.mof'
$packagePath = Join-Path $Root " TestPackage_ $suffix "
$triesLeft - -
if ( $triesLeft -le 0 ) {
throw " Could find unique folder name for package path "
}
}
$packagePath
}
2018-10-19 23:56:58 +02:00
function New-TestPackage
{
[ CmdletBinding ( ) ]
param (
[ Parameter ( Mandatory = $true ) ]
[ string ] $Destination
)
if ( Test-Path $Destination -PathType Leaf )
{
throw " Destination: ' $Destination ' is not a directory or does not exist. "
}
else
{
$null = New-Item -Path $Destination -ItemType Directory -Force
Write-Verbose -Message " Creating destination folder: $Destination "
}
2018-10-24 01:31:04 +02:00
$rootFolder = $env:TEMP
2018-11-26 19:44:42 +01:00
# In some build agents, typically macOS on AzDevOps, $env:TEMP might not be set.
2018-10-24 01:31:04 +02:00
if ( -not $rootFolder -and $env:TF_BUILD ) {
$rootFolder = $env:AGENT_WORKFOLDER
}
2018-10-29 22:13:05 +01:00
Write-Verbose -Verbose " RootFolder: $rootFolder "
2018-11-26 19:44:42 +01:00
$packageRoot = Get-UniquePackageFolderName -Root $rootFolder
2018-10-29 22:13:05 +01:00
2018-10-19 23:56:58 +02:00
$null = New-Item -ItemType Directory -Path $packageRoot -Force
$packagePath = Join-Path $Destination " TestPackage.zip "
2018-10-29 22:13:05 +01:00
Write-Verbose -Verbose " PackagePath: $packagePath "
2018-10-19 23:56:58 +02:00
# Build test tools so they are placed in appropriate folders under 'test' then copy to package root.
$null = Publish-PSTestTools
$powerShellTestRoot = Join-Path $PSScriptRoot 'test'
Copy-Item $powerShellTestRoot -Recurse -Destination $packageRoot -Force
Write-Verbose -Message " Copied test directory "
# Copy assests folder to package root for wix related tests.
$assetsPath = Join-Path $PSScriptRoot 'assets'
Copy-Item $assetsPath -Recurse -Destination $packageRoot -Force
Write-Verbose -Message " Copied assests directory "
# Create expected folder structure for resx files in package root.
$srcRootForResx = New-Item -Path " $packageRoot /src " -Force -ItemType Directory
$resourceDirectories = Get-ChildItem -Recurse " $PSScriptRoot /src " -Directory -Filter 'resources'
$resourceDirectories | ForEach-Object {
$directoryFullName = $_ . FullName
$partToRemove = Join-Path $PSScriptRoot " src "
$assemblyPart = $directoryFullName . Replace ( $partToRemove , '' )
$assemblyPart = $assemblyPart . TrimStart ( [ io.path ] :: DirectorySeparatorChar )
$resxDestPath = Join-Path $srcRootForResx $assemblyPart
$null = New-Item -Path $resxDestPath -Force -ItemType Directory
Write-Verbose -Message " Created resx directory : $resxDestPath "
Copy-Item -Path " $directoryFullName \* " -Recurse $resxDestPath -Force
}
Add-Type -AssemblyName System . IO . Compression . FileSystem
if ( Test-Path $packagePath )
{
Remove-Item -Path $packagePath -Force
}
[ System.IO.Compression.ZipFile ] :: CreateFromDirectory ( $packageRoot , $packagePath )
}
2018-11-05 19:48:05 +01:00
function New-NugetConfigFile
{
param (
[ Parameter ( Mandatory = $true ) ] [ string ] $NugetFeedUrl ,
[ Parameter ( Mandatory = $true ) ] [ string ] $FeedName ,
[ Parameter ( Mandatory = $true ) ] [ string ] $UserName ,
[ Parameter ( Mandatory = $true ) ] [ string ] $ClearTextPAT ,
[ Parameter ( Mandatory = $true ) ] [ string ] $Destination
)
$nugetConfigTemplate = @ '
< ? xml version = " 1.0 " encoding = " utf-8 " ? >
< configuration >
< packageSources >
< clear / >
< add key = " [FEEDNAME] " value = " [FEED] " / >
< add key = " nuget.org " value = " https://api.nuget.org/v3/index.json " / >
< / packageSources >
< packageSourceCredentials >
< [ FEEDNAME ] >
< add key = " Username " value = " [USERNAME] " / >
< add key = " ClearTextPassword " value = " [PASSWORD] " / >
< / [ FEEDNAME ] >
< / packageSourceCredentials >
< / configuration >
' @
$content = $nugetConfigTemplate . Replace ( '[FEED]' , $NugetFeedUrl ) . Replace ( '[FEEDNAME]' , $FeedName ) . Replace ( '[USERNAME]' , $UserName ) . Replace ( '[PASSWORD]' , $ClearTextPAT )
Set-Content -Path ( Join-Path $Destination 'nuget.config' ) -Value $content -Force
}