Skip to content

Commit 8454358

Browse files
authored
Refactor the build scripts and AppVeyor CI (#888)
This PR refactors the building scripts and fix the AppVeyor CI. Major changes are: 1. Add `build.ps1` to provide simple entry points that can be used in CI. 2. Add `tools/helper.psm1` to group utility functions. Move the scripts for `RunTests` to `helper.psm1`. The plan is to move big chunk of build action scripts to the `helper.psm1` to make `PSReadLine.build.ps1` less cumbersome but more clear about what build actions are available. 3. Fix the AppVeyor CI.
1 parent 3a97b70 commit 8454358

File tree

8 files changed

+517
-277
lines changed

8 files changed

+517
-277
lines changed

PSReadLine.build.ps1

Lines changed: 52 additions & 233 deletions
Original file line numberDiff line numberDiff line change
@@ -15,92 +15,30 @@
1515
#
1616

1717
[CmdletBinding()]
18-
param([switch]$Install,
19-
[string]$Configuration = (property Configuration Release))
18+
param(
19+
[switch]$Install,
2020

21-
# Final bits to release go here
22-
$targetDir = "bin/$Configuration/PSReadLine"
23-
if ($PSVersionTable.PSEdition -eq "Core")
24-
{
25-
$target = "netcoreapp2.1"
26-
}
27-
else
28-
{
29-
$target = "net461"
30-
}
31-
Write-Verbose "Building for '$target'" -Verbose
21+
[ValidateSet("Debug", "Release")]
22+
[string]$Configuration = (property Configuration Release),
3223

33-
$CLI_VERSION = "2.1.300"
24+
[ValidateSet("net461", "netcoreapp2.1")]
25+
[string]$Framework
26+
)
3427

35-
function ConvertTo-CRLF([string] $text) {
36-
$text.Replace("`r`n","`n").Replace("`n","`r`n")
37-
}
38-
39-
<#
40-
Synopsis: Ensure dotnet is installed
41-
#>
42-
task CheckDotnetInstalled `
43-
{
44-
$script:dotnet = (Get-Command dotnet -ErrorAction Ignore).Path
45-
if ($script:dotnet -eq $null)
46-
{
47-
$searchPaths = "~/.dotnet/dotnet", "~\AppData\Local\Microsoft\dotnet\dotnet.exe"
48-
$foundDotnet = $false
49-
foreach ($path in $searchPaths)
50-
{
51-
if (Test-Path $path)
52-
{
53-
$foundDotnet = $true
54-
$script:dotnet = $path
55-
break
56-
}
57-
}
28+
Import-Module "$PSScriptRoot/tools/helper.psm1"
5829

59-
if (!$foundDotnet)
60-
{
61-
if ($env:APPVEYOR)
62-
{
63-
# Install dotnet to the default location, it will be cached via appveyor.yml
64-
$installScriptUri = "https://raw.githubusercontent.com/dotnet/cli/release/2.1/scripts/obtain/dotnet-install.ps1"
65-
$installDir = "${env:LOCALAPPDATA}\Microsoft\dotnet"
66-
Invoke-WebRequest -Uri $installScriptUri -OutFile "${env:TEMP}/dotnet-install.ps1"
67-
& "$env:TEMP/dotnet-install.ps1" -Version $CLI_VERSION -InstallDir $installDir
68-
$script:dotnet = Join-Path $installDir "dotnet.exe"
69-
}
70-
else
71-
{
72-
throw "Could not find 'dotnet' command. Install DotNetCore SDK and ensure it is in the path."
73-
}
74-
}
75-
}
76-
}
30+
# Final bits to release go here
31+
$targetDir = "bin/$Configuration/PSReadLine"
7732

78-
<#
79-
Synopsis: Ensure nuget is installed
80-
#>
81-
task CheckNugetInstalled `
33+
if (-not $Framework)
8234
{
83-
$script:nugetExe = (Get-Command nuget.exe -ea Ignore).Path
84-
if ($null -eq $nugetExe)
85-
{
86-
$script:nugetExe = "${env:TEMP}/nuget.exe"
87-
if (!(Test-Path $nugetExe))
88-
{
89-
Invoke-WebRequest https://dist.nuget.org/win-x86-commandline/latest/nuget.exe -OutFile $nugetExe
90-
}
91-
}
35+
$Framework = if ($PSVersionTable.PSEdition -eq "Core") { "netcoreapp2.1" } else { "net461" }
9236
}
9337

38+
Write-Verbose "Building for '$Framework'" -Verbose
9439

95-
<#
96-
Synopsis: Ensure platyPS is installed
97-
#>
98-
task CheckPlatyPSInstalled `
99-
{
100-
if ($null -eq (Get-Module -List platyPS))
101-
{
102-
Install-Module -Scope CurrentUser -Repository PSGallery -Name platyPS
103-
}
40+
function ConvertTo-CRLF([string] $text) {
41+
$text.Replace("`r`n","`n").Replace("`n","`r`n")
10442
}
10543

10644
$buildMamlParams = @{
@@ -118,9 +56,9 @@ task BuildMamlHelp @buildMamlParams {
11856
$buildAboutTopicParams = @{
11957
Inputs = {
12058
Get-ChildItem docs/about_PSReadLine.help.txt
121-
"PSReadLine/bin/$Configuration/$target/Microsoft.PowerShell.PSReadLine2.dll"
122-
"$PSScriptRoot/GenerateFunctionHelp.ps1"
123-
"$PSScriptRoot/CheckHelp.ps1"
59+
"PSReadLine/bin/$Configuration/$Framework/Microsoft.PowerShell.PSReadLine2.dll"
60+
"$PSScriptRoot/tools/GenerateFunctionHelp.ps1"
61+
"$PSScriptRoot/tools/CheckHelp.ps1"
12462
}
12563
Outputs = "$targetDir/en-US/about_PSReadLine.help.txt"
12664
}
@@ -131,10 +69,10 @@ Synopsis: Generate about topic with function help
13169
task BuildAboutTopic @buildAboutTopicParams {
13270
# This step loads the dll that was just built, so only do that in another process
13371
# so the file isn't locked in any way for the rest of the build.
72+
$psExePath = Get-PSExePath
13473

13574
$generatedFunctionHelpFile = New-TemporaryFile
136-
$powershell = [System.Diagnostics.Process]::GetCurrentProcess().MainModule.Filename
137-
& $powershell -NoProfile -NonInteractive -File $PSScriptRoot/GenerateFunctionHelp.ps1 $Configuration $generatedFunctionHelpFile.FullName
75+
& $psExePath -NoProfile -NonInteractive -File $PSScriptRoot/tools/GenerateFunctionHelp.ps1 $Configuration $generatedFunctionHelpFile.FullName
13876
assert ($LASTEXITCODE -eq 0) "Generating function help failed"
13977

14078
$functionDescriptions = Get-Content -Raw $generatedFunctionHelpFile
@@ -143,20 +81,44 @@ task BuildAboutTopic @buildAboutTopicParams {
14381
$newAboutTopic = $newAboutTopic -replace "`r`n","`n"
14482
$newAboutTopic | Out-File -FilePath $targetDir\en-US\about_PSReadLine.help.txt -NoNewline -Encoding ascii
14583

146-
& $powershell -NoProfile -NonInteractive -File $PSScriptRoot/CheckHelp.ps1 $Configuration
84+
& $psExePath -NoProfile -NonInteractive -File $PSScriptRoot/tools/CheckHelp.ps1 $Configuration
14785
assert ($LASTEXITCODE -eq 0) "Checking help and function signatures failed"
14886
}
14987

15088
$binaryModuleParams = @{
15189
Inputs = { Get-ChildItem PSReadLine/*.cs, PSReadLine/PSReadLine.csproj, PSReadLine/PSReadLineResources.resx }
152-
Outputs = "PSReadLine/bin/$Configuration/$target/Microsoft.PowerShell.PSReadLine2.dll"
90+
Outputs = "PSReadLine/bin/$Configuration/$Framework/Microsoft.PowerShell.PSReadLine2.dll"
91+
}
92+
93+
$xUnitTestParams = @{
94+
Inputs = { Get-ChildItem test/*.cs, test/*.json, test/PSReadLine.Tests.csproj }
95+
Outputs = "test/bin/$Configuration/$Framework/PSReadLine.Tests.dll"
96+
}
97+
98+
$mockPSConsoleParams = @{
99+
Inputs = { Get-ChildItem TestPSReadLine/*.cs, TestPSReadLine/Program.manifest, TestPSReadLine/TestPSReadLine.csproj }
100+
Outputs = "TestPSReadLine/bin/$Configuration/$Framework/TestPSReadLine.dll"
153101
}
154102

155103
<#
156104
Synopsis: Build main binary module
157105
#>
158-
task BuildMainModule @binaryModuleParams CheckDotnetInstalled, {
159-
exec { & $script:dotnet publish -f $target -c $Configuration PSReadLine }
106+
task BuildMainModule @binaryModuleParams {
107+
exec { dotnet publish -f $Framework -c $Configuration PSReadLine }
108+
}
109+
110+
<#
111+
Synopsis: Build xUnit tests
112+
#>
113+
task BuildXUnitTests @xUnitTestParams {
114+
exec { dotnet publish -f $Framework -c $Configuration test }
115+
}
116+
117+
<#
118+
Synopsis: Build the mock powershell console.
119+
#>
120+
task BuildMockPSConsole @mockPSConsoleParams {
121+
exec { dotnet publish -f $Framework -c $Configuration TestPSReadLine }
160122
}
161123

162124
<#
@@ -171,150 +133,10 @@ task GenerateCatalog {
171133
}
172134
}
173135

174-
$buildTestParams = @{
175-
Inputs = { Get-ChildItem TestPSReadLine/*.cs, TestPSReadLine/TestPSReadLine.csproj }
176-
Outputs = "TestPSReadLine/bin/$Configuration/TestPSReadLine.exe"
177-
}
178-
179-
180136
<#
181137
Synopsis: Run the unit tests
182138
#>
183-
task RunTests BuildMainModule, {
184-
$env:PSREADLINE_TESTRUN = 1
185-
186-
# need to copy implemented assemblies so test code can host powershell otherwise we have to build for a specific runtime
187-
if ($PSVersionTable.PSEdition -eq "Core")
188-
{
189-
$psAssemblies = "System.Management.Automation.dll", "Newtonsoft.Json.dll", "System.Management.dll", "System.DirectoryServices.dll"
190-
foreach ($assembly in $psAssemblies)
191-
{
192-
if (Test-Path "$PSHOME\$assembly")
193-
{
194-
Copy-Item "$PSHOME\$assembly" "test\bin\$configuration\$target"
195-
}
196-
else
197-
{
198-
throw "Could not find '$PSHOME\$assembly'"
199-
}
200-
}
201-
}
202-
203-
Push-Location test
204-
if ([System.Environment]::OSVersion.Platform -eq [System.PlatformID]::Win32NT)
205-
{
206-
Add-Type -Language CSharpVersion3 @'
207-
using System;
208-
using System.Collections.Generic;
209-
using System.Globalization;
210-
using System.Runtime.InteropServices;
211-
using System.Threading;
212-
213-
public class KeyboardLayoutHelper
214-
{
215-
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
216-
static extern IntPtr LoadKeyboardLayout(string pwszKLID, uint Flags);
217-
218-
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
219-
static extern IntPtr GetKeyboardLayout(uint idThread);
220-
221-
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
222-
static extern int GetKeyboardLayoutList(int nBuff, [Out] IntPtr[] lpList);
223-
224-
// Used when setting the layout.
225-
[DllImport("user32.dll", CharSet = CharSet.Auto)]
226-
public static extern bool PostMessage(IntPtr hWnd, int Msg, int wParam, int lParam);
227-
228-
// Used for getting the layout.
229-
[DllImport("user32.dll", SetLastError = true)]
230-
static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint processId);
231-
232-
// Used in both getting and setting the layout
233-
[DllImport("user32.dll", SetLastError = true)]
234-
static extern IntPtr GetForegroundWindow();
235-
236-
const int WM_INPUTLANGCHANGEREQUEST = 0x0050;
237-
238-
private static string GetLayoutNameFromHKL(IntPtr hkl)
239-
{
240-
var lcid = (int)((uint)hkl & 0xffff);
241-
return (new CultureInfo(lcid)).Name;
242-
}
243-
244-
public static IEnumerable<string> GetKeyboardLayouts()
245-
{
246-
int cnt = GetKeyboardLayoutList(0, null);
247-
var list = new IntPtr[cnt];
248-
GetKeyboardLayoutList(list.Length, list);
249-
250-
foreach (var layout in list)
251-
{
252-
yield return GetLayoutNameFromHKL(layout);
253-
}
254-
}
255-
256-
public static string GetCurrentKeyboardLayout()
257-
{
258-
uint processId;
259-
IntPtr layout = GetKeyboardLayout(GetWindowThreadProcessId(GetForegroundWindow(), out processId));
260-
return GetLayoutNameFromHKL(layout);
261-
}
262-
263-
public static IntPtr SetKeyboardLayout(string lang)
264-
{
265-
var layoutId = (new CultureInfo(lang)).KeyboardLayoutId;
266-
var layout = LoadKeyboardLayout(layoutId.ToString("x8"), 0x80);
267-
// Hacky, but tests are probably running in a console app and the layout change
268-
// is ignored, so post the layout change to the foreground window.
269-
PostMessage(GetForegroundWindow(), WM_INPUTLANGCHANGEREQUEST, 0, layoutId);
270-
// Wait a bit until the layout has been changed.
271-
do {
272-
Thread.Sleep(100);
273-
} while (GetCurrentKeyboardLayout() != lang);
274-
return layout;
275-
}
276-
}
277-
'@
278-
279-
# Remember the current keyboard layout, changes are system wide and restoring
280-
# is the nice thing to do.
281-
$savedLayout = [KeyboardLayoutHelper]::GetCurrentKeyboardLayout()
282-
283-
# We want to run tests in as many layouts as possible. We have key info
284-
# data for layouts that might not be installed, and tests would fail
285-
# if we don't set the system wide layout to match the key data we'll use.
286-
$layouts = [KeyboardLayoutHelper]::GetKeyboardLayouts()
287-
Write-Output "Available layouts:", $layouts
288-
foreach ($layout in $layouts)
289-
{
290-
if (Test-Path "KeyInfo-${layout}-windows.json")
291-
{
292-
Write-Output "Testing $layout"
293-
$null = [KeyboardLayoutHelper]::SetKeyboardLayout($layout)
294-
$os,$es = @(New-TemporaryFile; New-TemporaryFile)
295-
$filter = "FullyQualifiedName~Test.$($layout -replace '-','_')_Windows"
296-
exec {
297-
# We have to use Start-Process so it creates a new window, because the keyboard
298-
# layout change won't be picked up by any processes running in the current conhost.
299-
$dnArgs = 'test', '--no-build', '-c', $configuration, '-f', $target, '--filter', $filter, '--logger', 'trx'
300-
$p = Start-Process -FilePath $script:dotnet -Wait -PassThru -RedirectStandardOutput $os -RedirectStandardError $es -ArgumentList $dnArgs
301-
Get-Content $os,$es
302-
Remove-Item $os,$es
303-
#$global:LASTEXITCODE = $p.ExitCode
304-
}
305-
}
306-
}
307-
# Restore the original keyboard layout
308-
$null = [KeyboardLayoutHelper]::SetKeyboardLayout($savedLayout)
309-
}
310-
else
311-
{
312-
exec { & $script:dotnet test --no-build -c $configuration -f $target --filter "FullyQualifiedName~Test.en_US_Linux" --logger trx }
313-
}
314-
Pop-Location
315-
316-
Remove-Item env:PSREADLINE_TESTRUN
317-
}
139+
task RunTests BuildMainModule, BuildXUnitTests, { Start-TestRun -Configuration $Configuration -Framework $Framework }
318140

319141
<#
320142
Synopsis: Copy all of the files that belong in the module to one place in the layout for installation
@@ -334,7 +156,7 @@ task LayoutModule BuildMainModule, BuildMamlHelp, {
334156
Set-Content -Path (Join-Path $targetDir (Split-Path $file -Leaf)) -Value (ConvertTo-CRLF $content) -Force
335157
}
336158

337-
$binPath = "PSReadLine/bin/$Configuration/$target/publish"
159+
$binPath = "PSReadLine/bin/$Configuration/$Framework/publish"
338160
Copy-Item $binPath/Microsoft.PowerShell.PSReadLine2.dll $targetDir
339161

340162
if (Test-Path $binPath/System.Runtime.InteropServices.RuntimeInformation.dll)
@@ -343,7 +165,7 @@ task LayoutModule BuildMainModule, BuildMamlHelp, {
343165
}
344166
else
345167
{
346-
Write-Warning "Build using $target is not sufficient to be downlevel compatible"
168+
Write-Warning "Build using $Framework is not sufficient to be downlevel compatible"
347169
}
348170

349171
# Copy module manifest, but fix the version to match what we've specified in the binary module.
@@ -386,15 +208,13 @@ task LayoutModule BuildMainModule, BuildMamlHelp, {
386208
}
387209
}, BuildAboutTopic
388210

389-
390211
<#
391212
Synopsis: Zip up the binary for release.
392213
#>
393-
task ZipRelease CheckDotNetInstalled, LayoutModule, {
214+
task ZipRelease LayoutModule, {
394215
Compress-Archive -Force -LiteralPath $targetDir -DestinationPath "bin/$Configuration/PSReadLine.zip"
395216
}
396217

397-
398218
<#
399219
Synopsis: Install newly built PSReadLine
400220
#>
@@ -497,4 +317,3 @@ task Clean {
497317
Synopsis: Default build rule - build and create module layout
498318
#>
499319
task . LayoutModule, RunTests
500-

0 commit comments

Comments
 (0)