summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author5an7y <[email protected]>2026-03-30 15:32:35 -0700
committer5an7y <[email protected]>2026-03-30 15:32:35 -0700
commitbc6b2708d9d762dde716a2af7c8fa08534c1afcc (patch)
treec2055795fba580545a5311b5228d1c51cb6f90cb
parentd92950792737971182298c34d7efa445fbb23728 (diff)
Merge Build-Sample.ps1 into Build-Samples.ps1 as internal function
- Inline Build-Sample.ps1 logic as Build-SingleSample function in Build-Samples.ps1 - Pass function definition into parallel runspaces via $using: pattern - Replace exit codes with return values for in-process execution - Delete Build-Sample.ps1 (no longer needed as a separate script) - Update Build-ChangedSamples.ps1 trigger file list
-rw-r--r--.github/scripts/Build-ChangedSamples.ps12
-rw-r--r--Build-Sample.ps1211
-rw-r--r--Build-Samples.ps1152
3 files changed, 146 insertions, 219 deletions
diff --git a/.github/scripts/Build-ChangedSamples.ps1 b/.github/scripts/Build-ChangedSamples.ps1
index 29f99061..3e91eb88 100644
--- a/.github/scripts/Build-ChangedSamples.ps1
+++ b/.github/scripts/Build-ChangedSamples.ps1
@@ -22,7 +22,7 @@ foreach ($file in $ChangedFiles) {
$filename = Split-Path $file -Leaf
# Files that can affect how every sample is built should trigger a full build
- if ($filename -eq "Build-Samples.ps1" -or $filename -eq "Build-Sample.ps1" -or $filename -eq "exclusions.csv" -or $filename -eq "Directory.Build.props" -or $filename -eq "packages.config") {
+ if ($filename -eq "Build-Samples.ps1" -or $filename -eq "exclusions.csv" -or $filename -eq "Directory.Build.props" -or $filename -eq "packages.config") {
$buildAll = $true
}
if ($dir -like "$root\.github\scripts" -or $dir -like "$root\.github\scripts\*") {
diff --git a/Build-Sample.ps1 b/Build-Sample.ps1
deleted file mode 100644
index c09b7694..00000000
--- a/Build-Sample.ps1
+++ /dev/null
@@ -1,211 +0,0 @@
-<#
-.SYNOPSIS
-Builds an specific directory containing a sample solution.
-
-.DESCRIPTION
-This script attempts to build a directory containing a driver sample Solution for the specified configurations and platforms.
-
-.PARAMETER Directory
-Path to a directory containing a valid Visual Studio Solution (.sln file). This is the solution that will be built.
-
-.PARAMETER SampleName
-A friendly name to refer to the sample. Is unspecified, a name will be automatically generated one from the sample path.
-
-.PARAMETER Configuration
-Configuration name that will be used to build the solution. Common available values are "Debug" and "Release".
-
-.PARAMETER Platform
-Platform to build the solution for (e.g. "x64", "arm64").
-
-.PARAMETER InfVerif_AdditionalOptions
-Additional options for infverif (e.g. "/samples").
-
-.PARAMETER LogFilesDirectoy
-Path to a directory where the log files will be written to. If not provided, outputs will be logged to the current working directory.
-
-.INPUTS
-None.
-
-.OUTPUTS
-Verbose output about the execution of this script will be provided only if -Verbose is provided. Otherwise, no output will be generated.
-
-.EXAMPLE
-.\Build-Sample -Directory .\usb\kmdf_fx2
-
-.EXAMPLE
-.\Build-Sample -Directory .\usb\kmdf_fx2 -Configuration 'Release' -Platform 'x64' -Verbose -LogFilesDirectory .\_logs
-
-#>
-
-[CmdletBinding()]
-param(
- [Parameter(Mandatory = $true,
- HelpMessage = 'Enter one directory path',
- Position = 0)]
- [string]$Directory,
- [string]$SampleName,
- [string]$Configuration = "Debug",
- [string]$Platform = "x64",
- [string]$InfVerif_AdditionalOptions = "/samples",
- $LogFilesDirectory = (Get-Location)
-)
-
-$Verbose = $false
-if ($PSBoundParameters.ContainsKey('Verbose')) {
- $Verbose = $PsBoundParameters.Get_Item('Verbose')
-}
-
-$oldPreference = $ErrorActionPreference
-$ErrorActionPreference = "stop"
-try
-{
- # Check that msbuild can be called before trying anything.
- Get-Command "msbuild" | Out-Null
-}
-catch
-{
- Write-Verbose "`u{274C} msbuild cannot be called from current environment. Check that msbuild is set in current path (for example, that it is called from a Visual Studio developer command)."
- Write-Error "msbuild cannot be called from current environment."
- exit 1
-}
-finally
-{
- $ErrorActionPreference = $oldPreference
-}
-
-if (-not (Test-Path -Path $Directory -PathType Container))
-{
- Write-Warning "`u{274C} A valid directory could not be found under $Directory"
- exit 1
-}
-
-New-Item -ItemType Directory -Force -Path $LogFilesDirectory | Out-Null
-
-if (-not (Test-Path -Path $LogFilesDirectory -PathType Container))
-{
- Write-Warning "`u{274C} A valid directory for storing log files could not be created under $LogFilesDirectory"
- # No exit here: process will continue but logs won't be available.
-}
-
-if ([string]::IsNullOrWhitespace($SampleName))
-{
- $SampleName = (Resolve-Path $Directory).Path.Replace((Get-Location), '').Replace('\', '.').Trim('.').ToLower()
-}
-
-$solutionFile = Get-ChildItem -Path $Directory -Filter *.sln | Select-Object -ExpandProperty FullName -First 1
-
-if ($null -eq $solutionFile)
-{
- Write-Warning "`u{274C} A solution could not be found under $Directory"
- exit 1
-}
-
-$configurationIsSupported = $false
-$inSolutionConfigurationPlatformsSection = $false
-foreach ($line in Get-Content -Path $solutionFile)
-{
- if (-not $inSolutionConfigurationPlatformsSection -and $line -match "\s*GlobalSection\(SolutionConfigurationPlatforms\).*")
- {
- $inSolutionConfigurationPlatformsSection = $true;
- continue;
- }
- elseif ($line -match "\s*EndGlobalSection.*")
- {
- $inSolutionConfigurationPlatformsSection = $false;
- continue;
- }
-
- if ($inSolutionConfigurationPlatformsSection)
- {
- [regex]$regex = ".*=\s*(?<ConfigString>(?<Configuration>.*)\|(?<Platform>.*))\s*"
- $match = $regex.Match($line)
- if ([string]::IsNullOrWhiteSpace($match.Groups["ConfigString"].Value) -or [string]::IsNullOrWhiteSpace($match.Groups["Platform"].Value))
- {
- Write-Warning "Could not parse configuration entry $line from file $solutionFile."
- continue;
- }
- if ($match.Groups["Configuration"].Value.Trim() -eq $Configuration -and $match.Groups["Platform"].Value.Trim() -eq $Platform)
- {
- $configurationIsSupported = $true;
- }
- }
-}
-
-if (-not $configurationIsSupported)
-{
- Write-Verbose "[$SampleName] `u{23E9} Skipped. Configuration $Configuration|$Platform not supported."
- exit 3
-}
-
-Write-Verbose "Building Sample: $SampleName; Configuration: $Configuration; Platform: $Platform {"
-
-$myexit=1
-
-#
-# Let us build up to three times (0th, 1st, and 2nd attempt).
-# If we succeed at first, then it is a success.
-# If we fail at first, but succeed at either of next two attempts, then it is a sporadic failure.
-# If we even at third attempt fail, then it is a true failure.
-#
-for ($i = 0; $i -lt 3; $i++)
-{
- $binLogFilePath = "$LogFilesDirectory\$SampleName.$Configuration.$Platform.$i.binlog"
- $errorLogFilePath = "$LogFilesDirectory\$SampleName.$Configuration.$Platform.$i.err"
- $warnLogFilePath = "$LogFilesDirectory\$SampleName.$Configuration.$Platform.$i.wrn"
- $OutLogFilePath = "$LogFilesDirectory\$SampleName.$Configuration.$Platform.$i.out"
-
- msbuild $solutionFile -clp:Verbosity=m -t:rebuild -property:Configuration=$Configuration -property:Platform=$Platform -p:TargetVersion=Windows10 -p:InfVerif_AdditionalOptions="$InfVerif_AdditionalOptions" -warnaserror -binaryLogger:LogFile=$binLogFilePath`;ProjectImports=None -flp1:errorsonly`;logfile=$errorLogFilePath -flp2:WarningsOnly`;logfile=$warnLogFilePath -noLogo > $OutLogFilePath
- if ($null -ne $env:WDS_WipeOutputs)
- {
- Write-Verbose ("WipeOutputs: " + $Directory + " " + (((Get-Volume (Get-Item ".").PSDrive.Name).SizeRemaining / 1GB)))
- Get-ChildItem -path $Directory -Recurse -Include x64 | Remove-Item -Recurse
- Get-ChildItem -path $Directory -Recurse -Include arm64 | Remove-Item -Recurse
- }
- if ($LASTEXITCODE -eq 0)
- {
- # We succeeded building.
- # If it was at a later attempt, let the caller know with a different exit code.
- if ($i -eq 0)
- {
- $myexit = 0
- }
- else
- {
- $myexit = 2
- }
- # Remove binlog on success to save space; keep otherwise to diagnose issues.
- Remove-Item $binLogFilePath
- break;
- }
- else
- {
- # We failed building.
- # Let us sleep for a bit.
- # Then let the while loop do its thing and re-run.
- Start-Sleep 1
- if ($Verbose)
- {
- Write-Warning "`u{274C} Build failed. Retrying to see if sporadic..."
- }
- }
-}
-
-if ($myexit -eq 1)
-{
- if ($Verbose)
- {
- Write-Warning "`u{274C} Build failed. Log available at $errorLogFilePath"
- }
- exit 1
-}
-
-if ($myexit -eq 2)
-{
- if ($Verbose)
- {
- Write-Warning "`u{274C} Build sporadically failed. Log available at $errorLogFilePath"
- }
- exit 2
-}
-
-Write-Verbose "Building Sample: $SampleName; Configuration: $Configuration; Platform: $Platform }"
diff --git a/Build-Samples.ps1 b/Build-Samples.ps1
index aad2d3e5..4242d428 100644
--- a/Build-Samples.ps1
+++ b/Build-Samples.ps1
@@ -14,10 +14,6 @@
Requires PowerShell 7+ (uses ForEach-Object -Parallel).
- Typical call chain:
- Build-AllSamples.ps1 -> Build-Samples.ps1 -> ListAllSamples.ps1 (for discovery)
- (this script) -> Build-Sample.ps1 (per sample)
-
.PARAMETER Samples
Optional array of specific sample names to build. When omitted, all samples are discovered dynamically via ListAllSamples.ps1.
@@ -240,6 +236,141 @@ function Get-DiskFreeGB {
}
}
+function Build-SingleSample {
+ <#
+ .SYNOPSIS
+ Builds a single sample directory for one configuration/platform combination.
+ .DESCRIPTION
+ Locates the .sln in the given directory, verifies the configuration|platform is
+ supported, then invokes msbuild with up to 3 attempts (to detect sporadic failures).
+ .OUTPUTS
+ Returns an integer exit code:
+ 0 = succeeded on first attempt
+ 1 = failed after all retries
+ 2 = sporadic (failed first, succeeded on retry)
+ 3 = unsupported configuration/platform
+ #>
+ param(
+ [string]$Directory,
+ [string]$SampleName,
+ [string]$Configuration = 'Debug',
+ [string]$Platform = 'x64',
+ [string]$InfVerif_AdditionalOptions = '/samples',
+ [string]$LogFilesDirectory = (Get-Location),
+ [bool]$Verbose = $false
+ )
+
+ if (-not (Test-Path -Path $Directory -PathType Container)) {
+ Write-Warning "`u{274C} A valid directory could not be found under $Directory"
+ return 1
+ }
+
+ New-Item -ItemType Directory -Force -Path $LogFilesDirectory | Out-Null
+
+ if ([string]::IsNullOrWhiteSpace($SampleName)) {
+ $SampleName = (Resolve-Path $Directory).Path.Replace((Get-Location), '').Replace('\', '.').Trim('.').ToLower()
+ }
+
+ $solutionFile = Get-ChildItem -Path $Directory -Filter *.sln |
+ Select-Object -ExpandProperty FullName -First 1
+
+ if ($null -eq $solutionFile) {
+ Write-Warning "`u{274C} A solution could not be found under $Directory"
+ return 1
+ }
+
+ # --- Check whether the solution supports the requested configuration|platform ---
+ $configurationIsSupported = $false
+ $inSolutionConfigurationPlatformsSection = $false
+ foreach ($line in Get-Content -Path $solutionFile) {
+ if (-not $inSolutionConfigurationPlatformsSection -and
+ $line -match '\s*GlobalSection\(SolutionConfigurationPlatforms\).*') {
+ $inSolutionConfigurationPlatformsSection = $true
+ continue
+ }
+ elseif ($line -match '\s*EndGlobalSection.*') {
+ $inSolutionConfigurationPlatformsSection = $false
+ continue
+ }
+
+ if ($inSolutionConfigurationPlatformsSection) {
+ [regex]$regex = '.*=\s*(?<ConfigString>(?<Configuration>.*)\|(?<Platform>.*))\s*'
+ $match = $regex.Match($line)
+ if ([string]::IsNullOrWhiteSpace($match.Groups['ConfigString'].Value) -or
+ [string]::IsNullOrWhiteSpace($match.Groups['Platform'].Value)) {
+ Write-Warning "Could not parse configuration entry $line from file $solutionFile."
+ continue
+ }
+ if ($match.Groups['Configuration'].Value.Trim() -eq $Configuration -and
+ $match.Groups['Platform'].Value.Trim() -eq $Platform) {
+ $configurationIsSupported = $true
+ }
+ }
+ }
+
+ if (-not $configurationIsSupported) {
+ Write-Verbose "[$SampleName] `u{23E9} Skipped. Configuration $Configuration|$Platform not supported."
+ return 3
+ }
+
+ Write-Verbose "Building Sample: $SampleName; Configuration: $Configuration; Platform: $Platform {"
+
+ $myexit = 1
+
+ # Build up to three times (0th, 1st, and 2nd attempt).
+ # Succeed on 1st -> success (0)
+ # Fail 1st, succeed on retry -> sporadic (2)
+ # Fail all three -> failure (1)
+ for ($i = 0; $i -lt 3; $i++) {
+ $binLogFilePath = "$LogFilesDirectory\$SampleName.$Configuration.$Platform.$i.binlog"
+ $errorLogFilePath = "$LogFilesDirectory\$SampleName.$Configuration.$Platform.$i.err"
+ $warnLogFilePath = "$LogFilesDirectory\$SampleName.$Configuration.$Platform.$i.wrn"
+ $outLogFilePath = "$LogFilesDirectory\$SampleName.$Configuration.$Platform.$i.out"
+
+ msbuild $solutionFile `
+ -clp:Verbosity=m -t:rebuild `
+ -property:Configuration=$Configuration `
+ -property:Platform=$Platform `
+ -p:TargetVersion=Windows10 `
+ -p:InfVerif_AdditionalOptions="$InfVerif_AdditionalOptions" `
+ -warnaserror `
+ -binaryLogger:LogFile=$binLogFilePath`;ProjectImports=None `
+ -flp1:errorsonly`;logfile=$errorLogFilePath `
+ -flp2:WarningsOnly`;logfile=$warnLogFilePath `
+ -noLogo > $outLogFilePath
+
+ if ($null -ne $env:WDS_WipeOutputs) {
+ Write-Verbose ("WipeOutputs: $Directory " + (((Get-Volume (Get-Item '.').PSDrive.Name).SizeRemaining / 1GB)))
+ Get-ChildItem -Path $Directory -Recurse -Include x64 | Remove-Item -Recurse
+ Get-ChildItem -Path $Directory -Recurse -Include arm64 | Remove-Item -Recurse
+ }
+
+ if ($LASTEXITCODE -eq 0) {
+ $myexit = if ($i -eq 0) { 0 } else { 2 }
+ # Remove binlog on success to save space; keep otherwise to diagnose issues.
+ Remove-Item $binLogFilePath
+ break
+ }
+ else {
+ Start-Sleep 1
+ if ($Verbose) {
+ Write-Warning "`u{274C} Build failed. Retrying to see if sporadic..."
+ }
+ }
+ }
+
+ if ($myexit -eq 1 -and $Verbose) {
+ Write-Warning "`u{274C} Build failed. Log available at $errorLogFilePath"
+ }
+ if ($myexit -eq 2 -and $Verbose) {
+ Write-Warning "`u{274C} Build sporadically failed. Log available at $errorLogFilePath"
+ }
+
+ Write-Verbose "Building Sample: $SampleName; Configuration: $Configuration; Platform: $Platform }"
+
+ return $myexit
+}
+
# =============================================================================
# Step 1 - Prepare Build Environment
# =============================================================================
@@ -392,6 +523,9 @@ $buildState = @{
$stopwatch = [Diagnostics.Stopwatch]::StartNew()
+# Capture function definition so it can be reconstructed inside each parallel runspace.
+$buildSingleSampleDef = ${function:Build-SingleSample}.ToString()
+
$sampleSet.GetEnumerator() | ForEach-Object -ThrottleLimit $ThrottleLimit -Parallel {
# --- Import shared state from parent scope ---
$logDir = $using:LogFilesDirectory
@@ -404,6 +538,9 @@ $sampleSet.GetEnumerator() | ForEach-Object -ThrottleLimit $ThrottleLimit -Paral
$total = $using:combinationsTotal
$throttle = $using:ThrottleLimit
+ # Reconstruct the function inside this parallel runspace
+ ${function:Build-SingleSample} = $using:buildSingleSampleDef
+
$sampleName = $_.Key
$directory = $_.Value
@@ -443,17 +580,18 @@ $sampleSet.GetEnumerator() | ForEach-Object -ThrottleLimit $ThrottleLimit -Paral
}
else {
# -- Build the sample --
- .\Build-Sample -Directory $directory -SampleName $sampleName `
+ $buildResult = Build-SingleSample `
+ -Directory $directory -SampleName $sampleName `
-LogFilesDirectory $logDir -Configuration $configuration `
-Platform $platform -InfVerif_AdditionalOptions $infOpts `
-Verbose:$isVerbose
- # Exit codes from Build-Sample.ps1:
+ # Return codes from Build-SingleSample:
# 0 = succeeded on first attempt
# 1 = failed after all retries
# 2 = sporadic (failed first, succeeded on retry)
# 3 = unsupported configuration/platform
- switch ($LASTEXITCODE) {
+ switch ($buildResult) {
0 { $succeededDelta = 1; $result = 'Succeeded' }
1 { $failedDelta = 1; $result = 'Failed'; $failEntry = "$sampleName $configuration|$platform" }
2 { $sporadicDelta = 1; $result = 'Sporadic'; $sporadicEntry = "$sampleName $configuration|$platform" }