2e9cccbdef
v2-ci / unit-tests (push) Has been cancelled
Copies the reviewed baseline-canonical SDK1 port and replaces its 1..4-cycle smoke entry with the late-slope
loop (same cycle body, every-fourth-cycle active restart, 30/25/120 s bounds, 7200 s phase, cycle-3 cold
baseline with unchanged +64 MiB managed / +128 MiB RSS / +32 descriptor caps, latched first rejection,
bounded shutdown checks). Run against the immutable efb9197a publication (#807 fix included), never rebuilt.
The held-reference closed-session list assertion is recorded instead (SDK1 Close keeps lists until Dispose,
per OWNERSHIP.md) and the managed-monotonic guard is latched, so the comparator yields numbers for the whole
phase. acceptance=false by construction. Windows descriptor observer shared byte-for-byte with the SDK2 test.
Claude-Session: https://claude.ai/code/session_01GT67ACmpMR7b9LGRPLijMi
81 lines
4.0 KiB
Python
81 lines
4.0 KiB
Python
#!/usr/bin/env python3
|
|
"""Publish the SDK 1.5.378.182 comparator (lmxopcua#799) self-contained against an immutable SDK1 publication.
|
|
|
|
Like ../baseline-canonical/build.py: never rebuilds product code. References come from the given RID-specific
|
|
framework-dependent publications of the SDK1 host and client (host wins duplicate names; native DLLs are skipped as
|
|
references and copied beside the output), plus Shouldly and its assertion dependencies. The comparator is published
|
|
self-contained for the requested RID on the same pinned runtime as the SDK2 run (10.0.14).
|
|
"""
|
|
import argparse
|
|
import hashlib
|
|
import json
|
|
from pathlib import Path
|
|
import shutil
|
|
import subprocess
|
|
import xml.etree.ElementTree as ET
|
|
|
|
RUNTIME = "10.0.14"
|
|
SOURCES = ["Program.cs", "BaselineRig.cs", "BaselineMetrics.cs", "CanonicalOperations.cs", "WindowsDescriptors.cs"]
|
|
|
|
|
|
def managed(path):
|
|
data = path.read_bytes()
|
|
if data[:2] != b"MZ":
|
|
return False
|
|
pe = int.from_bytes(data[0x3C:0x40], "little")
|
|
magic = int.from_bytes(data[pe + 24:pe + 26], "little")
|
|
directories = pe + 24 + (112 if magic == 0x20B else 96)
|
|
clr = directories + 14 * 8
|
|
return int.from_bytes(data[clr:clr + 4], "little") != 0
|
|
|
|
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument("--host", type=Path, required=True)
|
|
parser.add_argument("--client", type=Path, required=True)
|
|
parser.add_argument("--assertions", type=Path, required=True)
|
|
parser.add_argument("--rid", default="win-x64")
|
|
parser.add_argument("--output", type=Path, required=True)
|
|
args = parser.parse_args()
|
|
root = args.output.resolve()
|
|
root.mkdir(parents=True, exist_ok=False)
|
|
source = Path(__file__).resolve().parent
|
|
references, natives = {}, {}
|
|
for directory in [args.host, args.client]:
|
|
for dll in sorted(directory.resolve(strict=True).glob("*.dll")):
|
|
(references if managed(dll) else natives).setdefault(dll.name, dll)
|
|
for name in ["Shouldly.dll", "DiffEngine.dll", "EmptyFiles.dll", "EmptyFiles.Xml.dll"]:
|
|
path = args.assertions.resolve(strict=True) / name
|
|
if path.exists():
|
|
references.setdefault(name, path)
|
|
project = ET.Element("Project", Sdk="Microsoft.NET.Sdk")
|
|
properties = ET.SubElement(project, "PropertyGroup")
|
|
for key, value in {"OutputType": "Exe", "TargetFramework": "net10.0", "Nullable": "enable", "ImplicitUsings": "enable",
|
|
"TreatWarningsAsErrors": "true", "EnableDefaultCompileItems": "false", "AssemblyName": "sdk1-comparator",
|
|
"RuntimeIdentifier": args.rid, "SelfContained": "true", "RuntimeFrameworkVersion": RUNTIME,
|
|
"NoWarn": "CA1416"}.items():
|
|
ET.SubElement(properties, key).text = value
|
|
items = ET.SubElement(project, "ItemGroup")
|
|
ET.SubElement(items, "FrameworkReference", Include="Microsoft.AspNetCore.App")
|
|
for name, path in references.items():
|
|
reference = ET.SubElement(items, "Reference", Include=path.stem)
|
|
ET.SubElement(reference, "HintPath").text = str(path)
|
|
for name in SOURCES:
|
|
ET.SubElement(items, "Compile", Include=str(source / name))
|
|
path = root / "comparator.csproj"
|
|
ET.indent(project)
|
|
ET.ElementTree(project).write(path, encoding="unicode")
|
|
publish = root / "publish"
|
|
with (root / "build.log").open("w") as log:
|
|
result = subprocess.run(["dotnet", "publish", str(path), "-c", "Release", "-o", str(publish), "-m:2", "--nologo"],
|
|
stdout=log, stderr=subprocess.STDOUT)
|
|
if result.returncode == 0:
|
|
for name, native in natives.items():
|
|
if not (publish / name).exists():
|
|
shutil.copy2(native, publish / name)
|
|
manifest = {"diagnosticOnly": True, "productRebuilt": False, "rid": args.rid, "runtime": RUNTIME,
|
|
"references": {n: hashlib.sha256(p.read_bytes()).hexdigest() for n, p in references.items()},
|
|
"natives": {n: hashlib.sha256(p.read_bytes()).hexdigest() for n, p in natives.items()},
|
|
"sources": {n: hashlib.sha256((source / n).read_bytes()).hexdigest() for n in SOURCES}}
|
|
(root / "provenance.json").write_text(json.dumps(manifest, indent=2) + "\n")
|
|
raise SystemExit(result.returncode)
|