diff --git a/README.md b/README.md index 31d614f..96376bc 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,9 @@ Across open-source XSD generator trackers, certain widely adopted industry schem - **Extreme Scale & Facet Density**: Massive industrial standards with thousands of types (e.g. NeTEx with 2,400+ types, HL7 CDA with 1,450+ types, UBL with 1,000+ types). - **Multi-Megabyte Monolithic Schemas**: Massive schema files such as NATO UCI (8.0 MB single file) and HL7 FHIR (2.6 MB). -This corpus ensures PolyXML reliably compiles and generates clean, idiomatic code across all **7 target languages** (Rust, TypeScript, Python, Go, C#, Java, and C++) without crashing or producing conflicting type declarations. +The default validation run checks schema parsing and module ownership. A separate +UCI check generates code and compiles five destination languages; Rust and C++ +large-module compilation are tracked in the linked PolyXML issues. --- @@ -87,6 +89,7 @@ polyxml-schema-corpus/ │ └── w3c-xmlschema/ # Normative W3C XML Schema definition ├── polyxml.toml # Multi-module workspace configuration (45 modules) ├── runner.py # Automated validation and benchmark harness +├── scripts/check_uci_codegen.py # Bounded UCI generated-code compile check ├── LICENSE # MIT License ├── NOTICE # Standards attribution & third-party copyright notices └── README.md @@ -96,6 +99,20 @@ polyxml-schema-corpus/ ## ⚡ Usage +### UCI generated-code compilation + +With Python 3.12+, Go, TypeScript (`tsc`), Java, and .NET 8 installed, run: + +```bash +python3 scripts/check_uci_codegen.py --bin ../PolyXML/target/debug/polyxml +``` + +The script generates the four-schema UCI module into a temporary directory, +compiles one language at a time, and uses PolyXML's memory-cap wrapper when it +is available. Select a target with `--lang python` (repeat the option for +several targets). Full Rust and C++ UCI compilation is tracked in +[PolyXML issue #92](https://github.com/polyxml/PolyXML/issues/92). + ### 1. Run the Validation & Benchmark Suite Ensure `polyxml` is installed or compiled in `../PolyXML/target/release/polyxml`: diff --git a/scripts/check_uci_codegen.py b/scripts/check_uci_codegen.py new file mode 100644 index 0000000..128c8ec --- /dev/null +++ b/scripts/check_uci_codegen.py @@ -0,0 +1,106 @@ +#!/usr/bin/env python3 +"""Generate and compile the UCI module one target at a time. + +Rust and C++ are intentionally excluded until PolyXML can split their large +UCI compilation units. This check keeps all output in a temporary directory. +""" + +from __future__ import annotations + +import argparse +import os +import subprocess +import tempfile +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[1] +SCHEMAS = ( + "UCI_Versioning_v2_5_0.xsd", + "UCI_SecurityMarkings_v2_5_0.xsd", + "uci_entity_core.xsd", + "UCI_MessageDefinitions_v2_5_0.xsd", +) +LANGUAGES = ("python", "go", "typescript", "java", "csharp") +OUTPUTS = {"python": "python", "go": "go", "typescript": "ts", "java": "java", "csharp": "csharp"} + + +def run(command: list[str], *, cwd: Path, timeout: int, memcap: Path) -> None: + command = [str(memcap), *command] + print("$", " ".join(command), flush=True) + env = os.environ.copy() + env.setdefault("POLYXML_MEMCAP_PCT", "35") + env.setdefault("GOMAXPROCS", "2") + result = subprocess.run(command, cwd=cwd, env=env, capture_output=True, text=True, timeout=timeout) + if result.returncode: + raise RuntimeError( + f"command exited {result.returncode}: {' '.join(command)}\n" + + "\n".join((result.stdout + result.stderr).splitlines()[:25]) + ) + for line in (result.stdout + result.stderr).splitlines()[-4:]: + print(line) + + +def manifest(lang: str) -> str: + schemas = ",\n ".join(f'"schemas/defense-uci/{name}"' for name in SCHEMAS) + package = 'package = "org.polyxml.uci"\n' if lang == "java" else "" + return ( + '[workspace]\nname = "uci-compile-check"\noutput_base_dir = "generated"\n' + 'go_module = "polyxml/uci-check"\n\n' + f'[modules.defense_uci]\nschemas = [\n {schemas},\n]\n\n' + f'[[generate]]\ntarget = "{lang}"\noutput = "{OUTPUTS[lang]}"\n{package}' + ) + + +def main() -> None: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--bin", type=Path, required=True, help="PolyXML CLI executable") + parser.add_argument("--lang", choices=LANGUAGES, action="append", help="Target to check; repeat to select several") + parser.add_argument("--timeout", type=int, default=120, help="Seconds allowed for each command") + args = parser.parse_args() + compiler = args.bin.resolve(strict=True) + selected = args.lang or LANGUAGES + memcap = ROOT.parent / "PolyXML" / "scripts" / "memcap.sh" + if not memcap.is_file(): + parser.error(f"memory cap wrapper not found: {memcap}") + + with tempfile.TemporaryDirectory(prefix="polyxml-uci-check-") as directory: + work = Path(directory) + (work / "schemas").symlink_to(ROOT / "schemas", target_is_directory=True) + for lang in selected: + print(f"\n== {lang} ==", flush=True) + (work / "polyxml.toml").write_text(manifest(lang)) + run([str(compiler), "build", "--config", str(work / "polyxml.toml")], cwd=work, timeout=args.timeout, memcap=memcap) + source = work / "generated" / OUTPUTS[lang] / "defense_uci" + if lang == "python": + command = ["python3", "-m", "py_compile", str(source / "defense_uci.py")] + cwd = work + elif lang == "go": + command = ["go", "test", "./..."] + cwd = work / "generated" / "go" + elif lang == "typescript": + command = ["tsc", "--noEmit", "--skipLibCheck", "--target", "ES2020", str(source / "defense_uci.ts")] + cwd = work + elif lang == "java": + files = sorted(str(path) for path in source.glob("*.java")) + assert files, "no generated Java files" + (work / "java-files.txt").write_text("\n".join(files) + "\n") + classes = work / "java-classes" + classes.mkdir(exist_ok=True) + command = ["javac", "-J-Xmx1500m", "-Xmaxerrs", "10", "-d", str(classes), f"@{work / 'java-files.txt'}"] + cwd = work + else: + project = work / "UciCheck.csproj" + project.write_text( + 'net8.0' + 'false' + '\n' + ) + command = ["dotnet", "build", str(project), "--ignore-failed-sources", "-p:UseSharedCompilation=false"] + cwd = work + run(command, cwd=cwd, timeout=args.timeout, memcap=memcap) + print("\nAll selected UCI targets compiled.") + + +if __name__ == "__main__": + main()