standalone: VoxelForgeCore link-safe next to Unreal -- internals in vfcore::detail, symbol check
- The copied generator, mesher and Compat stand-ins compile inside vfcore::detail. - CheckCoreSymbols.ps1 (run by Build.ps1) fails on any unqualified Unreal-style symbol in the library: 16,255 on557f6aa, 0 now. A UE 5.7 CoreMinimal.h + VoxelForgeCore.h smoke compile passes. - Output identical: 11 hash rows at W1/W8, routes 5642/5642, look/passage/far audits pass, bad-shell control fails, six captures byte-identical. Cost, measured by me interleaved (8 workers, cold, 4 runs each, first run excluded): 0.963 s vs 0.937 s for557f6aa, ~27 ms in shape compilation; same math, so codegen/inlining. To be found. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
This commit is contained in:
1 parent
557f6aac30
commit
aeeedaa73d
11 files changed
+461
-87
No files matched your search
@@ -0,0 +1,82 @@
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(Mandatory=$true)][string]$LibraryPath,
|
||||
[string]$ResultPath
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$script:Root = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '..'))
|
||||
$LibraryPath = [IO.Path]::GetFullPath($LibraryPath)
|
||||
if (-not (Test-Path -LiteralPath $LibraryPath -PathType Leaf)) {
|
||||
throw "Core library not found: $LibraryPath"
|
||||
}
|
||||
|
||||
$VsBinRoots = @(Get-ChildItem -LiteralPath 'E:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC' -Directory -ErrorAction Stop |
|
||||
ForEach-Object { Join-Path $_.FullName 'bin\Hostx64\x64' } |
|
||||
Where-Object { Test-Path -LiteralPath (Join-Path $_ 'dumpbin.exe') })
|
||||
if ($VsBinRoots.Count -eq 0) { throw 'dumpbin.exe was not found in the Visual Studio toolchain.' }
|
||||
$Dumpbin = Join-Path $VsBinRoots[-1] 'dumpbin.exe'
|
||||
|
||||
# Build the forbidden set from the compatibility and copied Unreal-facing declarations so the
|
||||
# check covers the complete private type surface instead of a short hand-maintained example list.
|
||||
$ScanRoots = @(
|
||||
(Join-Path $script:Root 'Compat'),
|
||||
(Join-Path $script:Root 'Source'),
|
||||
(Join-Path $script:Root 'Core\src')
|
||||
)
|
||||
$TypeNames = [Collections.Generic.HashSet[string]]::new([StringComparer]::Ordinal)
|
||||
foreach ($ScanRoot in $ScanRoots) {
|
||||
foreach ($File in Get-ChildItem -LiteralPath $ScanRoot -Recurse -File -Include *.h,*.hpp,*.cpp) {
|
||||
foreach ($Match in [regex]::Matches([IO.File]::ReadAllText($File.FullName),
|
||||
'(?m)^\s*(?:class|struct|enum(?:\s+class)?|using)\s+([FTUE][A-Z][A-Za-z0-9_]*)\b')) {
|
||||
[void]$TypeNames.Add($Match.Groups[1].Value)
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($TypeNames.Count -eq 0) { throw 'Could not derive Unreal-style type names from the copied implementation.' }
|
||||
|
||||
$Dump = @(& $Dumpbin /SYMBOLS $LibraryPath 2>&1)
|
||||
if ($LASTEXITCODE -eq -1073741502) { throw 'dumpbin.exe exited with 0xC0000142; stopping as required.' }
|
||||
if ($LASTEXITCODE -ne 0) { throw "dumpbin.exe failed with exit code $LASTEXITCODE" }
|
||||
if ($Dump.Count -lt 1000) {
|
||||
throw "dumpbin returned only $($Dump.Count) lines; Core object symbols are not visible enough to verify."
|
||||
}
|
||||
|
||||
$Violations = [Collections.Generic.List[string]]::new()
|
||||
$Alternation = ($TypeNames | Sort-Object { $_.Length } -Descending | ForEach-Object { [regex]::Escape($_) }) -join '|'
|
||||
$GlobalTypePattern = [regex]::new('(?<![A-Za-z0-9_:])(?:' + $Alternation + ')(?![A-Za-z0-9_])',
|
||||
[Text.RegularExpressions.RegexOptions]::Compiled)
|
||||
$PrivateTypePattern = [regex]::new('vfcore::detail::(?:class\s+|struct\s+|enum\s+)?(?:' + $Alternation + ')(?![A-Za-z0-9_])',
|
||||
[Text.RegularExpressions.RegexOptions]::Compiled)
|
||||
$PrivateDestructorPattern = [regex]::new('~(?:' + $Alternation + ')(?![A-Za-z0-9_])',
|
||||
[Text.RegularExpressions.RegexOptions]::Compiled)
|
||||
foreach ($Line in $Dump) {
|
||||
if ($Line -notmatch '\|') { continue }
|
||||
$Symbol = ($Line -split '\|',2)[1]
|
||||
$DemangleOpen = $Symbol.IndexOf('(')
|
||||
if ($DemangleOpen -lt 0) { continue }
|
||||
$Demangled = $Symbol.Substring($DemangleOpen+1)
|
||||
# dumpbin repeats decorated-only linker helpers in parentheses when it has no readable
|
||||
# demangled form. Ignore those and remove known vfcore-qualified type spellings, including the
|
||||
# repeated destructor spelling after "::~", before looking for global type names.
|
||||
if ($Demangled -match '^\s*[?$]') { continue }
|
||||
$Demangled = $PrivateTypePattern.Replace($Demangled,'')
|
||||
$Demangled = $PrivateDestructorPattern.Replace($Demangled,'')
|
||||
# A type preceded by a scope separator belongs to a namespace. An unqualified instance in
|
||||
# the demangled dump is a global Unreal-style type and can ODR-collide with the engine.
|
||||
if ($GlobalTypePattern.IsMatch($Demangled)) {
|
||||
$Violations.Add($Line.Trim())
|
||||
}
|
||||
}
|
||||
|
||||
if ($Violations.Count -gt 0) {
|
||||
$ResultLines = @("CORE_LINK_SAFETY_FAIL global Unreal-style symbols: $($Violations.Count)") +
|
||||
@($Violations | Select-Object -First 40)
|
||||
if ($ResultPath) { [IO.File]::WriteAllLines([IO.Path]::GetFullPath($ResultPath),$ResultLines) }
|
||||
$ResultLines | ForEach-Object { Write-Output $_ }
|
||||
throw "Core archive contains $($Violations.Count) unqualified Unreal-style type symbols."
|
||||
}
|
||||
|
||||
$ResultLines = @("CORE_LINK_SAFETY_PASS checked $($Dump.Count) dumpbin lines against $($TypeNames.Count) copied Unreal-style type names.")
|
||||
if ($ResultPath) { [IO.File]::WriteAllLines([IO.Path]::GetFullPath($ResultPath),$ResultLines) }
|
||||
$ResultLines | ForEach-Object { Write-Output $_ }
|
||||
Reference in new issue
Block a user