From fee55e89c55a377f38248aabd09654b17d85bb87 Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Tue, 29 Sep 2026 18:31:16 +0200 Subject: [PATCH] Check the CultMath release gate against the type-definition table scripts/check-cultmath-types.cs reads the assembly with System.Reflection.Metadata and reports which named types are not public top-level CultMath types. The old NUL-delimited string search passed an appended string or a member named like the type, and failed a real type whose name is tail-shared in the string heap. Prerelease and range versions now get a clear message instead of a raw System.Version error. Co-Authored-By: Claude Sonnet 5 --- scripts/build-unity-package.ps1 | 22 ++++++++++++++-------- scripts/check-cultmath-types.cs | 30 ++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 8 deletions(-) create mode 100644 scripts/check-cultmath-types.cs diff --git a/scripts/build-unity-package.ps1 b/scripts/build-unity-package.ps1 index 49bf391f..9de7c718 100644 --- a/scripts/build-unity-package.ps1 +++ b/scripts/build-unity-package.ps1 @@ -58,15 +58,20 @@ if ($LASTEXITCODE -ne 0) { # dependency, so a cultlib release must not run ahead of the CultMath package it names. # (1) The declared dependency version must not exceed the CultMath package's own version. # (2) The CultMath.dll that package tracks must define every type CultMathResolver formats. -# Type names come from the resolver source (its Shape entries) and are looked up as -# NUL-delimited names in the assembly's metadata string heap, which is a name check, not a -# signature check; Windows PowerShell cannot load a netstandard2.1 assembly to reflect on it. +# The names come from the resolver source (its Shape entries); scripts\check-cultmath-types.cs +# looks them up in the assembly's type-definition table, which Windows PowerShell cannot load +# the assembly to do. It checks type names, not signatures. $cultMathPackageRoot = Join-Path $repoRoot "packages\cultmath\unity\org.gamecult.cultmath" $cultMathDeclared = (Get-Content -LiteralPath (Join-Path $templateRoot "package.json") -Raw | ConvertFrom-Json).dependencies.'org.gamecult.cultmath' $cultMathAvailable = (Get-Content -LiteralPath (Join-Path $cultMathPackageRoot "package.json") -Raw | ConvertFrom-Json).version if ([string]::IsNullOrWhiteSpace($cultMathDeclared)) { throw "package.json must declare org.gamecult.cultmath, which supplies CultMath.dll." } +foreach ($declared in @(@("org.gamecult.cultlib's org.gamecult.cultmath dependency", $cultMathDeclared), @("the CultMath package version", $cultMathAvailable))) { + if ($declared[1] -notmatch '^\d+\.\d+\.\d+$') { + throw "$($declared[0]) is '$($declared[1])'. The release gate compares exact release versions (major.minor.patch); prerelease and range versions are not supported." + } +} if ([version]$cultMathDeclared -gt [version]$cultMathAvailable) { throw "Release order: org.gamecult.cultlib depends on org.gamecult.cultmath $cultMathDeclared, but the CultMath package is at $cultMathAvailable. Release CultMath first." } @@ -75,11 +80,12 @@ $resolverTypes = @([regex]::Matches($resolverSource, '\bShape<(?!T>)(\w+)>\(') | if ($resolverTypes.Count -eq 0) { throw "Found no CultMath types in CultMathResolver.cs; the release gate cannot check the CultMath package." } -$cultMathBytes = [IO.File]::ReadAllBytes((Join-Path $cultMathPackageRoot "Runtime\Plugins\CultMath.dll")) -$cultMathText = [Text.Encoding]::GetEncoding(28591).GetString($cultMathBytes) -$missingTypes = @($resolverTypes | Where-Object { -not $cultMathText.Contains("`0$_`0") }) -if ($missingTypes.Count -gt 0) { - throw "The CultMath.dll in org.gamecult.cultmath $cultMathAvailable lacks types CultMathResolver formats: $($missingTypes -join ', '). Release CultMath first." +$missingTypes = & dotnet run (Join-Path $PSScriptRoot "check-cultmath-types.cs") -- (Join-Path $cultMathPackageRoot "Runtime\Plugins\CultMath.dll") @resolverTypes +if ($LASTEXITCODE -eq 1) { + throw "The CultMath.dll in org.gamecult.cultmath $cultMathAvailable lacks types CultMathResolver formats: $missingTypes. Release CultMath first." +} +if ($LASTEXITCODE -ne 0) { + throw "scripts\check-cultmath-types.cs failed with exit code $LASTEXITCODE." } # The tracked DLLs and pdbs are committed beside their source, so none may name a commit or a worktree: # Source Link writes the commit SHA into the pdb, the informational version carries it too, and each DLL diff --git a/scripts/check-cultmath-types.cs b/scripts/check-cultmath-types.cs new file mode 100644 index 00000000..dab33397 --- /dev/null +++ b/scripts/check-cultmath-types.cs @@ -0,0 +1,30 @@ +// Release gate helper for build-unity-package.ps1: reads an assembly's type-definition table and +// reports which of the named CultMath types it does not define as a public top-level type. +// Usage: dotnet run scripts/check-cultmath-types.cs -- ... +// Exit 0 when every type is defined, 1 when any is missing, 2 on bad usage. +using System.Reflection; +using System.Reflection.Metadata; +using System.Reflection.PortableExecutable; + +if (args.Length < 2) +{ + Console.Error.WriteLine("usage: check-cultmath-types ..."); + return 2; +} + +using var stream = File.OpenRead(args[0]); +using var pe = new PEReader(stream); +var metadata = pe.GetMetadataReader(); +var defined = new HashSet(); +foreach (var handle in metadata.TypeDefinitions) +{ + var type = metadata.GetTypeDefinition(handle); + if (!type.GetDeclaringType().IsNil) continue; + if ((type.Attributes & TypeAttributes.VisibilityMask) != TypeAttributes.Public) continue; + if (metadata.GetString(type.Namespace) == "CultMath") defined.Add(metadata.GetString(type.Name)); +} + +var missing = args.Skip(1).Where(name => !defined.Contains(name)).ToArray(); +if (missing.Length == 0) return 0; +Console.WriteLine(string.Join(", ", missing)); +return 1;