Skip to content

Commit

Permalink
Visual Studio BuildTools as a MinGW alternative
Browse files Browse the repository at this point in the history
Building the MSI hook on Windows
(`contrib/win-installer/podman-msihooks/check.c`)
currently requires MinGW. This commit updates the build
script so that, when MinGW is absent but the C compiler
included in Visual Studio BuildTools is installed, the
latter is used to build the MSI hook.

Other than that, `winmake.ps1` has a new `installertest`
target to run the Windows installer tests that are
currently verified by Cirrus CI.

Signed-off-by: Mario Loriedo <mario.loriedo@gmail.com>
  • Loading branch information
l0rd committed Jul 8, 2024
1 parent c5841b0 commit 81250cc
Show file tree
Hide file tree
Showing 7 changed files with 248 additions and 53 deletions.
28 changes: 28 additions & 0 deletions build_windows.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Windows.
- [Git and go](#git-and-go)
- [Pandoc](#pandoc)
- [.NET SDK](#net-sdk)
- [Visual Studio Build Tools](#visual-studio-build-tools)
- [Virtualization Provider](#virtualization-provider)
- [WSL](#wsl)
- [Hyper-V](#hyper-v)
Expand Down Expand Up @@ -87,6 +88,30 @@ used too and can be installed using `dotnet install`:
dotnet tool install --global wix
```

### Visual Studio Build Tools

The installer includes a C program that checks the installation of the
pre-required virtualization providers (WSL or Hyper-V). Building this program
requires the
[Microsoft C/C++ compiler](https://learn.microsoft.com/en-us/cpp/build/building-on-the-command-line?view=msvc-170) and the
[PowerShell Moduel VSSetup](https://github.com/microsoft/vssetup.powershell):

1. Download the Build Tools for Visual Studio 2022 installer
```pwsh
Invoke-WebRequest -Uri 'https://aka.ms/vs/17/release/vs_BuildTools.exe' -OutFile "$env:TEMP\vs_BuildTools.exe"
```
2. Run the installer with the parameter to include the optional C/C++ Tools
```pwsh
& "$env:TEMP\vs_BuildTools.exe" --passive --wait `
--add Microsoft.VisualStudio.Workload.VCTools `
--includeRecommended `
--remove Microsoft.VisualStudio.Component.VC.CMake.Project
```
3. Install the PowerShell Module VSSetup
```pwsh
Install-Module VSSetup
```

### Virtualization Provider

Running Podman on Windows requires a virtualization provider. The supported
Expand Down Expand Up @@ -340,6 +365,9 @@ otherwise):
contrib\win-installer\podman-5.1.0-dev-setup.exe /install /log podman-setup.log /quiet MachineProvider=wsl WSLCheckbox=0 HyperVCheckbox=0
```

:information_source: The `winmake.ps1` target `installertest` automatically
tests installing and uninstalling Podman.

### Build and test the standalone `podman.msi` file

Building and testing the standalone `podman.msi` package during development may
Expand Down
56 changes: 11 additions & 45 deletions contrib/cirrus/win-installer-main.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -12,55 +12,21 @@ if ($Env:CI -eq "true") {
$ENV:CONTAINERS_MACHINE_PROVIDER = "wsl"
}

$ConfFilePath = "$env:ProgramData\containers\containers.conf.d\99-podman-machine-provider.conf"
$WindowsPathsToTest = @("C:\Program Files\RedHat\Podman\podman.exe",
"C:\Program Files\RedHat\Podman\win-sshproxy.exe",
"$ConfFilePath",
"HKLM:\SOFTWARE\Red Hat\Podman")

Push-Location $WIN_INST_FOLDER

# Build Installer
# Note: consumes podman-remote-release-windows_amd64.zip from repo.tbz2
Run-Command ".\build.ps1 $Env:WIN_INST_VER dev `"$RELEASE_DIR`""

# Run the installer silently and WSL/HyperV install options disabled (prevent reboots)
# We need AllowOldWin=1 for server 2019 (cirrus image), can be dropped after server 2022
$ret = Start-Process -Wait -PassThru ".\podman-${ENV:WIN_INST_VER}-dev-setup.exe" -ArgumentList "/install /quiet MachineProvider=$ENV:CONTAINERS_MACHINE_PROVIDER WSLCheckbox=0 HyperVCheckbox=0 AllowOldWin=1 /log inst.log"
if ($ret.ExitCode -ne 0) {
Write-Host "Install failed, dumping log"
Get-Content inst.log
Pop-Location
throw "Exit code is $($ret.ExitCode)"
}
$WindowsPathsToTest | ForEach-Object {
if (! (Test-Path -Path $_) ) {
Pop-Location
throw "Expected $_ but it's not present after uninstall"
}
}
$machineProvider = Get-Content $ConfFilePath | Select-Object -Skip 1 | ConvertFrom-StringData | ForEach-Object { $_.provider }
if ( $machineProvider -ne "`"$ENV:CONTAINERS_MACHINE_PROVIDER`"" ) {
Pop-Location
throw "Expected `"$ENV:CONTAINERS_MACHINE_PROVIDER`" as default machine provider but got $machineProvider"
}

Write-Host "Installer verification successful!"

# Run the uninstaller silently to verify that it cleans up properly
$ret = Start-Process -Wait -PassThru ".\podman-${ENV:WIN_INST_VER}-dev-setup.exe" -ArgumentList "/uninstall /quiet /log uninst.log"
if ($ret.ExitCode -ne 0) {
Write-Host "Uninstall failed, dumping log"
Get-Content uninst.log
Pop-Location
throw "Exit code is $($ret.ExitCode)"
}
$WindowsPathsToTest | ForEach-Object {
if ( Test-Path -Path $_ ) {
Pop-Location
throw "Path $_ is still present after uninstall"
}
}

Write-Host "Uninstaller verification successful!"
Pop-Location

# Run the installer silently and WSL/HyperV install options disabled (prevent reboots)
# We need -skipWinVersionCheck for server 2019 (cirrus image), can be dropped after server 2022
$command = "$WIN_INST_FOLDER\test-installer.ps1 "
$command += "-operation all "
$command += "-provider $ENV:CONTAINERS_MACHINE_PROVIDER "
$command += "-setupExePath `"$WIN_INST_FOLDER\podman-$ENV:WIN_INST_VER-dev-setup.exe`" "
$command += "-installWSL:`$false "
$command += "-installHyperV:`$false "
$command += "-skipWinVersionCheck:`$true"
Run-Command "${command}"
6 changes: 0 additions & 6 deletions contrib/win-installer/build-hooks.bat

This file was deleted.

67 changes: 67 additions & 0 deletions contrib/win-installer/build-hooks.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
function Build-WSLKernelInstaller {
param (
[string]$wslkerninstFolder,
[string]$artifactsFolder
);
Set-Variable GOARCH=amd64
go build -ldflags -H=windowsgui -o "$artifactsFolder\podman-wslkerninst.exe" "$wslkerninstFolder"
}

function Build-MSIHooks {
param (
[string]$msiHooksFolder,
[string]$artifactsFolder
);

# Build using x86 toolchain, see comments in check.c for rationale and details
if ( Get-MingW ) {
Build-MSIHooks-Using-MingW $msiHooksFolder $artifactsFolder
} elseif ( Get-VSBuildTools ) {
$vsinstance = Get-VSSetupInstance | Select-VSSetupInstance -Product Microsoft.VisualStudio.Product.BuildTools
Build-MSIHooks-Using-VSBuildTools $msiHooksFolder $artifactsFolder $vsinstance
} else {
$msg = "A C/C++ compiler is required to build `"$msiHooksFolder\check.c`". "
$msg += "Supported compilers are MinGW CC (`"x86_64-w64-mingw32-gcc`") and the "
$msg += "`"Microsoft.VisualStudio.Product.BuildTools`" with `"VSSetup`" PowerShell extension."
Write-Error -Message $msg -ErrorAction Stop
}
}

function Get-MingW {
return Get-Command "x86_64-w64-mingw32-gcc" -errorAction SilentlyContinue
}

function Get-VSBuildTools {
return ((Get-Command "Get-VSSetupInstance" -errorAction SilentlyContinue) -and `
(@(Get-VSSetupInstance | Select-VSSetupInstance -Product "Microsoft.VisualStudio.Product.BuildTools").Count -gt 0))
}

function Build-MSIHooks-Using-MingW {
param (
[string]$msiHooksFolder,
[string]$artifactsFolder
);
Set-Variable GOARCH=amd64
x86_64-w64-mingw32-gcc $msiHooksFolder/check.c -shared -lmsi -mwindows -o $artifactsFolder/podman-msihooks.dll
}

function Build-MSIHooks-Using-VSBuildTools {
param (
[string]$msiHooksFolder,
[string]$artifactsFolder,
[Microsoft.VisualStudio.Setup.Instance]$vsinstance
);
$vspath = $vsinstance.InstallationPath
$vsinstanceid = $vsinstance.InstanceId

Import-Module "$vspath\Common7\Tools\Microsoft.VisualStudio.DevShell.dll"
Enter-VsDevShell $vsinstanceid -DevCmdArguments '-arch=amd64 -host_arch=amd64'
cl.exe /W4 /Fo$artifactsFolder\ $msiHooksFolder\check.c Advapi32.lib Msi.lib /link /DLL /out:$artifactsFolder\podman-msihooks.dll
}

$wslkerninstFolder="$PSScriptRoot\..\..\cmd\podman-wslkerninst"
$msiHooksFolder="$PSScriptRoot\podman-msihooks"
$artifactsFolder="$PSScriptRoot\artifacts"

Build-WSLKernelInstaller $wslkerninstFolder $artifactsFolder
Build-MSIHooks $msiHooksFolder $artifactsFolder
3 changes: 1 addition & 2 deletions contrib/win-installer/build.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ function CheckCommand() {
}

function CheckRequirements() {
CheckCommand "gcc" "MingW CC"
CheckCommand "wix" "WiX Toolset"
CheckCommand "go" "Golang"
}
Expand Down Expand Up @@ -104,7 +103,7 @@ if ($ENV:INSTVER -eq "") {
Exit 1
}

.\build-hooks.bat; ExitOnError
.\build-hooks.ps1; ExitOnError
SignItem @("artifacts/win-sshproxy.exe",
"artifacts/podman.exe",
"artifacts/podman-msihooks.dll",
Expand Down
93 changes: 93 additions & 0 deletions contrib/win-installer/test-installer.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
#!/usr/bin/env pwsh

# The Param statement must be the first statement, except for comments and any #Require statements.
param (
[Parameter(Mandatory)]
[ValidateSet("install", "uninstall", "all")]
[string]$operation,
[Parameter(Mandatory)]
[ValidateScript({Test-Path $_ -PathType Leaf})]
[string]$setupExePath,
[ValidateSet("wsl", "hyperv")]
[string]$provider="wsl",
[switch]$installWSL=$false,
[switch]$installHyperV=$false,
[switch]$skipWinVersionCheck=$false
)

$ConfFilePath = "$env:ProgramData\containers\containers.conf.d\99-podman-machine-provider.conf"
$WindowsPathsToTest = @("C:\Program Files\RedHat\Podman\podman.exe",
"C:\Program Files\RedHat\Podman\win-sshproxy.exe",
"$ConfFilePath",
"HKLM:\SOFTWARE\Red Hat\Podman")

function Test-Installation {
if ($installWSL) {$wslCheckboxVar = "1"} else {$wslCheckboxVar = "0"}
if ($installHyperV) {$hypervCheckboxVar = "1"} else {$hypervCheckboxVar = "0"}
if ($skipWinVersionCheck) {$allowOldWinVar = "1"} else {$allowOldWinVar = "0"}

Write-Host "Running the installer (provider=`"$provider`")..."
$ret = Start-Process -Wait `
-PassThru "$setupExePath" `
-ArgumentList "/install /quiet `
MachineProvider=${provider} `
WSLCheckbox=${wslCheckboxVar} `
HyperVCheckbox=${hypervCheckboxVar} `
AllowOldWin=${allowOldWinVar} `
/log $PSScriptRoot\podman-setup.log"
if ($ret.ExitCode -ne 0) {
Write-Host "Install failed, dumping log"
Get-Content $PSScriptRoot\podman-setup.log
throw "Exit code is $($ret.ExitCode)"
}

Write-Host "Verifying that the installer has created the expected files, folders and registry entries..."
$WindowsPathsToTest | ForEach-Object {
if (! (Test-Path -Path $_) ) {
throw "Expected $_ but it's not present after uninstall"
}
}

Write-Host "Verifying that the machine provider configuration is correct..."
$machineProvider = Get-Content $ConfFilePath | Select-Object -Skip 1 | ConvertFrom-StringData | ForEach-Object { $_.provider }
if ( $machineProvider -ne "`"$provider`"" ) {
throw "Expected `"$provider`" as default machine provider but got $machineProvider"
}

Write-Host "The installation verification was successful!`n"
}

function Test-Uninstallation {
Write-Host "Running the uninstaller"
$ret = Start-Process -Wait `
-PassThru "$setupExePath" `
-ArgumentList "/uninstall `
/quiet /log $PSScriptRoot\podman-setup-uninstall.log"
if ($ret.ExitCode -ne 0) {
Write-Host "Uninstall failed, dumping log"
Get-Content $PSScriptRoot\podman-setup-uninstall.log
throw "Exit code is $($ret.ExitCode)"
}

Write-Host "Verifying that the uninstaller has removed files, folders and registry entries as expected..."
$WindowsPathsToTest | ForEach-Object {
if ( Test-Path -Path $_ ) {
throw "Path $_ is still present after uninstall"
}
}

Write-Host "The uninstallation verification was successful!`n"
}

switch ($operation) {
'install' {
Test-Installation
}
'uninstall' {
Test-Uninstallation
}
'all' {
Test-Installation
Test-Uninstallation
}
}
48 changes: 48 additions & 0 deletions winmake.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,44 @@ function Installer{
Pop-Location
}

function Test-Installer{
param (
[string]$version,
[ValidateSet("dev", "prod")]
[string]$flavor = "dev",
[ValidateSet("wsl", "hyperv")]
[string]$provider = "wsl"
);

if (-Not $version) {
# Get Podman version from local source code
$version = Get-Podman-Version
}

if ($flavor -eq "prod") {
$suffix = ""
} else {
$suffix = "-dev"
}

$setupExePath = "$PSScriptRoot\contrib\win-installer\podman-${version}${suffix}-setup.exe"
if (!(Test-Path -Path $setupExePath -PathType Leaf)) {
Write-Host "Setup executable not found in path $setupExePath."
Write-Host "Make 'installer' before making the installer test:"
Write-Host " .\winmake.ps1 installer"
Exit 1
}

$command = "$PSScriptRoot\contrib\win-installer\test-installer.ps1"
$command += " -operation all"
$command += " -provider $provider"
$command += " -setupExePath $setupExePath"
$command += " -installWSL:`$false"
$command += " -installHyperV:`$false"
$command += " -skipWinVersionCheck:`$true"
Run-Command "${command}"
}

function Documentation{
Write-Host "Generating the documentation artifacts"
# Check that pandoc is installed
Expand Down Expand Up @@ -249,6 +287,13 @@ switch ($target) {
'installer' {
Installer
}
'installertest' {
if ($args.Count -gt 1) {
Test-Installer -provider $args[1]
} else {
Test-Installer
}
}
'docs' {
Documentation
}
Expand Down Expand Up @@ -276,6 +321,9 @@ switch ($target) {
Write-Host "Example: Build the windows installer"
Write-Host " .\winmake installer"
Write-Host
Write-Host "Example: Run windows installer tests"
Write-Host " .\winmake installertest"
Write-Host
Write-Host "Example: Generate the documetation artifacts"
Write-Host " .\winmake docs"
Write-Host
Expand Down

0 comments on commit 81250cc

Please sign in to comment.