2019-06-13 12:10:50 +00:00
param (
[ 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
2020-04-24 10:34:25 -07:00
[ Parameter ( Mandatory = $true ) ] [ string ] $DotnetSymbolVersion , # Version of dotnet symbol to use
2020-05-12 12:48:04 +00:00
[ Parameter ( Mandatory = $false ) ] [ switch ] $ContinueOnError , # If we should keep checking symbols after an error
[ Parameter ( Mandatory = $false ) ] [ switch ] $Clean # Clean extracted symbols directory after checking symbols
2019-06-13 12:10:50 +00:00
)
2020-05-30 12:44:29 +00:00
# Maximum number of jobs to run in parallel
$MaxParallelJobs = 6
2019-06-13 12:10:50 +00:00
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-05-30 12:44:29 +00:00
$CountMissingSymbols = {
2019-06-13 12:10:50 +00:00
param (
[ string ] $PackagePath # Path to a NuGet package
)
2020-05-30 12:44:29 +00:00
. $using : PSScriptRoot \ . . \ tools . ps1
Add-Type -AssemblyName System . IO . Compression . FileSystem
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-07-08 13:11:11 +00:00
return -2
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 ] @ {
result = -1
packagePath = $PackagePath
}
2020-05-12 12:48:04 +00:00
}
2019-06-13 12:10:50 +00:00
Get-ChildItem -Recurse $ExtractPath |
Where-Object { $RelevantExtensions -contains $_ . Extension } |
ForEach-Object {
2020-05-30 12:44:29 +00:00
$FileName = $_ . FullName
if ( $FileName -Match '\\ref\\' ) {
Write-Host " `t Ignoring reference assembly file " $FileName
2019-06-13 12:10:50 +00:00
return
}
2020-05-30 12:44:29 +00:00
$FirstMatchingSymbolDescriptionOrDefault = {
param (
[ 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 ] $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' )
$dotnetSymbolExe = " $env:USERPROFILE \.dotnet\tools "
$dotnetSymbolExe = Resolve-Path " $dotnetSymbolExe \dotnet-symbol.exe "
& $dotnetSymbolExe - -symbols - -modules - -windows -pdbs $TargetServerParam $FullPath -o $SymbolsPath | Out-Null
if ( Test-Path $PdbPath ) {
return 'PDB'
}
elseif ( Test-Path $NGenPdb ) {
return 'NGen PDB'
}
elseif ( Test-Path $SODbg ) {
return 'DBG for SO'
}
elseif ( Test-Path $DylibDwarf ) {
return 'Dwarf for Dylib'
}
elseif ( Test-Path $SymbolPath ) {
return 'Module'
}
else {
return $null
}
}
$SymbolsOnMSDL = & $FirstMatchingSymbolDescriptionOrDefault $FileName '--microsoft-symbol-server' $SymbolsPath
$SymbolsOnSymWeb = & $FirstMatchingSymbolDescriptionOrDefault $FileName '--internal-server' $SymbolsPath
2019-06-13 12:10:50 +00:00
2020-05-30 12:44:29 +00:00
Write-Host -NoNewLine " `t Checking file " $FileName " ... "
2019-06-13 12:10:50 +00:00
if ( $SymbolsOnMSDL -ne $null -and $SymbolsOnSymWeb -ne $null ) {
2019-11-22 13:41:58 +00:00
Write-Host " Symbols found on MSDL ( $SymbolsOnMSDL ) and SymWeb ( $SymbolsOnSymWeb ) "
2019-06-13 12:10:50 +00:00
}
else {
$MissingSymbols + +
if ( $SymbolsOnMSDL -eq $null -and $SymbolsOnSymWeb -eq $null ) {
2019-11-22 13:41:58 +00:00
Write-Host 'No symbols found on MSDL or SymWeb!'
2019-06-13 12:10:50 +00:00
}
else {
if ( $SymbolsOnMSDL -eq $null ) {
2019-11-22 13:41:58 +00:00
Write-Host 'No symbols found on MSDL!'
2019-06-13 12:10:50 +00:00
}
else {
2019-11-22 13:41:58 +00:00
Write-Host 'No symbols found on SymWeb!'
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 ] @ {
result = $MissingSymbols
packagePath = $PackagePath
}
}
function CheckJobResult (
$result ,
$packagePath ,
[ ref ] $DupedSymbols ,
[ ref ] $TotalFailures ) {
if ( $result -eq '-1' ) {
Write-PipelineTelemetryError -Category 'CheckSymbols' -Message " $packagePath has duplicated symbol files "
$DupedSymbols . Value + +
}
elseif ( $jobResult . result -ne '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 + +
}
2019-06-13 12:10:50 +00:00
}
function CheckSymbolsAvailable {
if ( Test-Path $ExtractPath ) {
Remove-Item $ExtractPath -Force -Recurse -ErrorAction SilentlyContinue
}
2020-04-24 10:34:25 -07:00
$TotalFailures = 0
2020-07-08 13:11:11 +00:00
$DupedSymbols = 0
2020-04-24 10:34:25 -07: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
2019-06-13 12:10:50 +00:00
Write-Host " Validating $FileName "
2019-11-22 13:41:58 +00:00
2020-05-30 12:44:29 +00:00
Start-Job -ScriptBlock $CountMissingSymbols -ArgumentList $FullName | Out-Null
$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 10:34:25 -07: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 ) {
Write-PipelineTelemetryError -Category 'CheckSymbols' -Message " Symbols missing for $TotalFailures packages "
2020-05-30 12:44:29 +00:00
}
2020-07-08 13:11:11 +00:00
if ( $DupedSymbols -gt 0 ) {
Write-PipelineTelemetryError -Category 'CheckSymbols' -Message " $DupedSymbols packages had duplicated symbol files "
}
2020-04-24 10:34:25 -07: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
}