2019-06-13 12:10:50 +00:00
param (
2021-05-01 13:00:30 +00:00
[ Parameter ( Mandatory = $true ) ] [ string ] $InputPath , # Full path to directory where NuGet packages to be checked are stored
[ Parameter ( Mandatory = $true ) ] [ string ] $ExtractPath , # Full path to directory where the packages will be extracted during validation
[ Parameter ( Mandatory = $true ) ] [ string ] $DotnetSymbolVersion , # Version of dotnet symbol to use
[ Parameter ( Mandatory = $false ) ] [ switch ] $CheckForWindowsPdbs , # If we should check for the existence of windows pdbs in addition to portable PDBs
[ Parameter ( Mandatory = $false ) ] [ switch ] $ContinueOnError , # If we should keep checking symbols after an error
2021-10-20 12:58:55 +00:00
[ Parameter ( Mandatory = $false ) ] [ switch ] $Clean , # Clean extracted symbols directory after checking symbols
[ Parameter ( Mandatory = $false ) ] [ string ] $SymbolExclusionFile # Exclude the symbols in the file from publishing to symbol server
2019-06-13 12:10:50 +00:00
)
2021-10-20 12:58:55 +00:00
. $PSScriptRoot \ . . \ tools . ps1
2020-05-30 12:44:29 +00:00
# Maximum number of jobs to run in parallel
2021-05-04 13:00:28 +00:00
$MaxParallelJobs = 16
2019-06-13 12:10:50 +00:00
2020-10-31 12:53:54 +00:00
# Max number of retries
$MaxRetry = 5
2020-05-30 12:44:29 +00:00
# Wait time between check for system load
$SecondsBetweenLoadChecks = 10
2019-06-13 12:10:50 +00:00
2020-10-31 12:53:54 +00:00
# Set error codes
Set-Variable -Name " ERROR_BADEXTRACT " -Option Constant -Value -1
Set-Variable -Name " ERROR_FILEDOESNOTEXIST " -Option Constant -Value -2
2021-05-01 13:00:30 +00:00
$WindowsPdbVerificationParam = " "
if ( $CheckForWindowsPdbs ) {
$WindowsPdbVerificationParam = " --windows-pdbs "
}
2021-10-20 12:58:55 +00:00
$ExclusionSet = New-Object System . Collections . Generic . HashSet [ string ] ;
if ( ! $InputPath -or ! ( Test-Path $InputPath ) ) {
Write-Host " No symbols to validate. "
ExitWithExitCode 0
}
#Check if the path exists
if ( $SymbolExclusionFile -and ( Test-Path $SymbolExclusionFile ) ) {
[ string[] ] $Exclusions = Get-Content " $SymbolExclusionFile "
$Exclusions | foreach { if ( $_ -and $_ . Trim ( ) ) { $ExclusionSet . Add ( $_ ) } }
}
else {
Write-Host " Symbol Exclusion file does not exists. No symbols to exclude. "
}
2020-05-30 12:44:29 +00:00
$CountMissingSymbols = {
2019-06-13 12:10:50 +00:00
param (
2021-05-01 13:00:30 +00:00
[ string ] $PackagePath , # Path to a NuGet package
[ string ] $WindowsPdbVerificationParam # If we should check for the existence of windows pdbs in addition to portable PDBs
2019-06-13 12:10:50 +00:00
)
2020-05-30 12:44:29 +00:00
Add-Type -AssemblyName System . IO . Compression . FileSystem
2020-10-31 12:53:54 +00:00
Write-Host " Validating $PackagePath "
2019-06-13 12:10:50 +00:00
# Ensure input file exist
if ( ! ( Test-Path $PackagePath ) ) {
Write-PipelineTaskError " Input file does not exist: $PackagePath "
2020-10-31 12:53:54 +00:00
return [ pscustomobject ] @ {
2021-05-01 13:00:30 +00:00
result = $using : ERROR_FILEDOESNOTEXIST
2020-10-31 12:53:54 +00:00
packagePath = $PackagePath
}
2019-06-13 12:10:50 +00:00
}
# Extensions for which we'll look for symbols
2019-11-22 13:41:58 +00:00
$RelevantExtensions = @ ( '.dll' , '.exe' , '.so' , '.dylib' )
2019-06-13 12:10:50 +00:00
# How many files are missing symbol information
$MissingSymbols = 0
$PackageId = [ System.IO.Path ] :: GetFileNameWithoutExtension ( $PackagePath )
$PackageGuid = New-Guid
2020-05-30 12:44:29 +00:00
$ExtractPath = Join-Path -Path $using : ExtractPath -ChildPath $PackageGuid
2019-11-22 13:41:58 +00:00
$SymbolsPath = Join-Path -Path $ExtractPath -ChildPath 'Symbols'
2019-06-13 12:10:50 +00:00
2020-05-12 12:48:04 +00:00
try {
[ System.IO.Compression.ZipFile ] :: ExtractToDirectory ( $PackagePath , $ExtractPath )
}
catch {
Write-Host " Something went wrong extracting $PackagePath "
Write-Host $_
2020-07-08 13:11:11 +00:00
return [ pscustomobject ] @ {
2021-05-01 13:00:30 +00:00
result = $using : ERROR_BADEXTRACT
2020-07-08 13:11:11 +00:00
packagePath = $PackagePath
}
2020-05-12 12:48:04 +00:00
}
2019-06-13 12:10:50 +00:00
Get-ChildItem -Recurse $ExtractPath |
2021-05-01 13:00:30 +00:00
Where-Object { $RelevantExtensions -contains $_ . Extension } |
ForEach-Object {
$FileName = $_ . FullName
if ( $FileName -Match '\\ref\\' ) {
Write-Host " `t Ignoring reference assembly file " $FileName
return
}
2019-06-13 12:10:50 +00:00
2021-05-01 13:00:30 +00:00
$FirstMatchingSymbolDescriptionOrDefault = {
2020-05-30 12:44:29 +00:00
param (
2021-05-01 13:00:30 +00:00
[ string ] $FullPath , # Full path to the module that has to be checked
[ string ] $TargetServerParam , # Parameter to pass to `Symbol Tool` indicating the server to lookup for symbols
[ string ] $WindowsPdbVerificationParam , # Parameter to pass to potential check for windows-pdbs.
2020-05-30 12:44:29 +00:00
[ string ] $SymbolsPath
)
$FileName = [ System.IO.Path ] :: GetFileName ( $FullPath )
$Extension = [ System.IO.Path ] :: GetExtension ( $FullPath )
# Those below are potential symbol files that the `dotnet symbol` might
# return. Which one will be returned depend on the type of file we are
# checking and which type of file was uploaded.
# The file itself is returned
$SymbolPath = $SymbolsPath + '\' + $FileName
# PDB file for the module
$PdbPath = $SymbolPath . Replace ( $Extension , '.pdb' )
# PDB file for R2R module (created by crossgen)
$NGenPdb = $SymbolPath . Replace ( $Extension , '.ni.pdb' )
# DBG file for a .so library
$SODbg = $SymbolPath . Replace ( $Extension , '.so.dbg' )
# DWARF file for a .dylib
$DylibDwarf = $SymbolPath . Replace ( $Extension , '.dylib.dwarf' )
2021-05-01 13:00:30 +00:00
2020-05-30 12:44:29 +00:00
$dotnetSymbolExe = " $env:USERPROFILE \.dotnet\tools "
$dotnetSymbolExe = Resolve-Path " $dotnetSymbolExe \dotnet-symbol.exe "
2020-10-31 12:53:54 +00:00
$totalRetries = 0
2020-05-30 12:44:29 +00:00
2020-10-31 12:53:54 +00:00
while ( $totalRetries -lt $using : MaxRetry ) {
2021-05-08 02:26:41 +00:00
2020-10-31 12:53:54 +00:00
# Save the output and get diagnostic output
2021-05-01 13:00:30 +00:00
$output = & $dotnetSymbolExe - -symbols - -modules $WindowsPdbVerificationParam $TargetServerParam $FullPath -o $SymbolsPath - -diagnostics | Out-String
2020-10-31 12:53:54 +00:00
2021-11-06 13:21:46 +00:00
if ( ( Test-Path $PdbPath ) -and ( Test-path $SymbolPath ) ) {
return 'Module and PDB for Module'
2020-10-31 12:53:54 +00:00
}
2021-11-06 13:21:46 +00:00
elseif ( ( Test-Path $NGenPdb ) -and ( Test-Path $PdbPath ) -and ( Test-Path $SymbolPath ) ) {
return 'Dll, PDB and NGen PDB'
2020-10-31 12:53:54 +00:00
}
2021-11-06 13:21:46 +00:00
elseif ( ( Test-Path $SODbg ) -and ( Test-Path $SymbolPath ) ) {
return 'So and DBG for SO'
2020-10-31 12:53:54 +00:00
}
2021-11-06 13:21:46 +00:00
elseif ( ( Test-Path $DylibDwarf ) -and ( Test-Path $SymbolPath ) ) {
return 'Dylib and Dwarf for Dylib'
2020-10-31 12:53:54 +00:00
}
elseif ( Test-Path $SymbolPath ) {
return 'Module'
}
2021-05-14 13:08:05 +00:00
else
{
2020-10-31 12:53:54 +00:00
$totalRetries + +
}
2020-05-30 12:44:29 +00:00
}
2020-10-31 12:53:54 +00:00
return $null
2020-05-30 12:44:29 +00:00
}
2021-10-20 12:58:55 +00:00
$FileRelativePath = $FileName . Replace ( " $ExtractPath \ " , " " )
if ( ( $ ( $using : ExclusionSet ) -ne $null ) -and ( $ ( $using : ExclusionSet ) . Contains ( $FileRelativePath ) -or ( $ ( $using : ExclusionSet ) . Contains ( $FileRelativePath . Replace ( " \ " , " / " ) ) ) ) ) {
Write-Host " Skipping $FileName from symbol validation "
2021-05-01 13:00:30 +00:00
}
2021-10-20 12:58:55 +00:00
else {
$FileGuid = New-Guid
$ExpandedSymbolsPath = Join-Path -Path $SymbolsPath -ChildPath $FileGuid
$SymbolsOnMSDL = & $FirstMatchingSymbolDescriptionOrDefault `
-FullPath $FileName `
-TargetServerParam '--microsoft-symbol-server' `
-SymbolsPath " $ExpandedSymbolsPath -msdl " `
-WindowsPdbVerificationParam $WindowsPdbVerificationParam
$SymbolsOnSymWeb = & $FirstMatchingSymbolDescriptionOrDefault `
-FullPath $FileName `
-TargetServerParam '--internal-server' `
-SymbolsPath " $ExpandedSymbolsPath -symweb " `
-WindowsPdbVerificationParam $WindowsPdbVerificationParam
Write-Host -NoNewLine " `t Checking file " $FileName " ... "
if ( $SymbolsOnMSDL -ne $null -and $SymbolsOnSymWeb -ne $null ) {
Write-Host " Symbols found on MSDL ( $SymbolsOnMSDL ) and SymWeb ( $SymbolsOnSymWeb ) "
2019-06-13 12:10:50 +00:00
}
else {
2021-10-20 12:58:55 +00:00
$MissingSymbols + +
if ( $SymbolsOnMSDL -eq $null -and $SymbolsOnSymWeb -eq $null ) {
Write-Host 'No symbols found on MSDL or SymWeb!'
2019-06-13 12:10:50 +00:00
}
else {
2021-10-20 12:58:55 +00:00
if ( $SymbolsOnMSDL -eq $null ) {
Write-Host 'No symbols found on MSDL!'
}
else {
Write-Host 'No symbols found on SymWeb!'
}
2019-06-13 12:10:50 +00:00
}
}
}
2021-05-01 13:00:30 +00:00
}
2019-06-13 12:10:50 +00:00
2020-05-30 12:44:29 +00:00
if ( $using : Clean ) {
2020-05-12 12:48:04 +00:00
Remove-Item $ExtractPath -Recurse -Force
}
2019-06-13 12:10:50 +00:00
Pop-Location
2020-07-08 13:11:11 +00:00
return [ pscustomobject ] @ {
2021-05-01 13:00:30 +00:00
result = $MissingSymbols
packagePath = $PackagePath
}
2020-07-08 13:11:11 +00:00
}
function CheckJobResult (
2021-05-01 13:00:30 +00:00
$result ,
$packagePath ,
[ ref ] $DupedSymbols ,
[ ref ] $TotalFailures ) {
2020-10-31 12:53:54 +00:00
if ( $result -eq $ERROR_BADEXTRACT ) {
2020-07-08 13:11:11 +00:00
Write-PipelineTelemetryError -Category 'CheckSymbols' -Message " $packagePath has duplicated symbol files "
$DupedSymbols . Value + +
}
2020-10-31 12:53:54 +00:00
elseif ( $result -eq $ERROR_FILEDOESNOTEXIST ) {
Write-PipelineTelemetryError -Category 'CheckSymbols' -Message " $packagePath does not exist "
$TotalFailures . Value + +
}
elseif ( $result -gt '0' ) {
2020-09-16 18:30:55 +00:00
Write-PipelineTelemetryError -Category 'CheckSymbols' -Message " Missing symbols for $result modules in the package $packagePath "
2020-07-08 13:11:11 +00:00
$TotalFailures . Value + +
}
2020-10-31 12:53:54 +00:00
else {
Write-Host " All symbols verified for package $packagePath "
}
2019-06-13 12:10:50 +00:00
}
function CheckSymbolsAvailable {
if ( Test-Path $ExtractPath ) {
Remove-Item $ExtractPath -Force -Recurse -ErrorAction SilentlyContinue
}
2021-05-14 13:08:05 +00:00
$TotalPackages = 0
2020-04-24 17:34:25 +00:00
$TotalFailures = 0
2020-07-08 13:11:11 +00:00
$DupedSymbols = 0
2020-04-24 17:34:25 +00:00
2019-06-13 12:10:50 +00:00
Get-ChildItem " $InputPath \*.nupkg " |
ForEach-Object {
$FileName = $_ . Name
2020-05-30 12:44:29 +00:00
$FullName = $_ . FullName
2019-11-22 13:41:58 +00:00
2019-06-13 12:10:50 +00:00
# These packages from Arcade-Services include some native libraries that
# our current symbol uploader can't handle. Below is a workaround until
# we get issue: https://github.com/dotnet/arcade/issues/2457 sorted.
2019-11-22 13:41:58 +00:00
if ( $FileName -Match 'Microsoft\.DotNet\.Darc\.' ) {
2019-06-13 12:10:50 +00:00
Write-Host " Ignoring Arcade-services file: $FileName "
Write-Host
return
}
2019-11-22 13:41:58 +00:00
elseif ( $FileName -Match 'Microsoft\.DotNet\.Maestro\.Tasks\.' ) {
2019-06-13 12:10:50 +00:00
Write-Host " Ignoring Arcade-services file: $FileName "
Write-Host
return
}
2019-11-22 13:41:58 +00:00
2021-05-14 13:08:05 +00:00
$TotalPackages + +
2021-05-01 13:00:30 +00:00
Start-Job -ScriptBlock $CountMissingSymbols -ArgumentList @ ( $FullName , $WindowsPdbVerificationParam ) | Out-Null
2020-05-30 12:44:29 +00:00
$NumJobs = @ ( Get-Job -State 'Running' ) . Count
while ( $NumJobs -ge $MaxParallelJobs ) {
Write-Host " There are $NumJobs validation jobs running right now. Waiting $SecondsBetweenLoadChecks seconds to check again. "
sleep $SecondsBetweenLoadChecks
$NumJobs = @ ( Get-Job -State 'Running' ) . Count
2019-06-13 12:10:50 +00:00
}
2020-05-30 12:44:29 +00:00
foreach ( $Job in @ ( Get-Job -State 'Completed' ) ) {
2020-06-11 17:47:07 +00:00
$jobResult = Wait-Job -Id $Job . Id | Receive-Job
2020-07-08 13:11:11 +00:00
CheckJobResult $jobResult . result $jobResult . packagePath ( [ ref ] $DupedSymbols ) ( [ ref ] $TotalFailures )
2020-06-11 17:47:07 +00:00
Remove-Job -Id $Job . Id
2020-05-30 12:44:29 +00:00
}
2019-06-13 12:10:50 +00:00
Write-Host
}
2020-04-24 17:34:25 +00:00
2020-05-30 12:44:29 +00:00
foreach ( $Job in @ ( Get-Job ) ) {
$jobResult = Wait-Job -Id $Job . Id | Receive-Job
2020-07-08 13:11:11 +00:00
CheckJobResult $jobResult . result $jobResult . packagePath ( [ ref ] $DupedSymbols ) ( [ ref ] $TotalFailures )
}
2020-05-30 12:44:29 +00:00
2020-07-08 13:11:11 +00:00
if ( $TotalFailures -gt 0 -or $DupedSymbols -gt 0 ) {
if ( $TotalFailures -gt 0 ) {
2021-05-14 13:08:05 +00:00
Write-PipelineTelemetryError -Category 'CheckSymbols' -Message " Symbols missing for $TotalFailures / $TotalPackages packages "
2020-05-30 12:44:29 +00:00
}
2020-07-08 13:11:11 +00:00
if ( $DupedSymbols -gt 0 ) {
2021-05-14 13:08:05 +00:00
Write-PipelineTelemetryError -Category 'CheckSymbols' -Message " $DupedSymbols / $TotalPackages packages had duplicated symbol files and could not be extracted "
2020-07-08 13:11:11 +00:00
}
2020-04-24 17:34:25 +00:00
ExitWithExitCode 1
}
2020-05-30 12:44:29 +00:00
else {
Write-Host " All symbols validated! "
}
2019-06-13 12:10:50 +00:00
}
2019-08-29 12:43:02 +00:00
function InstallDotnetSymbol {
2019-11-22 13:41:58 +00:00
$dotnetSymbolPackageName = 'dotnet-symbol'
2019-07-27 12:35:24 +00:00
$dotnetRoot = InitializeDotNetCli -install: $true
$dotnet = " $dotnetRoot \dotnet.exe "
$toolList = & " $dotnet " tool list - -global
2019-08-29 12:43:02 +00:00
if ( ( $toolList -like " * $dotnetSymbolPackageName * " ) -and ( $toolList -like " * $dotnetSymbolVersion * " ) ) {
Write-Host " dotnet-symbol version $dotnetSymbolVersion is already installed. "
2019-07-27 12:35:24 +00:00
}
else {
2019-08-29 12:43:02 +00:00
Write-Host " Installing dotnet-symbol version $dotnetSymbolVersion ... "
2019-11-22 13:41:58 +00:00
Write-Host 'You may need to restart your command window if this is the first dotnet tool you have installed.'
2019-08-29 12:43:02 +00:00
& " $dotnet " tool install $dotnetSymbolPackageName - -version $dotnetSymbolVersion - -verbosity " minimal " - -global
2019-06-13 12:10:50 +00:00
}
}
try {
2019-11-22 13:41:58 +00:00
. $PSScriptRoot \ post-build -utils . ps1
2019-08-29 12:43:02 +00:00
InstallDotnetSymbol
2019-06-13 12:10:50 +00:00
2020-05-30 12:44:29 +00:00
foreach ( $Job in @ ( Get-Job ) ) {
Remove-Job -Id $Job . Id
}
2019-06-13 12:10:50 +00:00
CheckSymbolsAvailable
}
catch {
Write-Host $_ . ScriptStackTrace
2019-11-22 13:41:58 +00:00
Write-PipelineTelemetryError -Category 'CheckSymbols' -Message $_
2019-06-13 12:10:50 +00:00
ExitWithExitCode 1
}