diff --git a/build.psm1 b/build.psm1 index 70b2293e9..625996e4a 100644 --- a/build.psm1 +++ b/build.psm1 @@ -115,16 +115,15 @@ function Start-PSBuild { # Add .NET CLI tools to PATH Find-Dotnet + # verify we have all tools in place to do the build + $precheck = precheck 'dotnet' "Build dependency 'dotnet' not found in PATH. Run Start-PSBootstrap. Also see: https://dotnet.github.io/getting-started/" + if ($IsWindows) { # use custom package store - this value is also defined in nuget.config under config/repositoryPath # dotnet restore uses this value as the target for installing the assemblies for referenced nuget packages. # dotnet build does not currently consume the config value but will consume env:NUGET_PACKAGES to resolve these dependencies $env:NUGET_PACKAGES="$PSScriptRoot\Packages" - } - # verify we have all tools in place to do the build - $precheck = precheck 'dotnet' "Build dependency 'dotnet' not found in PATH. Run Start-PSBootstrap. Also see: https://dotnet.github.io/getting-started/" - if ($FullCLR) { # cmake is needed to build powershell.exe $precheck = $precheck -and (precheck 'cmake' 'cmake not found. Run Start-PSBootstrap. You can also install it from https://chocolatey.org/packages/cmake.portable') @@ -261,8 +260,8 @@ function Start-PSBuild { if (-not (Test-Path $Lib)) { throw "Compilation of $Lib failed" } - } elseif ($FullCLR -and (-not $SMAOnly)) { - log "Start building native powershell.exe" + } elseif ($IsWindows -and (-not $SMAOnly)) { + log "Start building native Windows binaries" try { Push-Location "$PSScriptRoot\src\powershell-native" @@ -280,37 +279,62 @@ cmd.exe /C cd /d "$currentLocation" "&" "$($vcVarsPath)\vcvarsall.bat" "$NativeH } } -# Disabling until I figure out if it is necessary -# $overrideFlags = "-DCMAKE_USER_MAKE_RULES_OVERRIDE=$PSScriptRoot\src\powershell-native\windows-compiler-override.txt" - $overrideFlags = "" - $location = Get-Location - # - # BUILD_ONECORE - # + function Build-NativeWindowsBinaries { + param( + # Describes wither it should build the CoreCLR or FullCLR version + [ValidateSet("ON", "OFF")] + [string]$OneCoreValue, + + # Array of file names to copy from the local build directory to the packaging directory + [string[]]$FilesToCopy + ) + +# Disabling until I figure out if it is necessary +# $overrideFlags = "-DCMAKE_USER_MAKE_RULES_OVERRIDE=$PSScriptRoot\src\powershell-native\windows-compiler-override.txt" + $overrideFlags = "" + $location = Get-Location - $BuildOneCoreValues = @("ON","OFF") - foreach ($oneCoreValue in $BuildOneCoreValues) { $command = @" -cmd.exe /C cd /d "$location" "&" "$($vcVarsPath)\vcvarsall.bat" "$NativeHostArch" "&" cmake "$overrideFlags" -DBUILD_ONECORE=$oneCoreValue -G "$cmakeGenerator" . "&" msbuild ALL_BUILD.vcxproj "/p:Configuration=$msbuildConfiguration" +cmd.exe /C cd /d "$location" "&" "$($vcVarsPath)\vcvarsall.bat" "$NativeHostArch" "&" cmake "$overrideFlags" -DBUILD_ONECORE=$OneCoreValue -G "$cmakeGenerator" . "&" msbuild ALL_BUILD.vcxproj "/p:Configuration=$msbuildConfiguration" "@ log " Executing Build Command: $command" Start-NativeExecution { Invoke-Expression -Command:$command } + + $clrTarget = "FullClr" + if ($OneCoreValue -eq "ON") + { + $clrTarget = "CoreClr" + } + + # Copy the binaries from the local build directory to the packaging directory + $dstPath = ($script:Options).Top + $FilesToCopy | % { + $srcPath = Join-Path (Join-Path (Join-Path (Get-Location) "bin") $msbuildConfiguration) "$clrTarget/$_" + log " Copying $srcPath to $dstPath" + Copy-Item $srcPath $dstPath + } } + + if ($FullCLR) { + $fullBinaries = @( + 'powershell.exe', + 'powershell.pdb', + 'pwrshplugin.dll', + 'pwrshplugin.pdb' + ) + Build-NativeWindowsBinaries "OFF" $fullBinaries + } + else + { + $coreClrBinaries = @( + 'pwrshplugin.dll', + 'pwrshplugin.pdb' + ) + Build-NativeWindowsBinaries "ON" $coreClrBinaries - # Copy the executable binary from the local build directory to the expected destination to enable Start-DevPowerShell to work - # - # TODO: This should be updated to handle per-architecture builds gracefully. - - $dstPath = ($script:Options).Top - @( - 'powershell.exe', - 'powershell.pdb', - 'pwrshplugin.dll', - 'pwrshplugin.pdb' - ) | % { - $srcPath = Join-Path (Join-Path (Join-Path (Get-Location) "bin") $msbuildConfiguration) "FullCLR/$_" - log " Copying $srcPath to $dstPath" - Copy-Item $srcPath $dstPath + # Place the remoting configuration script in the same directory + # as the binary so it will get published. + Copy-Item .\Install-PowerShellRemoting.ps1 ($script:Options).Top } } finally { Pop-Location diff --git a/src/powershell-native/Install-PowerShellRemoting.ps1 b/src/powershell-native/Install-PowerShellRemoting.ps1 new file mode 100644 index 000000000..9ef4224e3 --- /dev/null +++ b/src/powershell-native/Install-PowerShellRemoting.ps1 @@ -0,0 +1,165 @@ +##################################################################################################### +# +# Registers the WinRM endpoint for this instance of PowerShell. +# +# Assumptions: +# 1. This script is run from within the PowerShell that it will register as a WinRM endpoint. +# 2. The CoreCLR and the the PowerShell assemblies are side-by-side in $PSHOME +# 3. Plugins are registered by version number. Only one plugin can be automatically registered +# per PowerShell version. However, multiple endpoints may be manually registered for a given +# plugin. +# +##################################################################################################### + +param +( + [Switch]$Verbose +) + +function Register-WinRmPlugin +{ + param + ( + # + # Expected Example: + # %windir%\\system32\\PowerShell\\6.0.0\\pwrshplugin.dll + # + [string] + [parameter(Mandatory=$true)] + [ValidateNotNullOrEmpty()] + $pluginAbsolutePath, + + # + # Expected Example: microsoft.ops.5.1 + # + [string] + [parameter(Mandatory=$true)] + [ValidateNotNullOrEmpty()] + $pluginEndpointName + ) + + $header = "Windows Registry Editor Version 5.00`n`n" + + $regKeyFormatString = "[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\WSMAN\Plugin\{0}]`n" + + $regKeyName = '"ConfigXML"="{0}"' + + # + # Example Values: + # + # Filename = %windir%\\system32\\PowerShell\\6.0.0\\pwrshplugin.dll + # Name = PowerShell.6.0.0 + # + $regKeyValueFormatString = '' + $valueString = $regKeyValueFormatString -f $pluginEndpointName, $pluginAbsolutePath + $keyValuePair = $regKeyName -f $valueString + + $regKey = $regKeyFormatString -f $pluginEndpointName + + $fileName = "$pluginEndpointName.reg" + + Set-Content -path .\$fileName "$header$regKey$keyValuePair`n" + + Write-Verbose "Performing WinRM registration with: $fileName" + reg.exe import .\$fileName + + # Clean up +# Remove-Item .\$fileName +} + +function Generate-PluginConfigFile +{ + param + ( + [string] + [parameter(Mandatory=$true)] + [ValidateNotNullOrEmpty()] + $pluginFile + ) + + # This always overwrites the file with a new version of it if the + # script is invoked multiple times. + Set-Content -Path $pluginFile -Value "PSHOMEDIR=$PSHOME" + Add-Content -Path $pluginFile -Value "CORECLRDIR=$PSHOME" + + Write-Verbose "Created Plugin Config File: $pluginFile" +} + +###################### +# # +# Install the plugin # +# # +###################### + +if (! ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) +{ + Write-Error "WinRM registration requires Administrator rights. To run this cmdlet, start PowerShell with the `"Run as administrator`" option." + Break +} + +$originalVerbosePreference = $VerbosePreference +if ($Verbose) +{ + $VerbosePreference = "Continue" +} + +$powershellVersion = $PSVersionTable.PSVersion +$pluginBasePath = Join-Path "$env:WINDIR\System32\PowerShell" $powerShellVersion + +$resolvedPluginAbsolutePath = "" +if (! (Test-Path $pluginBasePath)) +{ + Write-Verbose "Creating $pluginBasePath" + $resolvedPluginAbsolutePath = New-Item -Type Directory -Path $pluginBasePath +} +else +{ + $resolvedPluginAbsolutePath = Resolve-Path $pluginBasePath +} + +# The registration reg file requires "\\" instead of "\" in its path so it is properly escaped in the XML +$pluginRawPath = Join-Path $resolvedPluginAbsolutePath "pwrshplugin.dll" +$fixedPluginPath = $pluginRawPath -replace '\\','\\' + +# This is forced to ensure the the file is placed correctly +Copy-Item $PSHOME\pwrshplugin.dll $resolvedPluginAbsolutePath -Force -Verbose + +$pluginFile = Join-Path $resolvedPluginAbsolutePath "RemotePowerShellConfig.txt" +Generate-PluginConfigFile $pluginFile + +$pluginEndpointName = "powershell.$powershellVersion" + +# Register the plugin +Register-WinRmPlugin $fixedPluginPath $pluginEndpointName + +# Reset verbosity +if ($Verbose) +{ + $VerbosePreference = $originalVerbosePreference +} + +#################################################################### +# # +# Validations to confirm that everything was registered correctly. # +# # +#################################################################### + +if (! (Test-Path $pluginFile)) +{ + throw "WinRM Plugin configuration file not created. Expected = $pluginFile" +} + +if (! (Test-Path $resolvedPluginAbsolutePath\pwrshplugin.dll)) +{ + throw "WinRM Plugin DLL missing. Expected = $resolvedPluginAbsolutePath\pwrshplugin.dll" +} + +try +{ + Get-PSSessionConfiguration $pluginEndpointName +} +catch [Microsoft.PowerShell.Commands.WriteErrorException] +{ + Write-Error "No remoting session configuration matches the name $pluginEndpointName." +} + diff --git a/src/powershell-win-core/project.json b/src/powershell-win-core/project.json index 98413566f..b8c815db4 100644 --- a/src/powershell-win-core/project.json +++ b/src/powershell-win-core/project.json @@ -46,6 +46,9 @@ } }, "include": [ + "pwrshplugin.dll", + "pwrshplugin.pdb", + "Install-PowerShellRemoting.ps1", "../../powershell.version" ], },