大重构

This commit is contained in:
2026-07-28 17:55:29 +08:00
parent d78dff5c8f
commit bde75af7c1
20 changed files with 1093 additions and 0 deletions
+123
View File
@@ -0,0 +1,123 @@
$ErrorActionPreference = "Stop"
$inputDirs = @PSC_CPPCHECK_INPUT_DIRS@
$excludeDirs = @PSC_CPPCHECK_EXCLUDE_DIRS@
$enabledChecks = @PSC_CPPCHECK_ENABLE@
$suppressions = @PSC_CPPCHECK_SUPPRESSIONS@
$outputDir = "@PSC_CPPCHECK_OUTPUT_DIR@"
$compileCommands = "@PSC_CPPCHECK_COMPILE_COMMANDS@"
$configuredCppcheck = "@PSC_CPPCHECK_EXECUTABLE@"
$checkLevel = "@PSC_CPPCHECK_CHECK_LEVEL@"
$jobs = [int]"@PSC_CPPCHECK_JOBS@"
function Resolve-Tool([string]$Configured, [string]$Name, [string[]]$Fallbacks) {
if ($Configured -and (Test-Path -LiteralPath $Configured)) {
return (Resolve-Path -LiteralPath $Configured).Path
}
$command = Get-Command $Name -ErrorAction SilentlyContinue | Select-Object -First 1
if ($command) {
return $command.Source
}
foreach ($fallback in $Fallbacks) {
if (Test-Path -LiteralPath $fallback) {
return (Resolve-Path -LiteralPath $fallback).Path
}
}
throw "$Name executable was not found"
}
function Normalize-Path([string]$Path) {
return [IO.Path]::GetFullPath($Path).Replace("\", "/").TrimEnd("/")
}
function Test-InRoots([string]$Path, [string[]]$Roots) {
$normalized = Normalize-Path $Path
foreach ($root in $Roots) {
if ($normalized.StartsWith($root + "/", [StringComparison]::OrdinalIgnoreCase) -or $normalized.Equals($root, [StringComparison]::OrdinalIgnoreCase)) {
return $true
}
}
return $false
}
$cppcheck = Resolve-Tool $configuredCppcheck "cppcheck" @("C:\Program Files\Cppcheck\cppcheck.exe")
if (!(Test-Path -LiteralPath $compileCommands)) {
throw "compile_commands.json was not found at $compileCommands"
}
$roots = @($inputDirs | ForEach-Object { Normalize-Path $_ })
$excludes = @($excludeDirs | ForEach-Object { Normalize-Path $_ })
New-Item -ItemType Directory -Force -Path $outputDir | Out-Null
$database = Get-Content -LiteralPath $compileCommands -Raw | ConvertFrom-Json
$selected = @($database | Where-Object {
$file = Normalize-Path $_.file
(Test-InRoots $file $roots) -and !(Test-InRoots $file $excludes) -and [IO.Path]::GetExtension($file) -in @(".c", ".cc", ".cpp", ".cxx")
})
if ($selected.Count -eq 0) {
throw "No translation units matched the configured input directories"
}
$filteredDatabase = Join-Path $outputDir "compile_commands.json"
[IO.File]::WriteAllText($filteredDatabase, ($selected | ConvertTo-Json -Depth 8), [Text.UTF8Encoding]::new($false))
$xmlPath = Join-Path $outputDir "cppcheck.xml"
$cacheDir = Join-Path $outputDir "cache"
New-Item -ItemType Directory -Force -Path $cacheDir | Out-Null
$arguments = @(
"--project=$filteredDatabase",
"--enable=$($enabledChecks -join ',')",
"--check-level=$checkLevel",
"--std=c++20",
"--inconclusive",
"--inline-suppr",
"--output-format=xmlv3",
"--output-file=$xmlPath",
"--cppcheck-build-dir=$cacheDir"
"-j"
$jobs
)
foreach ($suppression in $suppressions) {
$arguments += "--suppress=$suppression"
}
& $cppcheck @arguments
$toolExitCode = $LASTEXITCODE
if (!(Test-Path -LiteralPath $xmlPath)) {
throw "Cppcheck did not produce $xmlPath"
}
[xml]$xml = Get-Content -LiteralPath $xmlPath -Raw
$records = [Collections.Generic.List[object]]::new()
foreach ($errorNode in $xml.results.errors.error) {
$locations = @($errorNode.location)
if ($locations.Count -eq 0) {
$records.Add([pscustomobject]@{
Severity = [string]$errorNode.severity
Id = [string]$errorNode.id
Message = [string]$errorNode.msg
File = ""
Line = 0
Column = 0
})
continue
}
foreach ($location in $locations) {
$locationFile = [string]$location.file
if ($locationFile -and (!(Test-InRoots $locationFile $roots) -or (Test-InRoots $locationFile $excludes))) {
continue
}
$records.Add([pscustomobject]@{
Severity = [string]$errorNode.severity
Id = [string]$errorNode.id
Message = [string]$errorNode.msg
File = $locationFile
Line = [int]$location.line
Column = [int]$location.column
})
}
}
$records | Export-Csv -LiteralPath (Join-Path $outputDir "cppcheck.csv") -NoTypeInformation -Encoding UTF8
$severity = @($records | Group-Object Severity | Sort-Object Count -Descending | ForEach-Object {
[pscustomobject]@{ severity = $_.Name; count = $_.Count }
})
$summary = [ordered]@{
tool = "cppcheck"
version = (& $cppcheck --version | Select-Object -First 1).Trim()
translation_units = $selected.Count
tool_exit_code = $toolExitCode
findings = $records.Count
severity = $severity
top = @($records | Select-Object -First 100)
}
[IO.File]::WriteAllText((Join-Path $outputDir "summary.json"), ($summary | ConvertTo-Json -Depth 8), [Text.UTF8Encoding]::new($false))
Write-Output "Cppcheck report: $outputDir"
+21
View File
@@ -0,0 +1,21 @@
param(
[string]$Version = "",
[switch]$Force
)
$ErrorActionPreference = "Stop"
$arguments = @("install", "--id", "Cppcheck.Cppcheck", "--exact", "--silent", "--accept-package-agreements", "--accept-source-agreements", "--disable-interactivity")
if ($Version) {
$arguments += @("--version", $Version)
}
if ($Force) {
$arguments += "--force"
}
& winget @arguments
if ($LASTEXITCODE -ne 0) {
exit $LASTEXITCODE
}
$cppcheck = "C:\Program Files\Cppcheck\cppcheck.exe"
if (!(Test-Path -LiteralPath $cppcheck)) {
throw "Cppcheck was not installed at $cppcheck"
}
& $cppcheck --version
+1
View File
@@ -0,0 +1 @@
include(${CMAKE_CURRENT_LIST_DIR}/main.cmake)
+57
View File
@@ -0,0 +1,57 @@
function(psc_cppcheck_ps_array out_value)
set(values)
foreach(item IN LISTS ARGN)
string(REPLACE "'" "''" escaped "${item}")
list(APPEND values "'${escaped}'")
endforeach()
string(REPLACE ";" "," value "${values}")
set(${out_value} "@(${value})" PARENT_SCOPE)
endfunction()
function(psc_add_cppcheck_target target_name)
set(options)
set(oneValueArgs OUTPUT_DIR COMPILE_COMMANDS CPPCHECK_EXECUTABLE CHECK_LEVEL JOBS)
set(multiValueArgs INPUT_DIRS EXCLUDE_DIRS ENABLE SUPPRESSIONS)
cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
if (NOT ARG_OUTPUT_DIR)
set(ARG_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/metrics/${target_name}/cppcheck")
endif ()
if (NOT ARG_COMPILE_COMMANDS)
set(ARG_COMPILE_COMMANDS "${CMAKE_BINARY_DIR}/compile_commands.json")
endif ()
if (NOT ARG_CPPCHECK_EXECUTABLE)
set(ARG_CPPCHECK_EXECUTABLE "C:/Program Files/Cppcheck/cppcheck.exe")
endif ()
if (NOT ARG_CHECK_LEVEL)
set(ARG_CHECK_LEVEL "normal")
endif ()
if (NOT ARG_JOBS)
set(ARG_JOBS 8)
endif ()
if (NOT ARG_INPUT_DIRS)
set(ARG_INPUT_DIRS "${CMAKE_SOURCE_DIR}")
endif ()
if (NOT ARG_ENABLE)
set(ARG_ENABLE warning style performance portability)
endif ()
if (NOT ARG_SUPPRESSIONS)
set(ARG_SUPPRESSIONS missingIncludeSystem unmatchedSuppression)
endif ()
psc_cppcheck_ps_array(PSC_CPPCHECK_INPUT_DIRS ${ARG_INPUT_DIRS})
psc_cppcheck_ps_array(PSC_CPPCHECK_EXCLUDE_DIRS ${ARG_EXCLUDE_DIRS})
psc_cppcheck_ps_array(PSC_CPPCHECK_ENABLE ${ARG_ENABLE})
psc_cppcheck_ps_array(PSC_CPPCHECK_SUPPRESSIONS ${ARG_SUPPRESSIONS})
set(PSC_CPPCHECK_OUTPUT_DIR "${ARG_OUTPUT_DIR}")
set(PSC_CPPCHECK_COMPILE_COMMANDS "${ARG_COMPILE_COMMANDS}")
set(PSC_CPPCHECK_EXECUTABLE "${ARG_CPPCHECK_EXECUTABLE}")
set(PSC_CPPCHECK_CHECK_LEVEL "${ARG_CHECK_LEVEL}")
set(PSC_CPPCHECK_JOBS "${ARG_JOBS}")
set(script_dir "${CMAKE_CURRENT_BINARY_DIR}/psc_metrics_scripts/${target_name}")
file(MAKE_DIRECTORY "${script_dir}")
set(script "${script_dir}/${target_name}_cppcheck.ps1")
configure_file("${CMAKE_CURRENT_FUNCTION_LIST_DIR}/Cppcheck.ps1.in" "${script}" @ONLY)
add_custom_target(${target_name}
COMMAND pwsh -NoProfile -ExecutionPolicy Bypass -File "${script}"
VERBATIM
)
set(${target_name}_OUTPUT_DIR "${ARG_OUTPUT_DIR}" PARENT_SCOPE)
endfunction()