diff --git a/.github/scripts/Build-ChangedSamples.ps1 b/.github/scripts/Build-ChangedSamples.ps1 index bdaee943c..3e91eb883 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-AllSamples.ps1" -or $filename -eq "Build-Sample.ps1" -or $filename -eq "Build-SampleSet.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\*") { @@ -52,9 +52,10 @@ foreach ($file in $ChangedFiles) { } if ($buildAll) { - .\Build-AllSamples -Verbose:$Verbose -LogFilesDirectory (Join-Path $root "_logs") + .\Build-Samples -Verbose:$Verbose -LogFilesDirectory (Join-Path $root "_logs") } else { - .\Build-SampleSet -SampleSet $sampleSet -Verbose:$Verbose -LogFilesDirectory (Join-Path $root "_logs") + $sampleNames = $sampleSet.Keys | Sort-Object + .\Build-Samples -Samples $sampleNames -Verbose:$Verbose -LogFilesDirectory (Join-Path $root "_logs") } diff --git a/.github/workflows/Code-Scanning.yml b/.github/workflows/Code-Scanning.yml index 63e155c9c..232545ada 100644 --- a/.github/workflows/Code-Scanning.yml +++ b/.github/workflows/Code-Scanning.yml @@ -66,7 +66,7 @@ jobs: WDS_Platform: x64 WDS_WipeOutputs: ${{ true }} - if: github.event_name == 'push' - run: .\Build-AllSamples.ps1 -Verbose -ThrottleLimit 1 + run: .\Build-Samples.ps1 -Verbose -ThrottleLimit 1 env: WDS_Configuration: Debug WDS_Platform: x64 diff --git a/.github/workflows/StaleIssues.yml b/.github/workflows/StaleIssues.yml deleted file mode 100644 index d3a3776a3..000000000 --- a/.github/workflows/StaleIssues.yml +++ /dev/null @@ -1,16 +0,0 @@ -name: Close inactive issues -on: - workflow_dispatch: -jobs: - close-issues: - runs-on: ubuntu-latest - permissions: - issues: write - steps: - - uses: actions/stale@v10 - with: - days-before-issue-stale: 365 - stale-issue-label: "stale" - stale-issue-message: "This issue has been inactive for a year. It will be closed in 31 days if no further activity occurs. If you believe this issue is still relevant, please comment to keep it open." - operations-per-run: 400 - repo-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1f3d76930..0e847b35c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,7 +29,7 @@ jobs: run: nuget restore .\packages.config -PackagesDirectory .\packages\ - name: Retrieve and build all available solutions - run: .\Build-AllSamples.ps1 -Verbose + run: .\Build-Samples.ps1 -Verbose env: WDS_Configuration: ${{ matrix.configuration }} WDS_Platform: ${{ matrix.platform }} diff --git a/Build-AllSamples.ps1 b/Build-AllSamples.ps1 deleted file mode 100644 index 6b2e16614..000000000 --- a/Build-AllSamples.ps1 +++ /dev/null @@ -1,72 +0,0 @@ -<# -.SYNOPSIS -Builds all available sample solutions in the repository (excluding specific solutions). - -.DESCRIPTION -This script searches for all available Visual Studio Solutions (.sln files) and attempts to run MSBuild to build them for the specified configurations and platforms. - -.PARAMETER Samples -A regular expression matching the samples to be built. Default is '' that matches all samples. Examples include '^tools.' or '.dchu'. - -.PARAMETER Configurations -A list of configurations to build samples under. Values available are 'Debug' and 'Release'. By default, $env:WDS_Configuration will be used as the sole configuration to build for. If this environment variable is not set the default is 'Debug' and 'Release'. - -.PARAMETER Platforms -A list of platforms to build samples under (e.g. 'x64', 'arm64'). By default, $env:WDS_Platform will be used as the sole platform to build for. If this environment variable is not set the default is 'x64' and'arm64'. - -.PARAMETER LogFilesDirectory -Path to a directory where the log files will be written to. If not provided, outputs will be logged to the '_logs' directory within the current working directory. - -.PARAMETER ThrottleLimit -An integer indicating how many combinations to build in parallel. If 0 or not provided this defaults to 5 x number of logical processors. - -.INPUTS -None. - -.OUTPUTS -None. - -.EXAMPLE -.\Build-AllSamples - -.EXAMPLE -.\Build-AllSamples -Samples '^tools.' -Configurations 'Debug','Release' -Platforms 'x64','arm64' - -#> - -[CmdletBinding()] -param( - [string]$Samples = "", - [string[]]$Configurations = @(if ([string]::IsNullOrEmpty($env:WDS_Configuration)) { ('Debug', 'Release') } else { $env:WDS_Configuration }), - [string[]]$Platforms = @(if ([string]::IsNullOrEmpty($env:WDS_Platform)) { ('x64', 'arm64') } else { $env:WDS_Platform }), - [string]$LogFilesDirectory = (Join-Path (Get-Location) "_logs"), - [string]$InfOptions = "", - [int]$ThrottleLimit -) - -$Verbose = $false -if ($PSBoundParameters.ContainsKey('Verbose')) { - $Verbose = $PsBoundParameters.Get_Item('Verbose') -} - -$root = Get-Location -$solutionFiles = Get-ChildItem -Path $root -Recurse -Filter *.sln | Select-Object -ExpandProperty FullName - -# To include in CI gate -$sampleSet = @{} -foreach ($file in $solutionFiles) { - $dir = (Get-Item $file).DirectoryName - $dir_norm = $dir.Replace($root, '').Trim('\').Replace('\', '.').ToLower() - if ($dir_norm -match ("^packages.")) { - Write-Verbose "`u{1F50E} Found and ignored non-sample [$dir_norm] at $dir" - } - elseif ($dir_norm -match ($Samples)) { - Write-Verbose "`u{1F50E} Found and filtered in sample [$dir_norm] at $dir" - $sampleSet[$dir_norm] = $dir - } - else { - Write-Verbose "`u{1F50E} Found and filtered out sample [$dir_norm] at $dir" - } -} - -.\Build-SampleSet -SampleSet $sampleSet -Configurations $Configurations -Platform $Platforms -LogFilesDirectory $LogFilesDirectory -Verbose:$Verbose -ThrottleLimit $ThrottleLimit -InfOptions $InfOptions diff --git a/Build-Sample.ps1 b/Build-Sample.ps1 deleted file mode 100644 index 093ba1ed9..000000000 --- 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*(?(?.*)\|(?.*))\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=0 - -# -# 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-SampleSet.ps1 b/Build-SampleSet.ps1 deleted file mode 100644 index 32e603bd1..000000000 --- a/Build-SampleSet.ps1 +++ /dev/null @@ -1,381 +0,0 @@ -[CmdletBinding()] -param( - [hashtable]$SampleSet, - [string[]]$Configurations = @(if ([string]::IsNullOrEmpty($env:WDS_Configuration)) { "Debug" } else { $env:WDS_Configuration }), - [string[]]$Platforms = @(if ([string]::IsNullOrEmpty($env:WDS_Platform)) { "x64" } else { $env:WDS_Platform }), - $LogFilesDirectory = (Get-Location), - [string]$ReportFileName = $(if ([string]::IsNullOrEmpty($env:WDS_ReportFileName)) { "_overview" } else { $env:WDS_ReportFileName }), - [string]$InfOptions = "", - [int]$ThrottleLimit = 0 -) - -$root = Get-Location - -# launch developer powershell (if necessary to prevent multiple developer sessions) -if (-not $env:VSCMD_VER) { - Import-Module (Resolve-Path "$env:ProgramFiles\Microsoft Visual Studio\*\*\Common7\Tools\Microsoft.VisualStudio.DevShell.dll") - Enter-VsDevShell -VsInstallPath (Resolve-Path "$env:ProgramFiles\Microsoft Visual Studio\*\*") - Set-Location $root -} - -$ThrottleFactor = 5 -$LogicalProcessors = (Get-CIMInstance -Class 'CIM_Processor' -Verbose:$false).NumberOfLogicalProcessors - -if ($ThrottleLimit -eq 0) { - $ThrottleLimit = $ThrottleFactor * $LogicalProcessors -} - -$Verbose = $false -if ($PSBoundParameters.ContainsKey('Verbose')) { - $Verbose = $PsBoundParameters.Get_Item('Verbose') -} - -New-Item -ItemType Directory -Force -Path $LogFilesDirectory | Out-Null -$reportFilePath = Join-Path $LogFilesDirectory "$ReportFileName.htm" -$reportCsvFilePath = Join-Path $LogFilesDirectory "$ReportFileName.csv" - - -Remove-Item -Recurse -Path $LogFilesDirectory 2>&1 | Out-Null -New-Item -ItemType Directory -Force -Path $LogFilesDirectory | Out-Null - -$oldPreference = $ErrorActionPreference -$ErrorActionPreference = "stop" -try { - # Check that msbuild can be called before trying anything. - Get-Command "msbuild" | Out-Null -} -catch { - Write-Host "`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 -} - -# -# Determine build environment: 'GitHub', 'NuGet', 'EWDK', or 'WDK'. -# Determine build number (used for exclusions based on build number). Five digits. Say, '22621'. -# Determine NuGet package version (if applicable). -# -$build_environment="" -$build_number=0 -$nuget_package_version=0 -# -# In Github we build using NuGet. -# -if ($env:GITHUB_REPOSITORY) { - $build_environment="GitHub" - $nuget_package_version=([regex]'(?<=x64\.)(\d+\.)(\d+\.)(\d+\.)(\d+)').Matches((Get-Childitem .\packages\*WDK.x64* -Name)).Value - $build_number=$nuget_package_version.split('.')[2] -} -# -# WDK NuGet will require presence of a folder 'packages'. The version is sourced from repo .\Env-Vars.ps1. -# -# Hack: If user has hydrated nuget packages, then use those. That will be indicated by presence of a folder named '.\packages'. -# Further, we need to test that the directory has been hydrated using '.\packages\*'. -# -elseif(Test-Path(".\packages\*")) { - $build_environment=("NuGet") - $nuget_package_version=([regex]'(?<=x64\.)(\d+\.)(\d+\.)(\d+\.)(\d+)').Matches((Get-Childitem .\packages\*WDK.x64* -Name)).Value - $build_number=$nuget_package_version.split('.')[2] -} -# -# EWDK sets environment variable BuildLab. For example 'ni_release_svc_prod1.22621.2428'. -# -elseif($env:BuildLab -match '(?[^.]*).(?[^.]*).(?[^.]*)') { - $build_environment=("EWDK."+$Matches.branch+"."+$Matches.build+"."+$Matches.qfe) - $build_number=$Matches.build -} -# -# WDK sets environment variable UCRTVersion. For example '10.0.22621.0'. -# -elseif ($env:UCRTVersion -match '10.0.(?.*).0') { - $build_environment="WDK" - $build_number=$Matches.build -} -else { - - # Dump all environment variables so as to help debug error: - Write-Output "Environment variables {" - Get-ChildItem env:* | Sort-Object name - Write-Output "Environment variables }" - - Write-Error "Could not determine build environment." - exit 1 -} - -# Determine WDK Visual Studio Component version -# -# Be lenient with EWDK builds that do not include the component information -if ($build_environment -match '^EWDK') { - $wdk_vs_component_ver = "(WDK Visual Studio Component Version is not included for EWDK builds)" -} else { - # Get the WDK extension version from installed packages - $wdk_vs_component_ver = Get-ChildItem "${env:ProgramData}\Microsoft\VisualStudio\Packages\Microsoft.Windows.DriverKit,version=*" -ErrorAction SilentlyContinue - if (-not $wdk_vs_component_ver) { - Write-Error "WDK Visual Studio Component version not found. Please ensure the WDK Component is installed." - exit 1 - } - $wdk_vs_component_ver = [regex]::Match($wdk_vs_component_ver.Name, '(\d+\.){3}\d+').Value -} - -# -# -# InfVerif_AdditionalOptions -# -# Samples must build cleanly and even without warnings. -# -# An exception is for infverif where specific warnings are acceptable. Those -# specific warnings indicates issues intentially present in the samples, that -# anyone that clones the samples must fix as part of productizing a driver. -# -# In 22621 those warnings are: /sw1284 /sw1285 /sw1293 /sw2083 /sw2086 -# -# After 22621 those warnings are put under a common flag: /samples -# -$InfVerif_AdditionalOptions=($build_number -le 22621 ? "/sw1284 /sw1285 /sw1293 /sw2083 /sw2086" : "/samples") - -# Override InfVerif_AdditionalOptions if InfOptions parameter was provided -if (-not [string]::IsNullOrEmpty($InfOptions)) { - $InfVerif_AdditionalOptions = $InfOptions -} - -# -# Determine exclusions. -# -# Exclusions are loaded from .\exclusions.csv. -# Each line has form: -# Path,Configurations,MinBuild,MaxBuild,Reason -# Where: -# Path: Is the path to folder containing solution(s) using backslashes. For example: 'audio\acx\samples\audiocodec\driver' . -# Configurations: Are the configurations to exclude. For example: '*|arm64' . -# MinBuild: Is the minimum WDK/EWDK build number the exclusion is applicable for. For example: '22621' . -# MaxBuild: Is the maximum WDK/EWDK build number the exclusion is applicable for. For example: '26031' . -# Reason: Is plain text documenting the reason for the exclusion. For example: 'error C1083: Cannot open include file: 'acx.h': No such file or directory' . -# -$exclusionConfigurations = @{} -$exclusionReasons = @{} -Import-Csv 'exclusions.csv' | ForEach-Object { - $excluded_driver=$_.Path.Replace($root, '').Trim('\').Replace('\', '.').ToLower() - $excluded_configurations=($_.configurations -eq '' ? '*' : $_.configurations) - $excluded_minbuild=($_.MinBuild -eq '' ? 00000 : $_.MinBuild) - $excluded_maxbuild=($_.MaxBuild -eq '' ? 99999 : $_.MaxBuild) - if (($excluded_minbuild -le $build_number) -and ($build_number -le $excluded_maxbuild) ) - { - $exclusionConfigurations[$excluded_driver] = $excluded_configurations - $exclusionReasons[$excluded_driver] = $_.Reason - Write-Verbose "Exclusion.csv entry applied for '$excluded_driver' for configuration '$excluded_configurations'." - } - else - { - Write-Verbose "Exclusion.csv entry not applied for '$excluded_driver' due to build number." - } -} - -$jresult = @{ - SolutionsBuilt = 0 - SolutionsSucceeded = 0 - SolutionsExcluded = 0 - SolutionsUnsupported = 0 - SolutionsFailed = 0 - SolutionsSporadic = 0 - Results = @() - FailSet = @() - lock = [System.Threading.Mutex]::new($false) -} - -$SolutionsTotal = $sampleSet.Count * $Configurations.Count * $Platforms.Count - -Write-Output "WDK Build Environment: $build_environment" -Write-Output "WDK Build Number: $build_number" -if (($build_environment -eq "GitHub") -or ($build_environment -eq "NuGet")) { -Write-Output "WDK Nuget Version: $nuget_package_version" -} -Write-Output "WDK Visual Studio Component Version: $wdk_vs_component_ver" -Write-Output "Samples: $($sampleSet.Count)" -Write-Output "Configurations: $($Configurations.Count) ($Configurations)" -Write-Output "Platforms: $($Platforms.Count) ($Platforms)" -Write-Output "InfVerif_AdditionalOptions: $InfVerif_AdditionalOptions" -Write-Output "Combinations: $SolutionsTotal" -Write-Output "LogicalProcessors: $LogicalProcessors" -Write-Output "ThrottleFactor: $ThrottleFactor" -Write-Output "ThrottleLimit: $ThrottleLimit" -Write-Output "WDS_WipeOutputs: $env:WDS_WipeOutputs" -Write-Output "Disk Remaining (GB): $(((Get-Volume ((Get-Item ".").PSDrive.Name)).SizeRemaining) / 1GB)" -Write-Output "" -Write-Output "T: Combinations" -Write-Output "B: Built" -Write-Output "R: Build is running currently" -Write-Output "P: Build is pending an available build slot" -Write-Output "" -Write-Output "S: Built and result was 'Succeeded'" -Write-Output "E: Built and result was 'Excluded'" -Write-Output "U: Built and result was 'Unsupported' (Platform and Configuration combination)" -Write-Output "F: Built and result was 'Failed'" -Write-Output "O: Built and result was 'Sporadic'" -Write-Output "" -Write-Output "Building all combinations..." - -$Results = @() - -$sw = [Diagnostics.Stopwatch]::StartNew() - -$SampleSet.GetEnumerator() | ForEach-Object -ThrottleLimit $ThrottleLimit -Parallel { - $LogFilesDirectory = $using:LogFilesDirectory - $exclusionConfigurations = $using:exclusionConfigurations - $exclusionReasons = $using:exclusionReasons - $Configurations = $using:Configurations - $Platforms = $using:Platforms - $InfVerif_AdditionalOptions = $using:InfVerif_AdditionalOptions - $Verbose = $using:Verbose - - $sampleName = $_.Key - $directory = $_.Value - - $ResultElement = new-object psobject - Add-Member -InputObject $ResultElement -MemberType NoteProperty -Name Sample -Value "$sampleName" - - foreach ($configuration in $Configurations) { - foreach ($platform in $Platforms) { - $thisunsupported = 0 - $thisfailed = 0 - $thissporadic = 0 - $thisexcluded = 0 - $thissucceeded = 0 - $thisresult = "Not run" - $thisfailset = @() - $thissporadicset = @() - - if ($exclusionConfigurations.ContainsKey($sampleName) -and ($exclusionConfigurations[$sampleName].Split(';') | Where-Object { "$configuration|$platform" -like $_ })) { - # Verbose - Write-Verbose "[$sampleName $configuration|$platform] `u{23E9} Excluded and skipped. Reason: $($exclusionReasons[$sampleName])" - $thisexcluded += 1 - $thisresult = "Excluded" - } - else { - .\Build-Sample -Directory $directory -SampleName $sampleName -LogFilesDirectory $LogFilesDirectory -Configuration $configuration -Platform $platform -InfVerif_AdditionalOptions $InfVerif_AdditionalOptions -Verbose:$Verbose - if ($LASTEXITCODE -eq 0) { - $thissucceeded += 1 - $thisresult = "Succeeded" - } - elseif ($LASTEXITCODE -eq 1) { - $thisfailset += "$sampleName $configuration|$platform" - $thisfailed += 1 - $thisresult = "Failed" - } - elseif ($LASTEXITCODE -eq 2) { - $thissporadicset += "$sampleName $configuration|$platform" - $thissporadic += 1 - $thisresult = "Sporadic" - } - else { - # ($LASTEXITCODE -eq 3) - $thisunsupported += 1 - $thisresult = "Unsupported" - } - } - Add-Member -InputObject $ResultElement -MemberType NoteProperty -Name "$configuration|$platform" -Value "$thisresult" - - $null = ($using:jresult).lock.WaitOne() - try { - ($using:jresult).SolutionsBuilt += 1 - ($using:jresult).SolutionsSucceeded += $thissucceeded - ($using:jresult).SolutionsExcluded += $thisexcluded - ($using:jresult).SolutionsUnsupported += $thisunsupported - ($using:jresult).SolutionsFailed += $thisfailed - ($using:jresult).SolutionsSporadic += $thissporadic - ($using:jresult).FailSet += $thisfailset - ($using:jresult).SporadicSet += $thissporadicset - $SolutionsTotal = $using:SolutionsTotal - $ThrottleLimit = $using:ThrottleLimit - $SolutionsBuilt = ($using:jresult).SolutionsBuilt - $SolutionsRemaining = $SolutionsTotal - $SolutionsBuilt - $SolutionsRunning = if ($SolutionsRemaining -ge $ThrottleLimit) { $ThrottleLimit } else { $SolutionsRemaining } - $SolutionsPending = if ($SolutionsRemaining -ge $ThrottleLimit) { ($SolutionsRemaining - $ThrottleLimit) } else { 0 } - $SolutionsBuiltPercent = [Math]::Round(100 * ($SolutionsBuilt / $using:SolutionsTotal)) - $TBRP = "T:" + ($SolutionsTotal) + "; B:" + (($using:jresult).SolutionsBuilt) + "; R:" + ($SolutionsRunning) + "; P:" + ($SolutionsPending) - $rstr = "S:" + (($using:jresult).SolutionsSucceeded) + "; E:" + (($using:jresult).SolutionsExcluded) + "; U:" + (($using:jresult).SolutionsUnsupported) + "; F:" + (($using:jresult).SolutionsFailed) + "; O:" + (($using:jresult).SolutionsSporadic) - Write-Progress -Activity "Building combinations" -Status "$SolutionsBuilt of $using:SolutionsTotal combinations built ($SolutionsBuiltPercent%) | $TBRP | $rstr" -PercentComplete $SolutionsBuiltPercent - } - finally { - ($using:jresult).lock.ReleaseMutex() - } - } - } - $null = ($using:jresult).lock.WaitOne() - try { - ($using:jresult).Results += $ResultElement - } - finally { - ($using:jresult).lock.ReleaseMutex() - } -} - -$sw.Stop() - -Write-Output "" - -if ($jresult.FailSet.Count -gt 0) { - Write-Output "Some combinations were built with errors:" - $jresult.FailSet = $jresult.FailSet | Sort-Object - foreach ($failedSample in $jresult.FailSet) { - $failedSample -match "^(.*) (\w*)\|(\w*)$" | Out-Null - $failName = $Matches[1] - $failConfiguration = $Matches[2] - $failPlatform = $Matches[3] - Write-Output "Build errors in Sample $failName; Configuration: $failConfiguration; Platform: $failPlatform {" - Get-Content "$LogFilesDirectory\$failName.$failConfiguration.$failPlatform.0.err" | Write-Output - Write-Output "} $failedSample" - } - Write-Error "Some combinations were built with errors." - Write-Output "" -} - -if ($jresult.SporadicSet.Count -gt 0) { - Write-Output "Some combinations were built with sporadic error:" - $jresult.SporadicSet = $jresult.SporadicSet | Sort-Object - foreach ($sporadicSample in $jresult.SporadicSet) { - $sporadicSample -match "^(.*) (\w*)\|(\w*)$" | Out-Null - $sporadicName = $Matches[1] - $sporadicConfiguration = $Matches[2] - $sporadicPlatform = $Matches[3] - Write-Output "Build sporadic errors in Sample $sporadicName; Configuration: $sporadicConfiguration; Platform: $sporadicPlatform {" - Get-Content "$LogFilesDirectory\$sporadicName.$sporadicConfiguration.$sporadicPlatform.0.err" | Write-Output - Write-Output "} $sporadicSample" - } - Write-Error "Some combinations were built with sporadic errors." - Write-Output "" -} - -# Display timer statistics to host -$min = $sw.Elapsed.Minutes -$seconds = $sw.Elapsed.Seconds - -$SolutionsSucceeded = $jresult.SolutionsSucceeded -$SolutionsExcluded = $jresult.SolutionsExcluded -$SolutionsUnsupported = $jresult.SolutionsUnsupported -$SolutionsFailed = $jresult.SolutionsFailed -$SolutionsSporadic = $jresult.SolutionsSporadic -$Results = $jresult.Results - -Write-Output "Built all combinations." -Write-Output "" -Write-Output "Elapsed time: $min minutes, $seconds seconds." -Write-Output ("Disk Remaining (GB): " + (((Get-Volume (Get-Item ".").PSDrive.Name).SizeRemaining / 1GB))) -Write-Output ("Samples: " + $sampleSet.Count) -Write-Output ("Configurations: " + $Configurations.Count + " (" + $Configurations + ")") -Write-Output ("Platforms: " + $Platforms.Count + " (" + $Platforms + ")") -Write-Output "Combinations: $SolutionsTotal" -Write-Output "Succeeded: $SolutionsSucceeded" -Write-Output "Excluded: $SolutionsExcluded" -Write-Output "Unsupported: $SolutionsUnsupported" -Write-Output "Failed: $SolutionsFailed" -Write-Output "Sporadic: $SolutionsSporadic" -Write-Output "Log files directory: $LogFilesDirectory" -Write-Output "Overview report: $reportFilePath" -Write-Output "" - -$Results | Sort-Object { $_.Sample } | ConvertTo-Csv | Out-File $reportCsvFilePath -$Results | Sort-Object { $_.Sample } | ConvertTo-Html -Title "Overview" | Out-File $reportFilePath -Invoke-Item $reportFilePath diff --git a/Build-Samples.ps1 b/Build-Samples.ps1 new file mode 100644 index 000000000..7eafb54d4 --- /dev/null +++ b/Build-Samples.ps1 @@ -0,0 +1,755 @@ +<# +.SYNOPSIS + Builds driver samples from a sample list file with parallel execution and exclusion support. + +.DESCRIPTION + This is the main build orchestrator for driver samples. It performs these steps: + + 1. Ensures a developer build environment (VS DevShell or EWDK) is active + 2. Discovers samples via ListAllSamples.ps1 (or uses the -Samples parameter if provided) + 3. Detects the build environment (GitHub CI, NuGet, EWDK, or WDK) and build number + 4. Loads exclusions from exclusions.csv (supports wildcard paths) + 5. Builds all non-excluded sample/configuration/platform combinations in parallel + 6. Generates CSV and HTML overview reports + + Requires PowerShell 7+ (uses ForEach-Object -Parallel). + + +.PARAMETER Samples + Optional array of specific sample names or wildcard patterns to build. Supports + wildcards (e.g. 'tools.*', 'audio.*') which are matched against all discovered + samples. When omitted, all samples are discovered dynamically via ListAllSamples.ps1. + +.PARAMETER Configurations + Build configurations (e.g. 'Debug','Release'). Defaults to $env:WDS_Configuration or + ('Debug','Release'). + +.PARAMETER Platforms + Build platforms (e.g. 'x64','arm64'). Defaults to $env:WDS_Platform or ('x64','arm64'). + +.PARAMETER LogFilesDirectory + Directory for build log files. Defaults to _logs in the current directory. + +.PARAMETER ReportFileName + Base name for the report files (without extension). Defaults to $env:WDS_ReportFileName + or '_overview'. + +.PARAMETER InfOptions + Additional InfVerif options (e.g. '/samples', '/msft'). If not provided, determined + automatically based on the WDK build number. + +.PARAMETER ThrottleLimit + Maximum parallel build jobs. Defaults to 5 x logical processors. + +.EXAMPLE + .\Build-Samples + + Discovers all samples via ListAllSamples.ps1 and builds them with default settings. + +.EXAMPLE + .\Build-Samples -Samples 'audio.acx.samples.audiocodec.driver','usb.kmdf_fx2' -Configurations 'Debug' -Platforms 'x64' + + Builds specific samples for a single configuration and platform. + +.EXAMPLE + .\Build-Samples -Samples 'tools.*' + + Builds all samples whose name matches the wildcard pattern 'tools.*'. + +.EXAMPLE + .\Build-Samples -ThrottleLimit 8 + + Discovers all samples and builds them with limited parallelism. +#> + +#Requires -Version 7.0 + +[CmdletBinding()] +param( + [string[]]$Samples, + [string[]]$Configurations = @(if ([string]::IsNullOrEmpty($env:WDS_Configuration)) { ('Debug', 'Release') } else { $env:WDS_Configuration }), + [string[]]$Platforms = @(if ([string]::IsNullOrEmpty($env:WDS_Platform)) { ('x64', 'arm64') } else { $env:WDS_Platform }), + [string]$LogFilesDirectory = (Join-Path (Get-Location) "_logs"), + [string]$ReportFileName = $(if ([string]::IsNullOrEmpty($env:WDS_ReportFileName)) { "_overview" } else { $env:WDS_ReportFileName }), + [string]$InfOptions, + [int]$ThrottleLimit = 0 +) + +# ============================================================================= +# Helper Functions +# ============================================================================= + +function Initialize-DevShell { + <# + .SYNOPSIS Imports the Visual Studio Developer PowerShell if not already active. + #> + param([string]$ReturnToDirectory) + + if ($env:VSCMD_VER) { + Write-Verbose "VS Developer Shell already active (VSCMD_VER=$env:VSCMD_VER)." + return + } + + $devShellDll = Resolve-Path "$env:ProgramFiles\Microsoft Visual Studio\*\*\Common7\Tools\Microsoft.VisualStudio.DevShell.dll" ` + -ErrorAction SilentlyContinue | Select-Object -First 1 + if (-not $devShellDll) { + Write-Error "Visual Studio Developer Shell module not found." + exit 1 + } + + Import-Module $devShellDll.Path + $vsInstall = Resolve-Path "$env:ProgramFiles\Microsoft Visual Studio\*\*" | Select-Object -First 1 + Enter-VsDevShell -VsInstallPath $vsInstall.Path + Set-Location $ReturnToDirectory +} + +function Assert-MsBuildAvailable { + <# + .SYNOPSIS Verifies msbuild.exe is on PATH. Exits with error if not found. + #> + $savedPref = $ErrorActionPreference + $ErrorActionPreference = 'Stop' + try { + Get-Command 'msbuild' | Out-Null + } + catch { + Write-Error "msbuild cannot be called from current environment. Ensure it is on PATH (run from VS Developer Command Prompt or EWDK)." + exit 1 + } + finally { + $ErrorActionPreference = $savedPref + } +} + +function Resolve-BuildEnvironment { + <# + .SYNOPSIS + Detects the active build environment and returns metadata. + .DESCRIPTION + Checks (in priority order) for GitHub CI, local NuGet packages, EWDK, or WDK. + Returns a hashtable: Name, BuildNumber (int), NuGetVersion, WdkVsComponentVersion. + #> + param([string]$RepoRoot) + + $result = @{ + Name = '' + BuildNumber = [int]0 + NuGetVersion = '' + WdkVsComponentVersion = '' + } + + if ($env:GITHUB_REPOSITORY) { + $result.Name = 'GitHub' + $wdkPackage = Get-ChildItem "$RepoRoot\packages\*WDK.x64*" -Name -ErrorAction SilentlyContinue + $result.NuGetVersion = ([regex]'(?<=x64\.)(\d+\.){3}\d+').Match($wdkPackage).Value + $result.BuildNumber = [int]($result.NuGetVersion.Split('.')[2]) + } + elseif (Test-Path "$RepoRoot\packages\*") { + $result.Name = 'NuGet' + $wdkPackage = Get-ChildItem "$RepoRoot\packages\*WDK.x64*" -Name -ErrorAction SilentlyContinue + $result.NuGetVersion = ([regex]'(?<=x64\.)(\d+\.){3}\d+').Match($wdkPackage).Value + $result.BuildNumber = [int]($result.NuGetVersion.Split('.')[2]) + } + elseif ($env:BuildLab -match '^(?[^.]+)\.(?\d+)\.(?[^.]+)$') { + $result.Name = "EWDK.$($Matches.branch).$($Matches.build).$($Matches.qfe)" + $result.BuildNumber = [int]$Matches.build + } + elseif ($env:UCRTVersion -match '10\.0\.(?\d+)\.0') { + $result.Name = 'WDK' + $result.BuildNumber = [int]$Matches.build + } + else { + Write-Output "Environment variables {" + Get-ChildItem env:* | Sort-Object Name + Write-Output "Environment variables }" + Write-Error "Could not determine build environment. Ensure EWDK, WDK, or NuGet packages are configured." + exit 1 + } + + # WDK VS component version (EWDK does not ship this metadata) + if ($result.Name -match '^EWDK') { + $result.WdkVsComponentVersion = '(not available for EWDK builds)' + } + else { + $vsComponent = Get-ChildItem "${env:ProgramData}\Microsoft\VisualStudio\Packages\Microsoft.Windows.DriverKit,version=*" -ErrorAction SilentlyContinue + if (-not $vsComponent) { + Write-Error "WDK Visual Studio Component not found. Ensure the WDK Component is installed." + exit 1 + } + $result.WdkVsComponentVersion = [regex]::Match($vsComponent.Name, '(\d+\.){3}\d+').Value + } + + return $result +} + +function Import-SampleExclusions { + <# + .SYNOPSIS + Loads exclusions.csv and returns exclusion objects applicable to the current build. + .DESCRIPTION + Each returned exclusion has: + - Pattern: dot-separated path (may contain wildcards, e.g. 'general.*') + - Configurations: semicolon-separated config|platform patterns (or '*' for all) + - Reason: human-readable explanation + + Only exclusions whose [MinBuild, MaxBuild] range includes the given build number + are returned. Exclusions outside the range are silently skipped. + .NOTES + CSV format: Path,Configurations,MinBuild,MaxBuild,Reason + Example row: network\wlan\wdi,*,,27100,"failure introduced in VS17.14" + #> + param( + [string]$CsvPath, + [int]$BuildNumber + ) + + if (-not (Test-Path $CsvPath)) { + Write-Warning "Exclusions file not found: $CsvPath. No exclusions will be applied." + return @() + } + + $exclusions = [System.Collections.ArrayList]::new() + Import-Csv $CsvPath | ForEach-Object { + $pattern = $_.Path.Trim('\').Replace('\', '.').ToLower() + $configs = if ([string]::IsNullOrWhiteSpace($_.Configurations)) { '*' } else { $_.Configurations } + $minBuild = if ([string]::IsNullOrWhiteSpace($_.MinBuild)) { 0 } else { [int]$_.MinBuild } + $maxBuild = if ([string]::IsNullOrWhiteSpace($_.MaxBuild)) { 99999 } else { [int]$_.MaxBuild } + + if ($minBuild -le $BuildNumber -and $BuildNumber -le $maxBuild) { + [void]$exclusions.Add([PSCustomObject]@{ + Pattern = $pattern + Configurations = $configs + Reason = $_.Reason + }) + Write-Verbose "Exclusion applied: '$pattern' configs='$configs' reason='$($_.Reason)'" + } + else { + Write-Verbose "Exclusion skipped: '$pattern' - build $BuildNumber outside [$minBuild, $maxBuild]" + } + } + + return $exclusions.ToArray() +} + +function Get-DiskFreeGB { + <# + .SYNOPSIS Returns free disk space in GB for the current drive, or 'N/A' on error. + #> + try { + return [math]::Round((Get-Volume (Get-Item '.').PSDrive.Name).SizeRemaining / 1GB, 1) + } + catch { + return 'N/A' + } +} + +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*(?(?.*)\|(?.*))\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 +# ============================================================================= + +$root = (Get-Location).Path + +Initialize-DevShell -ReturnToDirectory $root +Assert-MsBuildAvailable + +# ============================================================================= +# Step 2 - Calculate Parallelism +# ============================================================================= + +$throttleFactor = 5 +# Sum across all CPU sockets (Get-CimInstance returns an array on multi-socket systems) +$logicalProcessors = ((Get-CimInstance -Class CIM_Processor -Verbose:$false).NumberOfLogicalProcessors | Measure-Object -Sum).Sum + +if ($ThrottleLimit -eq 0) { + $ThrottleLimit = $throttleFactor * $logicalProcessors +} + +$verbose = $PSBoundParameters.ContainsKey('Verbose') -and $PSBoundParameters['Verbose'] + +# ============================================================================= +# Step 3 - Prepare Log Directory +# ============================================================================= + +Remove-Item -Recurse -Path $LogFilesDirectory -ErrorAction SilentlyContinue +New-Item -ItemType Directory -Force -Path $LogFilesDirectory | Out-Null + +$reportHtmlPath = Join-Path $LogFilesDirectory "$ReportFileName.htm" +$reportCsvPath = Join-Path $LogFilesDirectory "$ReportFileName.csv" + +# ============================================================================= +# Step 4 - Load Sample List +# ============================================================================= + +# Always discover the full sample list when patterns contain wildcards, +# or when no -Samples were provided at all. +$hasWildcards = $Samples | Where-Object { $_ -match '[*?]' } + +if (-not $Samples) { + # No filter: discover and build everything. + Write-Verbose "No -Samples provided. Discovering samples via ListAllSamples.ps1..." + $sampleNames = & (Join-Path $PSScriptRoot 'ListAllSamples.ps1') -Verbose:$verbose | + Where-Object { -not [string]::IsNullOrWhiteSpace($_) } +} +elseif ($hasWildcards) { + # One or more entries contain wildcards — discover all, then filter with -like. + Write-Verbose "Wildcard detected in -Samples. Discovering all samples to match patterns..." + $allSamples = & (Join-Path $PSScriptRoot 'ListAllSamples.ps1') -Verbose:$verbose | + Where-Object { -not [string]::IsNullOrWhiteSpace($_) } + $sampleNames = @() + foreach ($pattern in $Samples) { + $matched = $allSamples | Where-Object { $_ -like $pattern } + if ($matched) { + $sampleNames += $matched + } + else { + Write-Warning "Pattern '$pattern' did not match any samples." + } + } + $sampleNames = $sampleNames | Sort-Object -Unique +} +else { + # Exact list passed by the caller — use as-is, sorted alphabetically. + $sampleNames = $Samples | Sort-Object +} + +# Map sample names to directory paths, validating each exists +$sampleSet = [ordered]@{} +$skippedCount = 0 +foreach ($name in $sampleNames) { + $fullPath = Join-Path $root ($name.Replace('.', '\')) + if (Test-Path $fullPath -PathType Container) { + $sampleSet[$name] = $fullPath + } + else { + Write-Warning "Sample directory not found, skipping: $name" + $skippedCount++ + } +} + +if ($sampleSet.Count -eq 0) { + Write-Error "No valid sample directories found. Ensure ListAllSamples.ps1 is available in the repo root." + exit 1 +} + +# ============================================================================= +# Step 5 - Detect Build Environment +# ============================================================================= + +$buildEnv = Resolve-BuildEnvironment -RepoRoot $root +$buildNumber = $buildEnv.BuildNumber + +# ============================================================================= +# Step 6 - Determine InfVerif Options +# ============================================================================= +# +# Samples must build cleanly, but certain InfVerif warnings are acceptable because +# they flag issues intentionally present in samples (to be fixed when productizing). +# <= 22621: suppress individual warnings /sw1284 /sw1285 /sw1293 /sw2083 /sw2086 +# > 22621: these are grouped under /samples +# +if ($InfOptions) { + $infVerifOptions = $InfOptions +} +else { + $infVerifOptions = if ($buildNumber -le 22621) { '/sw1284 /sw1285 /sw1293 /sw2083 /sw2086' } else { '/samples' } +} + +# ============================================================================= +# Step 7 - Load Exclusions +# ============================================================================= + +$exclusions = Import-SampleExclusions -CsvPath (Join-Path $root 'exclusions.csv') -BuildNumber $buildNumber + +# ============================================================================= +# Step 8 - Print Build Plan +# ============================================================================= + +$combinationsTotal = $sampleSet.Count * $Configurations.Count * $Platforms.Count + +Write-Output "" +Write-Output "--- WDK Sample Build Plan ------------------------------------------" +Write-Output " Environment: $($buildEnv.Name)" +Write-Output " Build Number: $buildNumber" +if ($buildEnv.NuGetVersion) { + Write-Output " NuGet Version: $($buildEnv.NuGetVersion)" +} +Write-Output " WDK VS Component: $($buildEnv.WdkVsComponentVersion)" +Write-Output " InfVerif Options: $infVerifOptions" +Write-Output "" +Write-Output " Samples: $($sampleSet.Count) ($skippedCount skipped)" +Write-Output " Configurations: $($Configurations -join ', ')" +Write-Output " Platforms: $($Platforms -join ', ')" +Write-Output " Combinations: $combinationsTotal" +Write-Output " Exclusions: $($exclusions.Count)" +Write-Output "" +Write-Output " Parallelism: $ThrottleLimit jobs ($logicalProcessors cores x $throttleFactor)" +Write-Output " Disk Free (GB): $(Get-DiskFreeGB)" +Write-Output " Wipe Outputs: $(-not [string]::IsNullOrEmpty($env:WDS_WipeOutputs))" +Write-Output "--------------------------------------------------------------------" +Write-Output "" +Write-Output "Progress legend:" +Write-Output " T=Total B=Built R=Running P=Pending" +Write-Output " S=Succeeded E=Excluded U=Unsupported F=Failed O=Sporadic" +Write-Output "" +Write-Output "Building all combinations..." + +# ============================================================================= +# Step 9 - Execute Parallel Builds +# ============================================================================= + +# Shared mutable state protected by a Mutex. This is required because +# ForEach-Object -Parallel runs each iteration in a separate runspace, +# so standard .NET locks (Monitor/lock) do not work across runspaces. +$buildState = @{ + Built = 0 + Succeeded = 0 + Excluded = 0 + Unsupported = 0 + Failed = 0 + Sporadic = 0 + Results = @() + FailSet = @() + SporadicSet = @() + Lock = [System.Threading.Mutex]::new($false) +} + +$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 + $exclusions = $using:exclusions + $configs = $using:Configurations + $platforms = $using:Platforms + $infOpts = $using:infVerifOptions + $isVerbose = $using:verbose + $state = $using:buildState + $total = $using:combinationsTotal + $throttle = $using:ThrottleLimit + + # Reconstruct the function inside this parallel runspace + ${function:Build-SingleSample} = $using:buildSingleSampleDef + + $sampleName = $_.Key + $directory = $_.Value + + # Build a result row: one column per configuration|platform combination + $resultRow = [PSCustomObject]@{ Sample = $sampleName } + + foreach ($configuration in $configs) { + foreach ($platform in $platforms) { + $result = 'Not run' + $succeededDelta = 0 + $excludedDelta = 0 + $unsupportedDelta = 0 + $failedDelta = 0 + $sporadicDelta = 0 + $failEntry = $null + $sporadicEntry = $null + + # -- Check exclusions (supports wildcard paths like 'general.*') -- + $exclusionReason = $null + foreach ($excl in $exclusions) { + if ($sampleName -like $excl.Pattern) { + $configKey = "$configuration|$platform" + foreach ($cfgPattern in $excl.Configurations.Split(';')) { + if ($configKey -like $cfgPattern) { + $exclusionReason = $excl.Reason + break + } + } + if ($exclusionReason) { break } + } + } + + if ($exclusionReason) { + Write-Verbose "[$sampleName $configuration|$platform] Excluded: $exclusionReason" + $excludedDelta = 1 + $result = 'Excluded' + } + else { + # -- Build the sample -- + $buildResult = Build-SingleSample ` + -Directory $directory -SampleName $sampleName ` + -LogFilesDirectory $logDir -Configuration $configuration ` + -Platform $platform -InfVerif_AdditionalOptions $infOpts ` + -Verbose:$isVerbose + + # 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 ($buildResult) { + 0 { $succeededDelta = 1; $result = 'Succeeded' } + 1 { $failedDelta = 1; $result = 'Failed'; $failEntry = "$sampleName $configuration|$platform" } + 2 { $sporadicDelta = 1; $result = 'Sporadic'; $sporadicEntry = "$sampleName $configuration|$platform" } + default { $unsupportedDelta = 1; $result = 'Unsupported' } + } + } + + $resultRow | Add-Member -MemberType NoteProperty -Name "$configuration|$platform" -Value $result + + # -- Update shared counters (under lock) -- + $null = $state.Lock.WaitOne() + try { + $state.Built += 1 + $state.Succeeded += $succeededDelta + $state.Excluded += $excludedDelta + $state.Unsupported += $unsupportedDelta + $state.Failed += $failedDelta + $state.Sporadic += $sporadicDelta + if ($failEntry) { $state.FailSet += $failEntry } + if ($sporadicEntry) { $state.SporadicSet += $sporadicEntry } + + # Update progress bar + $built = $state.Built + $remaining = $total - $built + $running = [Math]::Min($remaining, $throttle) + $pending = [Math]::Max($remaining - $throttle, 0) + $pct = [Math]::Round(100 * $built / $total) + + $statusLine = "$built of $total combinations built ($pct%) | " + + "T:$total; B:$built; R:$running; P:$pending | " + + "S:$($state.Succeeded); E:$($state.Excluded); U:$($state.Unsupported); F:$($state.Failed); O:$($state.Sporadic)" + + # Write-Host with carriage return for a single-line progress indicator. + # Write-Progress does not reliably render from -Parallel runspaces. + Write-Host "`rBuilding combinations [$statusLine]" -NoNewline + } + finally { + $state.Lock.ReleaseMutex() + } + } + } + + # Append the completed result row + $null = $state.Lock.WaitOne() + try { + $state.Results += $resultRow + } + finally { + $state.Lock.ReleaseMutex() + } +} + +$stopwatch.Stop() + +# End the progress line +Write-Host "" + +# ============================================================================= +# Step 10 - Report Failures +# ============================================================================= + +Write-Output "" + +if ($buildState.FailSet.Count -gt 0) { + Write-Output "--- Build Failures -------------------------------------------------" + foreach ($entry in ($buildState.FailSet | Sort-Object)) { + if ($entry -match '^(?.*)\s+(?\w+)\|(?\w+)$') { + $errLog = Join-Path $LogFilesDirectory "$($Matches.name).$($Matches.config).$($Matches.platform).0.err" + Write-Output " [FAIL] $($Matches.name) ($($Matches.config)|$($Matches.platform)):" + if (Test-Path $errLog) { + Get-Content $errLog | ForEach-Object { Write-Output " $_" } + } + else { + Write-Output " (error log not found: $errLog)" + } + } + } + Write-Output "" + Write-Error "Some combinations were built with errors." +} + +if ($buildState.SporadicSet.Count -gt 0) { + Write-Output "--- Sporadic Failures (succeeded on retry) -------------------------" + foreach ($entry in ($buildState.SporadicSet | Sort-Object)) { + if ($entry -match '^(?.*)\s+(?\w+)\|(?\w+)$') { + $errLog = Join-Path $LogFilesDirectory "$($Matches.name).$($Matches.config).$($Matches.platform).0.err" + Write-Output " [SPORADIC] $($Matches.name) ($($Matches.config)|$($Matches.platform)):" + if (Test-Path $errLog) { + Get-Content $errLog | ForEach-Object { Write-Output " $_" } + } + } + } + Write-Output "" + Write-Error "Some combinations had sporadic build failures." +} + +# ============================================================================= +# Step 11 - Final Summary +# ============================================================================= + +$elapsed = $stopwatch.Elapsed + +Write-Output "--- Build Complete -------------------------------------------------" +Write-Output " Elapsed: $($elapsed.Minutes)m $($elapsed.Seconds)s" +Write-Output " Disk Free (GB): $(Get-DiskFreeGB)" +Write-Output "" +Write-Output " Samples: $($sampleSet.Count)" +Write-Output " Configurations: $($Configurations -join ', ')" +Write-Output " Platforms: $($Platforms -join ', ')" +Write-Output " Combinations: $combinationsTotal" +Write-Output "" +Write-Output " Succeeded: $($buildState.Succeeded)" +Write-Output " Excluded: $($buildState.Excluded)" +Write-Output " Unsupported: $($buildState.Unsupported)" +Write-Output " Failed: $($buildState.Failed)" +Write-Output " Sporadic: $($buildState.Sporadic)" +Write-Output "" +Write-Output " Log directory: $LogFilesDirectory" +Write-Output " CSV report: $reportCsvPath" +Write-Output " HTML report: $reportHtmlPath" +Write-Output "--------------------------------------------------------------------" + +# ============================================================================= +# Step 12 - Generate Reports +# ============================================================================= + +$sortedResults = $buildState.Results | Sort-Object { $_.Sample } +$sortedResults | ConvertTo-Csv | Out-File $reportCsvPath +$sortedResults | ConvertTo-Html -Title "WDK Sample Build Overview" | Out-File $reportHtmlPath + +# Only open the HTML report interactively (not in CI/automation) +if (-not $env:GITHUB_REPOSITORY -and -not $env:BUILD_BUILDID -and [Environment]::UserInteractive) { + Invoke-Item $reportHtmlPath +} diff --git a/Building-Locally.md b/Building-Locally.md index 312c59674..645e9a615 100644 --- a/Building-Locally.md +++ b/Building-Locally.md @@ -129,80 +129,82 @@ Microsoft.Windows.WDK.arm64.10.0.26000.1 --- -## Step 6: Check all samples builds with expected results for all flavors +## Step 6: Build all samples ```powershell pwsh -.\Build-AllSamples +.\Build-Samples ``` -Above builds all samples for all configurations and platforms. +Above builds all samples for all configurations and platforms. -You can refine what exact samples to build, what configurations, and platforms to build. build Here are a few examples: +You can refine what exact samples to build, what configurations, and platforms to build. Here are a few examples: ```powershell # Get Help: -Get-Help .\Build-AllSamples +Get-Help .\Build-Samples # Build all solutions for all flavors with builds running in parallel: -.\Build-AllSamples +.\Build-Samples # Build with Verbose output (print start and finish of each sample): -.\Build-AllSamples -Verbose +.\Build-Samples -Verbose -# Build without massive parallism (slow, but good debugging): -.\Build-AllSamples -ThrottleLimit 1 +# Build without massive parallelism (slow, but good for debugging): +.\Build-Samples -ThrottleLimit 1 -# Build the solutions in the tools folder for all flavors: -.\Build-AllSamples -Samples '^tools.' -Configurations 'Debug','Release' -Platforms 'x64','arm64' +# Build all samples inside the 'tools' folder using wildcards: +.\Build-Samples -Samples 'tools.*' -# Build the solutions in the tools folder for only 'Debug|x64': -.\Build-AllSamples -Samples '^tools.' -Configurations 'Debug' -Platforms 'x64' +# Build specific samples for only 'Debug|x64': +.\Build-Samples -Samples 'tools.sdv.samples.sampledriver' -Configurations 'Debug' -Platforms 'x64' ``` Example of expected output: ``` -Build Environment: NuGet -Build Number: 26100 -Samples: 132 -Configurations: 2 (Debug Release) -Platforms: 2 (x64 arm64) -InfVerif_AdditionalOptions: /samples -Combinations: 528 -LogicalProcessors: 12 -ThrottleFactor: 5 -ThrottleLimit: 60 -WDS_WipeOutputs: -Disk Remaining (GB): ... - -T: Combinations -B: Built -R: Build is running currently -P: Build is pending an available build slot - -S: Built and result was 'Succeeded' -E: Built and result was 'Excluded' -U: Built and result was 'Unsupported' (Platform and Configuration combination) -F: Built and result was 'Failed' -O: Built and result was 'Sporadic' +--- WDK Sample Build Plan ------------------------------------------ + Environment: NuGet + Build Number: 26100 + NuGet Version: 10.0.26100.1 + WDK VS Component: 10.0.26100.1882 + InfVerif Options: /samples + + Samples: 132 (0 skipped) + Configurations: Debug, Release + Platforms: x64, arm64 + Combinations: 528 + Exclusions: 4 + + Parallelism: 60 jobs (12 cores x 5) + Disk Free (GB): ... + Wipe Outputs: False +-------------------------------------------------------------------- + +Progress legend: + T=Total B=Built R=Running P=Pending + S=Succeeded E=Excluded U=Unsupported F=Failed O=Sporadic Building all combinations... -Built all combinations. +--- Build Complete ------------------------------------------------- + Elapsed: 12m 42s + Disk Free (GB): ... + + Samples: 132 + Configurations: Debug, Release + Platforms: x64, arm64 + Combinations: 528 + + Succeeded: 526 + Excluded: 0 + Unsupported: 2 + Failed: 0 + Sporadic: 0 -Elapsed time: 12 minutes, 42 seconds. -Disk Remaining (GB): ... -Samples: 132 -Configurations: 2 (Debug Release) -Platforms: 2 (x64 arm64) -Combinations: 528 -Succeeded: 526 -Excluded: 0 -Unsupported: 2 -Failed: 0 -Sporadic: 0 -Log files directory: .\_logs -Overview report: .\_overview.htm + Log directory: .\_logs + CSV report: .\_logs\_overview.csv + HTML report: .\_logs\_overview.htm +-------------------------------------------------------------------- ``` --- @@ -214,7 +216,7 @@ To restore a specific version of our WDK NuGet packages: Follow these steps before running "nuget restore" command: * Open the .\packages.config file and update the full version (including the branch if required) in all three entries. * Open the .\Directory.build.props file and update the version and build of the package with the same values as in previous step. -* Open .\Build-SampleSet and change the NuGet build number (used by .\exclusions.csv and for determining infverif flags) +* Open .\Build-Samples.ps1 and check the NuGet build number logic (used by .\exclusions.csv and for determining infverif flags) * Now you can run "nuget restore" A few examples of how to interact with nuget: diff --git a/ListAllSamples.ps1 b/ListAllSamples.ps1 new file mode 100644 index 000000000..1a1a4a784 --- /dev/null +++ b/ListAllSamples.ps1 @@ -0,0 +1,49 @@ +<# +.SYNOPSIS + Enumerates all available sample solutions in the repository and writes them to the console. + +.DESCRIPTION + Searches for all .sln files recursively from the repo root, excludes NuGet package directories + (paths containing 'packages' as a segment), computes a normalized sample name for each, and writes + the sorted list to stdout (one sample name per line). + + The sample name is derived from the relative directory path: backslashes are replaced with dots + and the result is lowercased. + +.EXAMPLE + .\ListAllSamples + + Discovers all samples and writes the sorted names to the console. + +.OUTPUTS + Sorted sample names written to stdout, one per line. +#> + +[CmdletBinding()] +param() + +$root = (Get-Location).Path + +# Discover all .sln files +$solutionFiles = Get-ChildItem -Path $root -Recurse -Filter *.sln | Select-Object -ExpandProperty FullName + +$sampleNames = @{} + +foreach ($file in $solutionFiles) { + $dir = (Get-Item $file).DirectoryName + $dirNorm = $dir.Replace($root, '').Trim('\').Replace('\', '.').ToLower() + + if ($dirNorm -match '(^|\.|\b)packages(\.|$)') { + Write-Verbose "Ignored NuGet package directory: $dirNorm" + continue + } + + if (-not $sampleNames.ContainsKey($dirNorm)) { + $sampleNames[$dirNorm] = $true + } +} + +$sortedNames = $sampleNames.Keys | Sort-Object + +Write-Verbose "Found $($sortedNames.Count) samples." +$sortedNames | Write-Output diff --git a/simbatt/func/batclass_prepublish.h b/simbatt/func/batclass_prepublish.h deleted file mode 100644 index a952222b3..000000000 --- a/simbatt/func/batclass_prepublish.h +++ /dev/null @@ -1,51 +0,0 @@ -/*++ - -Copyright (c) Microsoft Corporation. All rights reserved. - -Module Name: - - batclass_prepublish.h - -Abstract: - - This module defines pre-publish definations to be made available in DDK/SDK. - - N.B. This file must not be included when batclass.h defines - BATTERY_MINIPORT_INFO_V1_1 type and BATTERY_CLASS_MINOR_VERSION_1. - - N.B. This code is provided "AS IS" without any expressed or implied warranty. - ---*/ - -//---------------------------------------------------------------------- Pragmas - -#pragma once - -//--------------------------------------------------------------------- Includes - -#include - -//-------------------------------------------------- Would be Public Definitions - -#ifndef BATTERY_CLASS_MINOR_VERSION_1 - -typedef struct { - USHORT MajorVersion; - USHORT MinorVersion; - - PVOID Context; // Miniport context - - BCLASS_QUERY_TAG QueryTag; - BCLASS_QUERY_INFORMATION QueryInformation; - BCLASS_SET_INFORMATION SetInformation; - BCLASS_QUERY_STATUS QueryStatus; - BCLASS_SET_STATUS_NOTIFY SetStatusNotify; - BCLASS_DISABLE_STATUS_NOTIFY DisableStatusNotify; - PDEVICE_OBJECT Pdo; - PUNICODE_STRING DeviceName; - PDEVICE_OBJECT Fdo; -} BATTERY_MINIPORT_INFO_V1_1, *PBATTERY_MINIPORT_INFO_V1_1; - -#define BATTERY_CLASS_MINOR_VERSION_1 0x0001 - -#endif // BATTERY_CLASS_MINOR_VERSION_1 diff --git a/simbatt/func/miniclass.c b/simbatt/func/miniclass.c index 612b4c660..78a13bdf6 100644 --- a/simbatt/func/miniclass.c +++ b/simbatt/func/miniclass.c @@ -239,9 +239,7 @@ Return Value: DevExt->State.BatteryStatus.Capacity = 100; DevExt->State.BatteryStatus.Voltage = BATTERY_UNKNOWN_VOLTAGE; DevExt->State.BatteryStatus.Rate = 0; - DevExt->State.BatteryInfo.Capabilities = BATTERY_SYSTEM_BATTERY | - BATTERY_CAPACITY_RELATIVE; - + DevExt->State.BatteryInfo.Capabilities = BATTERY_SYSTEM_BATTERY; DevExt->State.BatteryInfo.Technology = 1; DevExt->State.BatteryInfo.Chemistry[0] = 'F'; DevExt->State.BatteryInfo.Chemistry[1] = 'a'; diff --git a/simbatt/func/simbattdriverif.h b/simbatt/func/simbattdriverif.h index 6fb45d8bb..04549f725 100644 --- a/simbatt/func/simbattdriverif.h +++ b/simbatt/func/simbattdriverif.h @@ -25,20 +25,6 @@ Module Name: //------------------------------------------------------------------ Definitions -// -// Battery bus driver interface -// - -// {780AC894-01FF-4b5e-B4C8-9C00709200EB} -DEFINE_GUID(BATTBUS_DEVINTERFACE_GUID, - 0x780ac894, 0x1ff, 0x4b5e, 0xb4, 0xc8, 0x9c, 0x0, 0x70, 0x92, 0x0, 0xeb); - -#define BATTBUS_IOCTL(_index_) \ - CTL_CODE(FILE_DEVICE_BUS_EXTENDER, _index_, METHOD_BUFFERED, FILE_READ_DATA) - -#define IOCTL_BATTBUS_PLUGIN_HARDWARE BATTBUS_IOCTL(0x0) -#define IOCTL_BATTBUS_UNPLUG_HARDWARE BATTBUS_IOCTL(0x1) - // // Simulated battery ioctl interface // @@ -62,67 +48,3 @@ DEFINE_GUID(SIMBATT_DEVINTERFACE_GUID, #define IOCTL_SIMBATT_SET_UNIQUE_ID SIMBATT_IOCTL(0x809) #define IOCTL_SIMBATT_GET_MAXCHARGINGCURRENT SIMBATT_IOCTL(0x810) #define SIMBATT_RATE_CALCULATE 0x7fffffff -#define MAX_SUPPORTED_SIMBATT_CHILDREN 20 - -//------------------------------------------------------------------- Data Types - -// -// Data structure used in PlugIn and UnPlug ioctls -// - -#define BATTBUS_TYPE_SIMBATT 0; - -typedef struct _BATTBUS_PLUGIN_HARDWARE -{ - // - // Size of this type. - // - - ULONG Size; - - // - // Unique serial number of the device to be enumerated. - // Enumeration will be failed if another device on the - // bus has the same serial number. - // - - ULONG SerialNo; - - // - // UI number. - // - - ULONG UINumber; - - // - // Type of device being enumerated - // - // Reserved value, set to 0. - // - - ULONG Type; - -} BATTBUS_PLUGIN_HARDWARE, *PBATTBUS_PLUGIN_HARDWARE; - -typedef struct _BATTBUS_UNPLUG_HARDWARE -{ - // - // size of this type - // - - ULONG Size; - - // - // Serial number of the device to be unplugged. - // - - ULONG SerialNo; - - // - // Must not be referenced used. - // - - ULONG Reserved[2]; - -} BATTBUS_UNPLUG_HARDWARE, *PBATTBUS_UNPLUG_HARDWARE; - diff --git a/simbatt/func/wdf.c b/simbatt/func/wdf.c index de9aee307..3b9e87adf 100644 --- a/simbatt/func/wdf.c +++ b/simbatt/func/wdf.c @@ -19,7 +19,7 @@ Module Name: #include "simbatt.h" #include "simbattdriverif.h" -#include "batclass_prepublish.h" +#include //------------------------------------------------------------------- Prototypes