msbuild
339 строк · 11.4 Кб
1param(
2[Parameter(Mandatory = $true)][string] $InputPath, # Full path to directory where NuGet packages to be checked are stored
3[Parameter(Mandatory = $true)][string] $ExtractPath, # Full path to directory where the packages will be extracted during validation
4[Parameter(Mandatory = $true)][string] $DotnetSymbolVersion, # Version of dotnet symbol to use
5[Parameter(Mandatory = $false)][switch] $CheckForWindowsPdbs, # If we should check for the existence of windows pdbs in addition to portable PDBs
6[Parameter(Mandatory = $false)][switch] $ContinueOnError, # If we should keep checking symbols after an error
7[Parameter(Mandatory = $false)][switch] $Clean, # Clean extracted symbols directory after checking symbols
8[Parameter(Mandatory = $false)][string] $SymbolExclusionFile # Exclude the symbols in the file from publishing to symbol server
9)
10
11. $PSScriptRoot\..\tools.ps1
12# Maximum number of jobs to run in parallel
13$MaxParallelJobs = 16
14
15# Max number of retries
16$MaxRetry = 5
17
18# Wait time between check for system load
19$SecondsBetweenLoadChecks = 10
20
21# Set error codes
22Set-Variable -Name "ERROR_BADEXTRACT" -Option Constant -Value -1
23Set-Variable -Name "ERROR_FILEDOESNOTEXIST" -Option Constant -Value -2
24
25$WindowsPdbVerificationParam = ""
26if ($CheckForWindowsPdbs) {
27$WindowsPdbVerificationParam = "--windows-pdbs"
28}
29
30$ExclusionSet = New-Object System.Collections.Generic.HashSet[string];
31
32if (!$InputPath -or !(Test-Path $InputPath)){
33Write-Host "No symbols to validate."
34ExitWithExitCode 0
35}
36
37#Check if the path exists
38if ($SymbolExclusionFile -and (Test-Path $SymbolExclusionFile)){
39[string[]]$Exclusions = Get-Content "$SymbolExclusionFile"
40$Exclusions | foreach { if($_ -and $_.Trim()){$ExclusionSet.Add($_)} }
41}
42else{
43Write-Host "Symbol Exclusion file does not exists. No symbols to exclude."
44}
45
46$CountMissingSymbols = {
47param(
48[string] $PackagePath, # Path to a NuGet package
49[string] $WindowsPdbVerificationParam # If we should check for the existence of windows pdbs in addition to portable PDBs
50)
51
52Add-Type -AssemblyName System.IO.Compression.FileSystem
53
54Write-Host "Validating $PackagePath "
55
56# Ensure input file exist
57if (!(Test-Path $PackagePath)) {
58Write-PipelineTaskError "Input file does not exist: $PackagePath"
59return [pscustomobject]@{
60result = $using:ERROR_FILEDOESNOTEXIST
61packagePath = $PackagePath
62}
63}
64
65# Extensions for which we'll look for symbols
66$RelevantExtensions = @('.dll', '.exe', '.so', '.dylib')
67
68# How many files are missing symbol information
69$MissingSymbols = 0
70
71$PackageId = [System.IO.Path]::GetFileNameWithoutExtension($PackagePath)
72$PackageGuid = New-Guid
73$ExtractPath = Join-Path -Path $using:ExtractPath -ChildPath $PackageGuid
74$SymbolsPath = Join-Path -Path $ExtractPath -ChildPath 'Symbols'
75
76try {
77[System.IO.Compression.ZipFile]::ExtractToDirectory($PackagePath, $ExtractPath)
78}
79catch {
80Write-Host "Something went wrong extracting $PackagePath"
81Write-Host $_
82return [pscustomobject]@{
83result = $using:ERROR_BADEXTRACT
84packagePath = $PackagePath
85}
86}
87
88Get-ChildItem -Recurse $ExtractPath |
89Where-Object { $RelevantExtensions -contains $_.Extension } |
90ForEach-Object {
91$FileName = $_.FullName
92if ($FileName -Match '\\ref\\') {
93Write-Host "`t Ignoring reference assembly file " $FileName
94return
95}
96
97$FirstMatchingSymbolDescriptionOrDefault = {
98param(
99[string] $FullPath, # Full path to the module that has to be checked
100[string] $TargetServerParam, # Parameter to pass to `Symbol Tool` indicating the server to lookup for symbols
101[string] $WindowsPdbVerificationParam, # Parameter to pass to potential check for windows-pdbs.
102[string] $SymbolsPath
103)
104
105$FileName = [System.IO.Path]::GetFileName($FullPath)
106$Extension = [System.IO.Path]::GetExtension($FullPath)
107
108# Those below are potential symbol files that the `dotnet symbol` might
109# return. Which one will be returned depend on the type of file we are
110# checking and which type of file was uploaded.
111
112# The file itself is returned
113$SymbolPath = $SymbolsPath + '\' + $FileName
114
115# PDB file for the module
116$PdbPath = $SymbolPath.Replace($Extension, '.pdb')
117
118# PDB file for R2R module (created by crossgen)
119$NGenPdb = $SymbolPath.Replace($Extension, '.ni.pdb')
120
121# DBG file for a .so library
122$SODbg = $SymbolPath.Replace($Extension, '.so.dbg')
123
124# DWARF file for a .dylib
125$DylibDwarf = $SymbolPath.Replace($Extension, '.dylib.dwarf')
126
127$dotnetSymbolExe = "$env:USERPROFILE\.dotnet\tools"
128$dotnetSymbolExe = Resolve-Path "$dotnetSymbolExe\dotnet-symbol.exe"
129
130$totalRetries = 0
131
132while ($totalRetries -lt $using:MaxRetry) {
133
134# Save the output and get diagnostic output
135$output = & $dotnetSymbolExe --symbols --modules $WindowsPdbVerificationParam $TargetServerParam $FullPath -o $SymbolsPath --diagnostics | Out-String
136
137if ((Test-Path $PdbPath) -and (Test-path $SymbolPath)) {
138return 'Module and PDB for Module'
139}
140elseif ((Test-Path $NGenPdb) -and (Test-Path $PdbPath) -and (Test-Path $SymbolPath)) {
141return 'Dll, PDB and NGen PDB'
142}
143elseif ((Test-Path $SODbg) -and (Test-Path $SymbolPath)) {
144return 'So and DBG for SO'
145}
146elseif ((Test-Path $DylibDwarf) -and (Test-Path $SymbolPath)) {
147return 'Dylib and Dwarf for Dylib'
148}
149elseif (Test-Path $SymbolPath) {
150return 'Module'
151}
152else
153{
154$totalRetries++
155}
156}
157
158return $null
159}
160
161$FileRelativePath = $FileName.Replace("$ExtractPath\", "")
162if (($($using:ExclusionSet) -ne $null) -and ($($using:ExclusionSet).Contains($FileRelativePath) -or ($($using:ExclusionSet).Contains($FileRelativePath.Replace("\", "/"))))){
163Write-Host "Skipping $FileName from symbol validation"
164}
165
166else {
167$FileGuid = New-Guid
168$ExpandedSymbolsPath = Join-Path -Path $SymbolsPath -ChildPath $FileGuid
169
170$SymbolsOnMSDL = & $FirstMatchingSymbolDescriptionOrDefault `
171-FullPath $FileName `
172-TargetServerParam '--microsoft-symbol-server' `
173-SymbolsPath "$ExpandedSymbolsPath-msdl" `
174-WindowsPdbVerificationParam $WindowsPdbVerificationParam
175$SymbolsOnSymWeb = & $FirstMatchingSymbolDescriptionOrDefault `
176-FullPath $FileName `
177-TargetServerParam '--internal-server' `
178-SymbolsPath "$ExpandedSymbolsPath-symweb" `
179-WindowsPdbVerificationParam $WindowsPdbVerificationParam
180
181Write-Host -NoNewLine "`t Checking file " $FileName "... "
182
183if ($SymbolsOnMSDL -ne $null -and $SymbolsOnSymWeb -ne $null) {
184Write-Host "Symbols found on MSDL ($SymbolsOnMSDL) and SymWeb ($SymbolsOnSymWeb)"
185}
186else {
187$MissingSymbols++
188
189if ($SymbolsOnMSDL -eq $null -and $SymbolsOnSymWeb -eq $null) {
190Write-Host 'No symbols found on MSDL or SymWeb!'
191}
192else {
193if ($SymbolsOnMSDL -eq $null) {
194Write-Host 'No symbols found on MSDL!'
195}
196else {
197Write-Host 'No symbols found on SymWeb!'
198}
199}
200}
201}
202}
203
204if ($using:Clean) {
205Remove-Item $ExtractPath -Recurse -Force
206}
207
208Pop-Location
209
210return [pscustomobject]@{
211result = $MissingSymbols
212packagePath = $PackagePath
213}
214}
215
216function CheckJobResult(
217$result,
218$packagePath,
219[ref]$DupedSymbols,
220[ref]$TotalFailures) {
221if ($result -eq $ERROR_BADEXTRACT) {
222Write-PipelineTelemetryError -Category 'CheckSymbols' -Message "$packagePath has duplicated symbol files"
223$DupedSymbols.Value++
224}
225elseif ($result -eq $ERROR_FILEDOESNOTEXIST) {
226Write-PipelineTelemetryError -Category 'CheckSymbols' -Message "$packagePath does not exist"
227$TotalFailures.Value++
228}
229elseif ($result -gt '0') {
230Write-PipelineTelemetryError -Category 'CheckSymbols' -Message "Missing symbols for $result modules in the package $packagePath"
231$TotalFailures.Value++
232}
233else {
234Write-Host "All symbols verified for package $packagePath"
235}
236}
237
238function CheckSymbolsAvailable {
239if (Test-Path $ExtractPath) {
240Remove-Item $ExtractPath -Force -Recurse -ErrorAction SilentlyContinue
241}
242
243$TotalPackages = 0
244$TotalFailures = 0
245$DupedSymbols = 0
246
247Get-ChildItem "$InputPath\*.nupkg" |
248ForEach-Object {
249$FileName = $_.Name
250$FullName = $_.FullName
251
252# These packages from Arcade-Services include some native libraries that
253# our current symbol uploader can't handle. Below is a workaround until
254# we get issue: https://github.com/dotnet/arcade/issues/2457 sorted.
255if ($FileName -Match 'Microsoft\.DotNet\.Darc\.') {
256Write-Host "Ignoring Arcade-services file: $FileName"
257Write-Host
258return
259}
260elseif ($FileName -Match 'Microsoft\.DotNet\.Maestro\.Tasks\.') {
261Write-Host "Ignoring Arcade-services file: $FileName"
262Write-Host
263return
264}
265
266$TotalPackages++
267
268Start-Job -ScriptBlock $CountMissingSymbols -ArgumentList @($FullName,$WindowsPdbVerificationParam) | Out-Null
269
270$NumJobs = @(Get-Job -State 'Running').Count
271
272while ($NumJobs -ge $MaxParallelJobs) {
273Write-Host "There are $NumJobs validation jobs running right now. Waiting $SecondsBetweenLoadChecks seconds to check again."
274sleep $SecondsBetweenLoadChecks
275$NumJobs = @(Get-Job -State 'Running').Count
276}
277
278foreach ($Job in @(Get-Job -State 'Completed')) {
279$jobResult = Wait-Job -Id $Job.Id | Receive-Job
280CheckJobResult $jobResult.result $jobResult.packagePath ([ref]$DupedSymbols) ([ref]$TotalFailures)
281Remove-Job -Id $Job.Id
282}
283Write-Host
284}
285
286foreach ($Job in @(Get-Job)) {
287$jobResult = Wait-Job -Id $Job.Id | Receive-Job
288CheckJobResult $jobResult.result $jobResult.packagePath ([ref]$DupedSymbols) ([ref]$TotalFailures)
289}
290
291if ($TotalFailures -gt 0 -or $DupedSymbols -gt 0) {
292if ($TotalFailures -gt 0) {
293Write-PipelineTelemetryError -Category 'CheckSymbols' -Message "Symbols missing for $TotalFailures/$TotalPackages packages"
294}
295
296if ($DupedSymbols -gt 0) {
297Write-PipelineTelemetryError -Category 'CheckSymbols' -Message "$DupedSymbols/$TotalPackages packages had duplicated symbol files and could not be extracted"
298}
299
300ExitWithExitCode 1
301}
302else {
303Write-Host "All symbols validated!"
304}
305}
306
307function InstallDotnetSymbol {
308$dotnetSymbolPackageName = 'dotnet-symbol'
309
310$dotnetRoot = InitializeDotNetCli -install:$true
311$dotnet = "$dotnetRoot\dotnet.exe"
312$toolList = & "$dotnet" tool list --global
313
314if (($toolList -like "*$dotnetSymbolPackageName*") -and ($toolList -like "*$dotnetSymbolVersion*")) {
315Write-Host "dotnet-symbol version $dotnetSymbolVersion is already installed."
316}
317else {
318Write-Host "Installing dotnet-symbol version $dotnetSymbolVersion..."
319Write-Host 'You may need to restart your command window if this is the first dotnet tool you have installed.'
320& "$dotnet" tool install $dotnetSymbolPackageName --version $dotnetSymbolVersion --verbosity "minimal" --global
321}
322}
323
324try {
325. $PSScriptRoot\post-build-utils.ps1
326
327InstallDotnetSymbol
328
329foreach ($Job in @(Get-Job)) {
330Remove-Job -Id $Job.Id
331}
332
333CheckSymbolsAvailable
334}
335catch {
336Write-Host $_.ScriptStackTrace
337Write-PipelineTelemetryError -Category 'CheckSymbols' -Message $_
338ExitWithExitCode 1
339}
340