From 918aadac7bb2e00be8d8ef77bfcfe7b3cb932196 Mon Sep 17 00:00:00 2001 From: Arch-AI-tech Date: Wed, 28 Oct 2026 16:06:32 +0100 Subject: [PATCH 01/54] ci: add Cargo license policy Co-Authored-By: Claude Sonnet 5.5 --- deny.toml | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 deny.toml diff --git a/deny.toml b/deny.toml new file mode 100644 index 0000000..548a82d --- /dev/null +++ b/deny.toml @@ -0,0 +1,12 @@ +[licenses] +confidence-threshold = 0.8 +allow = [ + "Apache-2.0", + "BSD-2-Clause", + "BSD-3-Clause", + "BSL-1.0", + "ISC", + "MIT", + "Unicode-3.0", + "Zlib", +] From a1875f872284b81ea96021108d4349341590de6b Mon Sep 17 00:00:00 2001 From: Arch-AI-tech Date: Wed, 28 Oct 2026 16:08:28 +0100 Subject: [PATCH 02/54] ci: check Cargo npm and Python licenses Co-Authored-By: Claude Sonnet 5.5 --- .github/workflows/licenses.yml | 78 ++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 .github/workflows/licenses.yml diff --git a/.github/workflows/licenses.yml b/.github/workflows/licenses.yml new file mode 100644 index 0000000..5864ed6 --- /dev/null +++ b/.github/workflows/licenses.yml @@ -0,0 +1,78 @@ +name: License checks + +on: + push: + branches: [main] + pull_request: + +permissions: + contents: read + +jobs: + cargo-licenses: + name: Cargo dependency licenses + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: EmbarkStudios/cargo-deny-action@b66acf5e9fe20f8aba065be86778a8a4c846f902 + with: + command: check licenses + + npm-licenses: + name: npm dependency licenses + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: "24" + - run: npm ci --ignore-scripts + - name: Check npm lockfile + run: | + node --input-type=module <<'NODE' + import assert from "node:assert/strict"; + import { readFileSync } from "node:fs"; + const allowed = new Set(["MIT", "Apache-2.0", "MIT OR Apache-2.0", "Apache-2.0 OR MIT"]); + function check(lock) { + if (lock?.lockfileVersion !== 3 || !lock.packages || typeof lock.packages !== "object" || Array.isArray(lock.packages) || !lock.packages[""]) throw new Error("Invalid npm lockfile"); + return Object.entries(lock.packages).filter(([path, pkg]) => path !== "" && !allowed.has(pkg?.license)).map(([path, pkg]) => `${path}: ${JSON.stringify(pkg?.license)}`); + } + assert.equal(check({ lockfileVersion: 3, packages: { "": {}, x: { license: "MIT AND GPL-3.0-only" } } }).length, 1); + assert.equal(check({ lockfileVersion: 3, packages: { "": {}, x: {} } }).length, 1); + assert.equal(check({ lockfileVersion: 3, packages: { "": {}, x: { license: "MIT" } } }).length, 0); + const failures = check(JSON.parse(readFileSync("package-lock.json", "utf8"))); + if (failures.length) { console.error(failures.join("\n")); process.exitCode = 1; } + NODE + + python-licenses: + name: Python dependency licenses (${{ matrix.os }}, ${{ matrix.python }}) + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + python: ["3.9", "3.11", "3.13"] + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python }} + - name: Install pinned checker and TOML parser + run: python -m pip install pip-licenses==5.5.5 tomli + - name: Resolve build, test and development dependencies + shell: python + run: | + import subprocess + import sys + from pathlib import Path + import tomli + root = tomli.loads(Path("pyproject.toml").read_text(encoding="utf-8")) + pure = tomli.loads(Path("packaging/pure/pyproject.toml").read_text(encoding="utf-8")) + requirements = root["build-system"]["requires"] + pure["build-system"]["requires"] + requirements += root["project"].get("dependencies", []) + for extra in root["project"].get("optional-dependencies", {}).values(): + requirements += extra + requirements += ["build"] + subprocess.run([sys.executable, "-m", "pip", "install", *requirements], check=True) + - name: Check installed Python dependency licenses + run: python -m piplicenses --with-system --from=meta --allow-only="MIT;MIT License;Apache-2.0;BSD-2-Clause;BSD-3-Clause;ISC;MIT OR Apache-2.0;Apache-2.0 OR MIT" From 944d939622ed09c1573b250283a3ba6ec0b5dbd7 Mon Sep 17 00:00:00 2001 From: Arch-AI-tech Date: Wed, 28 Oct 2026 17:43:46 +0100 Subject: [PATCH 03/54] ci: permit Apache license with LLVM exception Co-Authored-By: Claude Sonnet 5.5 --- deny.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/deny.toml b/deny.toml index 548a82d..2629bec 100644 --- a/deny.toml +++ b/deny.toml @@ -2,6 +2,7 @@ confidence-threshold = 0.8 allow = [ "Apache-2.0", + "Apache-2.0 WITH LLVM-exception", "BSD-2-Clause", "BSD-3-Clause", "BSL-1.0", From a4b9704dd42b4392cf00c08375d1d330e6ce9580 Mon Sep 17 00:00:00 2001 From: Arch-AI-tech Date: Wed, 28 Oct 2026 18:04:09 +0100 Subject: [PATCH 04/54] ci: verify scoped legacy Python licenses Co-Authored-By: Claude Sonnet 5.5 --- .github/workflows/licenses.yml | 60 ++++++++++++++++++++++++++++++++-- 1 file changed, 57 insertions(+), 3 deletions(-) diff --git a/.github/workflows/licenses.yml b/.github/workflows/licenses.yml index 5864ed6..d96a496 100644 --- a/.github/workflows/licenses.yml +++ b/.github/workflows/licenses.yml @@ -49,6 +49,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: fail-fast: false + max-parallel: 2 matrix: os: [ubuntu-latest, macos-latest, windows-latest] python: ["3.9", "3.11", "3.13"] @@ -57,8 +58,8 @@ jobs: - uses: actions/setup-python@v5 with: python-version: ${{ matrix.python }} - - name: Install pinned checker and TOML parser - run: python -m pip install pip-licenses==5.5.5 tomli + - name: Install TOML parser + run: python -m pip install tomli - name: Resolve build, test and development dependencies shell: python run: | @@ -75,4 +76,57 @@ jobs: requirements += ["build"] subprocess.run([sys.executable, "-m", "pip", "install", *requirements], check=True) - name: Check installed Python dependency licenses - run: python -m piplicenses --with-system --from=meta --allow-only="MIT;MIT License;Apache-2.0;BSD-2-Clause;BSD-3-Clause;ISC;MIT OR Apache-2.0;Apache-2.0 OR MIT" + shell: python + run: | + import hashlib + import importlib.metadata + import sys + + allowed = { + 'mit', 'mit license', 'apache-2.0', 'bsd-2-clause', 'bsd-3-clause', + 'isc', 'unlicense', 'mit or apache-2.0', 'apache-2.0 or mit', + 'apache-2.0 or bsd-2-clause', + } + reviewed = { + ('nodeenv', '1.11.0'): ('bsd', '606faf42d48b54d539dae99db6fecd48544535587e27275b319a3f36113cb6fa'), + ('colorama', '0.4.6'): ('', 'cac35c02686e5d04a5a7140bfb3b36e73aed496656e891102e428886d7930318'), + ('exceptiongroup', '1.3.1'): ('', '6e5070d765031e0ad403a1cbf90ae6d19a0228f802e320b7acff460aa72ed47c'), + } + + def check(name, version, expression, legacy, hashes): + expression = (expression or '').strip().lower() + legacy = (legacy or '').strip().lower() + if expression: + return expression in allowed + if legacy in allowed: + return True + entry = reviewed.get((name.lower().replace('_', '-'), version)) + return bool(entry and legacy == entry[0] and hashes == [entry[1]]) + + expected = reviewed[('nodeenv', '1.11.0')][1] + assert check('nodeenv', '1.11.0', None, 'BSD', [expected]) + assert not check('nodeenv', '1.11.1', None, 'BSD', [expected]) + assert not check('nodeenv', '1.11.0', None, 'BSD', ['wrong']) + assert not check('nodeenv', '1.11.0', 'MIT AND GPL-3.0-only', 'BSD', [expected]) + assert not check('unknown', '1', None, None, []) + assert check('approved', '1', 'Apache-2.0 OR BSD-2-Clause', None, []) + + failures = [] + distributions = list(importlib.metadata.distributions()) + if not distributions: + raise SystemExit('No Python distributions found') + for dist in distributions: + name = dist.metadata.get('Name') or '' + version = dist.version + expression = dist.metadata.get('License-Expression') + legacy = dist.metadata.get('License') + files = [item for item in (dist.files or ()) + if any(part.endswith('.dist-info') for part in item.parts) + and item.name.lower() in {'license', 'license.txt'}] + hashes = [hashlib.sha256(dist.locate_file(item).read_bytes()).hexdigest() for item in files] + if not check(name, version, expression, legacy, hashes): + failures.append(f'{name} {version}: expression={expression!r}, license={legacy!r}, hashes={hashes}') + if failures: + print('\n'.join(failures), file=sys.stderr) + raise SystemExit(1) + print(f'Checked {len(distributions)} installed Python distributions') From a8dff867a8cbff1e46ce7ccf79f44c3ba0d04b2f Mon Sep 17 00:00:00 2001 From: Arch-AI-tech Date: Wed, 28 Oct 2026 18:25:39 +0100 Subject: [PATCH 05/54] ci: isolate and verify Python tooling licenses Co-Authored-By: Claude Sonnet 5.5 --- .github/workflows/licenses.yml | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/.github/workflows/licenses.yml b/.github/workflows/licenses.yml index d96a496..fae3873 100644 --- a/.github/workflows/licenses.yml +++ b/.github/workflows/licenses.yml @@ -58,6 +58,19 @@ jobs: - uses: actions/setup-python@v5 with: python-version: ${{ matrix.python }} + - name: Create isolated license environment + shell: python + run: | + import os + import subprocess + import sys + venv = os.path.join(os.environ['RUNNER_TEMP'], 'license-venv') + subprocess.run([sys.executable, '-m', 'venv', venv], check=True) + bin_dir = os.path.join(venv, 'Scripts' if sys.platform == 'win32' else 'bin') + with open(os.environ['GITHUB_PATH'], 'a', encoding='utf-8') as output: + output.write(bin_dir + '\n') + with open(os.environ['GITHUB_ENV'], 'a', encoding='utf-8') as output: + output.write('VIRTUAL_ENV=' + venv + '\n') - name: Install TOML parser run: python -m pip install tomli - name: Resolve build, test and development dependencies @@ -69,12 +82,16 @@ jobs: import tomli root = tomli.loads(Path("pyproject.toml").read_text(encoding="utf-8")) pure = tomli.loads(Path("packaging/pure/pyproject.toml").read_text(encoding="utf-8")) - requirements = root["build-system"]["requires"] + pure["build-system"]["requires"] - requirements += root["project"].get("dependencies", []) - for extra in root["project"].get("optional-dependencies", {}).values(): + runtime = root['project'].get('dependencies', []) + pure['project'].get('dependencies', []) + if runtime: + raise SystemExit('Runtime dependencies require a separate license policy') + requirements = root['build-system']['requires'] + pure['build-system']['requires'] + for extra in root['project'].get('optional-dependencies', {}).values(): + requirements += extra + for extra in pure['project'].get('optional-dependencies', {}).values(): requirements += extra - requirements += ["build"] - subprocess.run([sys.executable, "-m", "pip", "install", *requirements], check=True) + requirements += ['build'] + subprocess.run([sys.executable, '-m', 'pip', 'install', '--upgrade', *requirements], check=True) - name: Check installed Python dependency licenses shell: python run: | @@ -84,13 +101,14 @@ jobs: allowed = { 'mit', 'mit license', 'apache-2.0', 'bsd-2-clause', 'bsd-3-clause', - 'isc', 'unlicense', 'mit or apache-2.0', 'apache-2.0 or mit', + 'isc', 'unlicense', 'psf-2.0', 'mit or apache-2.0', 'apache-2.0 or mit', 'apache-2.0 or bsd-2-clause', } reviewed = { ('nodeenv', '1.11.0'): ('bsd', '606faf42d48b54d539dae99db6fecd48544535587e27275b319a3f36113cb6fa'), ('colorama', '0.4.6'): ('', 'cac35c02686e5d04a5a7140bfb3b36e73aed496656e891102e428886d7930318'), ('exceptiongroup', '1.3.1'): ('', '6e5070d765031e0ad403a1cbf90ae6d19a0228f802e320b7acff460aa72ed47c'), + ('pathspec', '1.1.1'): ('', 'fab3dd6bdab226f1c08630b1dd917e11fcb4ec5e1e020e2c16f83a0a13863e85'), } def check(name, version, expression, legacy, hashes): @@ -110,6 +128,9 @@ jobs: assert not check('nodeenv', '1.11.0', 'MIT AND GPL-3.0-only', 'BSD', [expected]) assert not check('unknown', '1', None, None, []) assert check('approved', '1', 'Apache-2.0 OR BSD-2-Clause', None, []) + assert check('pathspec', '1.1.1', None, None, [reviewed[('pathspec', '1.1.1')][1]]) + assert not check('pathspec', '1.1.1', None, None, ['wrong']) + assert check('exceptiongroup', '1.3.1', None, None, [reviewed[('exceptiongroup', '1.3.1')][1]]) failures = [] distributions = list(importlib.metadata.distributions()) From 3863ff55c71a6f49a4994fd2f7a72a97fee7ccd2 Mon Sep 17 00:00:00 2001 From: Arch-AI-tech Date: Wed, 28 Oct 2026 18:39:45 +0100 Subject: [PATCH 06/54] ci: guard Python runtime dependency scope Co-Authored-By: Claude Sonnet 5.5 --- .github/workflows/licenses.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/licenses.yml b/.github/workflows/licenses.yml index fae3873..b839f23 100644 --- a/.github/workflows/licenses.yml +++ b/.github/workflows/licenses.yml @@ -85,10 +85,12 @@ jobs: runtime = root['project'].get('dependencies', []) + pure['project'].get('dependencies', []) if runtime: raise SystemExit('Runtime dependencies require a separate license policy') + root_extras = root['project'].get('optional-dependencies', {}) + pure_extras = pure['project'].get('optional-dependencies', {}) + if set(root_extras) - {'dev', 'test'} or pure_extras: + raise SystemExit('New extras require a separate license policy') requirements = root['build-system']['requires'] + pure['build-system']['requires'] - for extra in root['project'].get('optional-dependencies', {}).values(): - requirements += extra - for extra in pure['project'].get('optional-dependencies', {}).values(): + for extra in root_extras.values(): requirements += extra requirements += ['build'] subprocess.run([sys.executable, '-m', 'pip', 'install', '--upgrade', *requirements], check=True) From b80848f5d2aa44fe5e8ee468b04cbafbfc37e202 Mon Sep 17 00:00:00 2001 From: Arch-AI-tech Date: Thu, 29 Oct 2026 06:16:28 +0100 Subject: [PATCH 07/54] fix: include licenses in pure Python wheel Co-Authored-By: Claude Sonnet 5.5 --- .github/workflows/CI.yml | 1 + .github/workflows/release.yml | 1 + scripts/check_pure_wheel_metadata.py | 7 +++++++ 3 files changed, 9 insertions(+) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 6a7f2f8..983c8cc 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -229,6 +229,7 @@ jobs: mkdir -p "$stage/python/ktav" "$stage/packaging-pure" cp -R python/ktav/. "$stage/python/ktav/" cp README.md "$stage/packaging-pure/README.md" + cp LICENSE-MIT LICENSE-APACHE "$stage/packaging-pure/" # STAGED pyproject only: rewrite the ../../python mapping to # point inside the stage; the repo's packaging/pure/pyproject.toml # is NOT modified. diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 88a4e5a..fc9663e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -219,6 +219,7 @@ jobs: mkdir -p "$stage/python/ktav" "$stage/packaging-pure" cp -R python/ktav/. "$stage/python/ktav/" cp README.md "$stage/packaging-pure/README.md" + cp LICENSE-MIT LICENSE-APACHE "$stage/packaging-pure/" sed -e 's|../../python/ktav|../python/ktav|' \ -e 's|../../python|../python|' \ packaging/pure/pyproject.toml > "$stage/packaging-pure/pyproject.toml" diff --git a/scripts/check_pure_wheel_metadata.py b/scripts/check_pure_wheel_metadata.py index 40be3ce..1169aee 100644 --- a/scripts/check_pure_wheel_metadata.py +++ b/scripts/check_pure_wheel_metadata.py @@ -20,6 +20,13 @@ def main() -> None: if not entries[0].startswith("ktav-"): raise SystemExit(f"unexpected distribution metadata path: {entries[0]!r}") metadata = email.message_from_bytes(archive.read(entries[0])) + dist_info = entries[0].split("/", 1)[0] + root = Path(__file__).resolve().parent.parent + for name in ("LICENSE-MIT", "LICENSE-APACHE"): + candidates = [f"{dist_info}/{name}", f"{dist_info}/licenses/{name}"] + matches = [entry for entry in archive.namelist() if entry in candidates] + if len(matches) != 1 or archive.read(matches[0]) != (root / name).read_bytes(): + raise SystemExit(f"missing, duplicate or incorrect license file: {name}") if metadata.get("Name", "").lower() != "ktav" or metadata.get("Version") != "0.8.1": raise SystemExit("expected ktav 0.8.1 Name/Version metadata") metadata_versions = metadata.get_all("Metadata-Version", []) From 332ec8ea3f3c36449624cb2b867654a942723654 Mon Sep 17 00:00:00 2001 From: Arch-AI-tech Date: Thu, 29 Oct 2026 06:28:08 +0100 Subject: [PATCH 08/54] test: cover pure wheel license validation Co-Authored-By: Claude Sonnet 5.5 --- tests/ci/test_selfcontained_checks.py | 40 ++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/tests/ci/test_selfcontained_checks.py b/tests/ci/test_selfcontained_checks.py index 7c09416..0823f71 100644 --- a/tests/ci/test_selfcontained_checks.py +++ b/tests/ci/test_selfcontained_checks.py @@ -82,8 +82,15 @@ def test_installed_guard_allows_stdlib_and_source_pure_package() -> None: assert result.returncode == 0, result.stderr -def wheel(tmp_path: Path, *metadata: bytes) -> Path: +def wheel( + tmp_path: Path, *metadata: bytes, licenses: dict[str, bytes] | None = None +) -> Path: path = tmp_path / "fixture.whl" + if licenses is None: + licenses = { + f"licenses/{name}": (ROOT / name).read_bytes() + for name in ("LICENSE-MIT", "LICENSE-APACHE") + } with zipfile.ZipFile(path, "w") as archive: for index, body in enumerate(metadata): name = ( @@ -92,6 +99,8 @@ def wheel(tmp_path: Path, *metadata: bytes) -> Path: else f"extra-{index}.dist-info/METADATA" ) archive.writestr(name, body) + for name, body in licenses.items(): + archive.writestr(f"ktav-0.8.1.dist-info/{name}", body) return path @@ -117,6 +126,35 @@ def test_wheel_metadata_accepts_valid_pure_wheel(tmp_path: Path) -> None: assert "metadata OK" in result.stdout +@pytest.mark.parametrize("license_name", ["LICENSE-MIT", "LICENSE-APACHE"]) +@pytest.mark.parametrize("failure", ["missing", "modified", "duplicate"]) +def test_wheel_metadata_rejects_invalid_license( + tmp_path: Path, license_name: str, failure: str +) -> None: + licenses = { + f"licenses/{name}": (ROOT / name).read_bytes() + for name in ("LICENSE-MIT", "LICENSE-APACHE") + } + key = f"licenses/{license_name}" + if failure == "missing": + del licenses[key] + elif failure == "modified": + licenses[key] = b"incorrect license" + else: + licenses[license_name] = licenses[key] + valid = metadata_text(("Metadata-Version", "2.4"), ("Name", "ktav"), ("Version", "0.8.1")) + result = run_metadata(wheel(tmp_path, valid, licenses=licenses)) + assert result.returncode != 0 + assert f"missing, duplicate or incorrect license file: {license_name}" in result.stderr + + +def test_wheel_metadata_accepts_legacy_license_paths(tmp_path: Path) -> None: + licenses = {name: (ROOT / name).read_bytes() for name in ("LICENSE-MIT", "LICENSE-APACHE")} + valid = metadata_text(("Metadata-Version", "2.4"), ("Name", "ktav"), ("Version", "0.8.1")) + result = run_metadata(wheel(tmp_path, valid, licenses=licenses)) + assert result.returncode == 0, result.stderr + + def test_wheel_metadata_rejects_dependencies(tmp_path: Path) -> None: path = wheel( tmp_path, From 73533d2583eb3edabf09bb2a5ccd3d976b81bbb0 Mon Sep 17 00:00:00 2001 From: Arch-AI-tech Date: Thu, 29 Oct 2026 06:36:42 +0100 Subject: [PATCH 09/54] style: format wheel license tests Co-Authored-By: Claude Sonnet 5.5 --- tests/ci/test_selfcontained_checks.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/tests/ci/test_selfcontained_checks.py b/tests/ci/test_selfcontained_checks.py index 0823f71..596e05e 100644 --- a/tests/ci/test_selfcontained_checks.py +++ b/tests/ci/test_selfcontained_checks.py @@ -82,9 +82,7 @@ def test_installed_guard_allows_stdlib_and_source_pure_package() -> None: assert result.returncode == 0, result.stderr -def wheel( - tmp_path: Path, *metadata: bytes, licenses: dict[str, bytes] | None = None -) -> Path: +def wheel(tmp_path: Path, *metadata: bytes, licenses: dict[str, bytes] | None = None) -> Path: path = tmp_path / "fixture.whl" if licenses is None: licenses = { @@ -132,8 +130,7 @@ def test_wheel_metadata_rejects_invalid_license( tmp_path: Path, license_name: str, failure: str ) -> None: licenses = { - f"licenses/{name}": (ROOT / name).read_bytes() - for name in ("LICENSE-MIT", "LICENSE-APACHE") + f"licenses/{name}": (ROOT / name).read_bytes() for name in ("LICENSE-MIT", "LICENSE-APACHE") } key = f"licenses/{license_name}" if failure == "missing": From 3a194cafa9e896f472434b4a07467721d950c460 Mon Sep 17 00:00:00 2001 From: Arch-AI-tech Date: Thu, 29 Oct 2026 06:46:34 +0100 Subject: [PATCH 10/54] ci: capture Python Cargo license evidence Co-Authored-By: Claude Sonnet 5.5 --- .github/workflows/licenses.yml | 26 ++++++++++++++++++++++++++ ci/about.toml | 11 +++++++++++ 2 files changed, 37 insertions(+) create mode 100644 ci/about.toml diff --git a/.github/workflows/licenses.yml b/.github/workflows/licenses.yml index b839f23..3dd42f9 100644 --- a/.github/workflows/licenses.yml +++ b/.github/workflows/licenses.yml @@ -18,6 +18,32 @@ jobs: with: command: check licenses + cargo-license-evidence: + name: Cargo license evidence + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + - uses: taiki-e/install-action@v2 + with: + tool: cargo-about@0.9.3 + - name: Record the resolved Cargo graph and license texts + shell: bash + run: | + set -euo pipefail + mkdir -p "$RUNNER_TEMP/cargo-license-evidence" + cargo generate-lockfile + cp Cargo.lock "$RUNNER_TEMP/cargo-license-evidence/Cargo.lock" + rustc --version --verbose > "$RUNNER_TEMP/cargo-license-evidence/rustc.txt" + cargo metadata --locked --all-features --format-version 1 > "$RUNNER_TEMP/cargo-license-evidence/metadata.json" + cargo about generate --locked --all-features --fail --config ci/about.toml --format json > "$RUNNER_TEMP/cargo-license-evidence/licenses.json" + - uses: actions/upload-artifact@v4 + if: always() + with: + name: cargo-license-evidence + path: ${{ runner.temp }}/cargo-license-evidence/ + if-no-files-found: warn + npm-licenses: name: npm dependency licenses runs-on: ubuntu-latest diff --git a/ci/about.toml b/ci/about.toml new file mode 100644 index 0000000..b33cfe6 --- /dev/null +++ b/ci/about.toml @@ -0,0 +1,11 @@ +accepted = [ + "Apache-2.0", + "Apache-2.0 WITH LLVM-exception", + "BSD-2-Clause", + "BSD-3-Clause", + "BSL-1.0", + "ISC", + "MIT", + "Unicode-3.0", + "Zlib", +] From 92895f5619b02c71ec8d1f8decfb8e32612ba934 Mon Sep 17 00:00:00 2001 From: Arch-AI-tech Date: Thu, 29 Oct 2026 06:54:29 +0100 Subject: [PATCH 11/54] ci: capture Rust standard library copyright reports Co-Authored-By: Claude Sonnet 5.5 --- .github/workflows/licenses.yml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/.github/workflows/licenses.yml b/.github/workflows/licenses.yml index 3dd42f9..c6bcff3 100644 --- a/.github/workflows/licenses.yml +++ b/.github/workflows/licenses.yml @@ -35,6 +35,9 @@ jobs: cargo generate-lockfile cp Cargo.lock "$RUNNER_TEMP/cargo-license-evidence/Cargo.lock" rustc --version --verbose > "$RUNNER_TEMP/cargo-license-evidence/rustc.txt" + stdlib_report="$(rustc --print sysroot)/share/doc/rust/COPYRIGHT-library.html" + test -s "$stdlib_report" + cp "$stdlib_report" "$RUNNER_TEMP/cargo-license-evidence/COPYRIGHT-library.html" cargo metadata --locked --all-features --format-version 1 > "$RUNNER_TEMP/cargo-license-evidence/metadata.json" cargo about generate --locked --all-features --fail --config ci/about.toml --format json > "$RUNNER_TEMP/cargo-license-evidence/licenses.json" - uses: actions/upload-artifact@v4 @@ -44,6 +47,29 @@ jobs: path: ${{ runner.temp }}/cargo-license-evidence/ if-no-files-found: warn + rust-stdlib-081-evidence: + name: Published Python 0.8.1 Rust std evidence + runs-on: ubuntu-latest + steps: + - uses: dtolnay/rust-toolchain@1.99.1 + - name: Record Rust standard library copyright report + shell: bash + run: | + set -euo pipefail + mkdir -p "$RUNNER_TEMP/rust-stdlib-081" + rustc --version --verbose > "$RUNNER_TEMP/rust-stdlib-081/rustc.txt" + grep -Fx 'release: 1.99.1' "$RUNNER_TEMP/rust-stdlib-081/rustc.txt" + grep -Fx 'commit-hash: 58cf8f9c35b355199727627d07ae34a4e2846d67' "$RUNNER_TEMP/rust-stdlib-081/rustc.txt" + report="$(rustc --print sysroot)/share/doc/rust/COPYRIGHT-library.html" + test -s "$report" + cp "$report" "$RUNNER_TEMP/rust-stdlib-081/COPYRIGHT-library.html" + sha256sum "$RUNNER_TEMP/rust-stdlib-081/COPYRIGHT-library.html" + - uses: actions/upload-artifact@v4 + with: + name: rust-stdlib-081-evidence + path: ${{ runner.temp }}/rust-stdlib-081/ + if-no-files-found: error + npm-licenses: name: npm dependency licenses runs-on: ubuntu-latest From 3dfbe40e176eea74c99bbe60ad0a5b8e2f36b9db Mon Sep 17 00:00:00 2001 From: Arch-AI-tech Date: Thu, 29 Oct 2026 07:02:36 +0100 Subject: [PATCH 12/54] ci: verify musllinux static runtime option Co-Authored-By: Claude Sonnet 5.5 --- .github/workflows/musllinux-evidence.yml | 63 ++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 .github/workflows/musllinux-evidence.yml diff --git a/.github/workflows/musllinux-evidence.yml b/.github/workflows/musllinux-evidence.yml new file mode 100644 index 0000000..0125b41 --- /dev/null +++ b/.github/workflows/musllinux-evidence.yml @@ -0,0 +1,63 @@ +name: Musllinux runtime evidence + +on: + pull_request: + +permissions: + contents: read + +jobs: + static-libgcc: + name: Musllinux static libgcc (${{ matrix.target }}) + runs-on: ubuntu-latest + strategy: + fail-fast: false + max-parallel: 1 + matrix: + target: [x86_64, aarch64] + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: '3.12' + - uses: dtolnay/rust-toolchain@stable + - name: Resolve Cargo dependencies + run: cargo generate-lockfile + - name: Build musllinux wheel with static libgcc + uses: PyO3/maturin-action@v1 + with: + target: ${{ matrix.target }} + args: --release --out dist --locked + manylinux: musllinux_1_2 + before-script-linux: | + export RUSTFLAGS="-C link-arg=-static-libgcc" + - name: Verify wheel native payload + shell: python + run: | + import os + import re + import subprocess + import zipfile + from pathlib import Path + + wheels = list(Path('dist').glob('*.whl')) + if len(wheels) != 1: + raise SystemExit(f'expected one wheel, got {wheels!r}') + with zipfile.ZipFile(wheels[0]) as wheel: + native = [name for name in wheel.namelist() + if re.search(r'\.so(?:\.\d+)*$', name)] + if native != ['ktav/_core.abi3.so']: + raise SystemExit(f'unexpected wheel native payload: {native!r}') + module = Path(os.environ['RUNNER_TEMP']) / 'ktav-native.so' + module.write_bytes(wheel.read(native[0])) + dynamic = subprocess.check_output(['readelf', '-d', str(module)], text=True) + print(dynamic) + if re.search(r'Shared library: \[libgcc_s(?:\.|-)', dynamic): + raise SystemExit('native module still dynamically requires libgcc_s') + - uses: actions/upload-artifact@v4 + with: + name: musllinux-static-libgcc-${{ matrix.target }} + path: | + dist/*.whl + Cargo.lock + if-no-files-found: error From bc35c7f3826f9fc3d580f9613043e48cb90d42be Mon Sep 17 00:00:00 2001 From: Arch-AI-tech Date: Thu, 29 Oct 2026 07:13:18 +0100 Subject: [PATCH 13/54] ci: test static unwind linkage for musllinux Co-Authored-By: Claude Sonnet 5.5 --- .github/workflows/musllinux-evidence.yml | 25 +++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/.github/workflows/musllinux-evidence.yml b/.github/workflows/musllinux-evidence.yml index 0125b41..0c2aab0 100644 --- a/.github/workflows/musllinux-evidence.yml +++ b/.github/workflows/musllinux-evidence.yml @@ -30,7 +30,30 @@ jobs: args: --release --out dist --locked manylinux: musllinux_1_2 before-script-linux: | - export RUSTFLAGS="-C link-arg=-static-libgcc" + archive="$("${TARGET_CC:?}" -print-file-name=libgcc_eh.a)" + test -f "$archive" + cat > /tmp/ktav-static-unwind-linker <<'SH' + #!/usr/bin/env bash + set -euo pipefail + args=() + replaced=0 + for arg in "$@"; do + if [[ "$arg" == -lgcc_s ]]; then + args+=("-Wl,-Bstatic" "-lgcc_eh" "-Wl,-Bdynamic") + replaced=1 + else + args+=("$arg") + fi + done + if [[ "$replaced" != 1 ]]; then + printf '%s\n' 'expected -lgcc_s in target link' >&2 + exit 1 + fi + exec "${TARGET_CC:?}" -static-libgcc "${args[@]}" + SH + chmod +x /tmp/ktav-static-unwind-linker + export CARGO_TARGET_X86_64_UNKNOWN_LINUX_MUSL_LINKER=/tmp/ktav-static-unwind-linker + export CARGO_TARGET_AARCH64_UNKNOWN_LINUX_MUSL_LINKER=/tmp/ktav-static-unwind-linker - name: Verify wheel native payload shell: python run: | From 9015ca0adfcf56cb720b00c5d42d88f520782218 Mon Sep 17 00:00:00 2001 From: Arch-AI-tech Date: Thu, 29 Oct 2026 07:27:17 +0100 Subject: [PATCH 14/54] test: exercise musllinux native panic handling Co-Authored-By: Claude Sonnet 5.5 --- .github/workflows/musllinux-evidence.yml | 65 ++++++++++++++++++++++-- tests/fixtures/unwind-probe/Cargo.toml | 17 +++++++ tests/fixtures/unwind-probe/src/lib.rs | 11 ++++ 3 files changed, 89 insertions(+), 4 deletions(-) create mode 100644 tests/fixtures/unwind-probe/Cargo.toml create mode 100644 tests/fixtures/unwind-probe/src/lib.rs diff --git a/.github/workflows/musllinux-evidence.yml b/.github/workflows/musllinux-evidence.yml index 0c2aab0..96f8b16 100644 --- a/.github/workflows/musllinux-evidence.yml +++ b/.github/workflows/musllinux-evidence.yml @@ -54,6 +54,12 @@ jobs: chmod +x /tmp/ktav-static-unwind-linker export CARGO_TARGET_X86_64_UNKNOWN_LINUX_MUSL_LINKER=/tmp/ktav-static-unwind-linker export CARGO_TARGET_AARCH64_UNKNOWN_LINUX_MUSL_LINKER=/tmp/ktav-static-unwind-linker + cargo metadata --locked --format-version 1 | python3 -c 'import json,sys; d=json.load(sys.stdin); v=[p["version"] for p in d["packages"] if p["name"]=="pyo3"]; assert v==["0.28.3"],v' + cargo generate-lockfile --manifest-path tests/fixtures/unwind-probe/Cargo.toml + cargo build --locked --release --manifest-path tests/fixtures/unwind-probe/Cargo.toml --target "${RUST_MUSL_CROSS_TARGET:?}" + mkdir -p probe + cp "tests/fixtures/unwind-probe/target/${RUST_MUSL_CROSS_TARGET}/release/lib_unwind_probe.so" probe/_unwind_probe.so + cp tests/fixtures/unwind-probe/Cargo.lock probe/Cargo.lock - name: Verify wheel native payload shell: python run: | @@ -73,14 +79,65 @@ jobs: raise SystemExit(f'unexpected wheel native payload: {native!r}') module = Path(os.environ['RUNNER_TEMP']) / 'ktav-native.so' module.write_bytes(wheel.read(native[0])) - dynamic = subprocess.check_output(['readelf', '-d', str(module)], text=True) - print(dynamic) - if re.search(r'Shared library: \[libgcc_s(?:\.|-)', dynamic): - raise SystemExit('native module still dynamically requires libgcc_s') + for binary in (module, Path('probe/_unwind_probe.so')): + dynamic = subprocess.check_output(['readelf', '-d', str(binary)], text=True) + print(f'{binary}:\n{dynamic}') + if re.search(r'Shared library: \[libgcc_s(?:\.|-)', dynamic): + raise SystemExit(f'{binary} still dynamically requires libgcc_s') - uses: actions/upload-artifact@v4 with: name: musllinux-static-libgcc-${{ matrix.target }} path: | dist/*.whl Cargo.lock + probe/_unwind_probe.so + probe/Cargo.lock if-no-files-found: error + + musllinux-runtime: + name: Musllinux runtime and panic (${{ matrix.target }}) + needs: static-libgcc + runs-on: ${{ matrix.runner }} + strategy: + fail-fast: false + matrix: + include: + - target: x86_64 + runner: ubuntu-24.04 + image: quay.io/pypa/musllinux_1_2_x86_64 + - target: aarch64 + runner: ubuntu-24.04-arm + image: quay.io/pypa/musllinux_1_2_aarch64 + steps: + - uses: actions/download-artifact@v4 + with: + name: musllinux-static-libgcc-${{ matrix.target }} + path: payload + - name: Verify native parsing and PyO3 panic conversion + shell: bash + env: + MUSL_IMAGE: ${{ matrix.image }} + run: | + set -euo pipefail + docker pull "$MUSL_IMAGE" + docker run --rm -i --network none \ + --mount "type=bind,source=$PWD/payload,target=/payload,readonly" \ + "$MUSL_IMAGE" /bin/sh -c ' + /opt/python/cp312-cp312/bin/python -m pip install --no-index --no-deps /payload/dist/*.whl + PYTHONPATH=/payload/probe /opt/python/cp312-cp312/bin/python - + ' <<'PY' + import _unwind_probe + import ktav + import ktav._core + + for _ in range(2): + try: + _unwind_probe.panic_probe() + except BaseException as exc: + if type(exc).__name__ != 'PanicException' or str(exc) != 'musllinux unwind probe': + raise + else: + raise AssertionError('Rust panic was not converted to PanicException') + if ktav.loads('answer: 42') != {'answer': 42}: + raise AssertionError('native parser returned an unexpected value') + PY diff --git a/tests/fixtures/unwind-probe/Cargo.toml b/tests/fixtures/unwind-probe/Cargo.toml new file mode 100644 index 0000000..592152d --- /dev/null +++ b/tests/fixtures/unwind-probe/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "ktav-unwind-probe" +version = "0.0.0" +edition = "2021" +publish = false + +[workspace] + +[lib] +name = "_unwind_probe" +crate-type = ["cdylib"] + +[dependencies] +pyo3 = { version = "=0.28.3", features = ["extension-module", "abi3-py39"] } + +[profile.release] +panic = "unwind" diff --git a/tests/fixtures/unwind-probe/src/lib.rs b/tests/fixtures/unwind-probe/src/lib.rs new file mode 100644 index 0000000..7d80601 --- /dev/null +++ b/tests/fixtures/unwind-probe/src/lib.rs @@ -0,0 +1,11 @@ +use pyo3::prelude::*; + +#[pyfunction] +fn panic_probe() { + panic!("musllinux unwind probe"); +} + +#[pymodule] +fn _unwind_probe(module: &Bound<'_, PyModule>) -> PyResult<()> { + module.add_function(wrap_pyfunction!(panic_probe, module)?) +} From 9b825d4ef3fb41eb4809e5adba11fb364b89acbb Mon Sep 17 00:00:00 2001 From: Arch-AI-tech Date: Thu, 29 Oct 2026 07:28:04 +0100 Subject: [PATCH 15/54] ci: reject generic license text fallbacks Co-Authored-By: Claude Sonnet 5.5 --- .github/workflows/licenses.yml | 22 ++++++++++++++++++++++ ci/about.toml | 10 +++++----- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/.github/workflows/licenses.yml b/.github/workflows/licenses.yml index c6bcff3..0520a05 100644 --- a/.github/workflows/licenses.yml +++ b/.github/workflows/licenses.yml @@ -40,6 +40,28 @@ jobs: cp "$stdlib_report" "$RUNNER_TEMP/cargo-license-evidence/COPYRIGHT-library.html" cargo metadata --locked --all-features --format-version 1 > "$RUNNER_TEMP/cargo-license-evidence/metadata.json" cargo about generate --locked --all-features --fail --config ci/about.toml --format json > "$RUNNER_TEMP/cargo-license-evidence/licenses.json" + python3 - "$RUNNER_TEMP/cargo-license-evidence/licenses.json" <<'PY' + import json + import sys + + def missing_source(entry): + return not entry.get('source_path') or not entry.get('text') + + assert missing_source({'source_path': None, 'text': 'generic'}) + assert not missing_source({'source_path': 'LICENSE', 'text': 'text'}) + with open(sys.argv[1], encoding='utf-8') as source: + report = json.load(source) + licenses = report.get('licenses') + if not isinstance(licenses, list) or not licenses: + raise SystemExit('license report has no license texts') + fallback = [entry for entry in licenses if missing_source(entry)] + if fallback: + for entry in fallback: + packages = [(item['crate']['name'], item['crate']['version']) + for item in entry.get('used_by', [])] + print(f"unverified {entry.get('id')}: {packages}", file=sys.stderr) + raise SystemExit('canonical license fallback lacks verified source text') + PY - uses: actions/upload-artifact@v4 if: always() with: diff --git a/ci/about.toml b/ci/about.toml index b33cfe6..9ef5947 100644 --- a/ci/about.toml +++ b/ci/about.toml @@ -1,11 +1,11 @@ accepted = [ - "Apache-2.0", - "Apache-2.0 WITH LLVM-exception", + "MIT", + "ISC", "BSD-2-Clause", "BSD-3-Clause", + "Zlib", "BSL-1.0", - "ISC", - "MIT", + "Apache-2.0 WITH LLVM-exception", + "Apache-2.0", "Unicode-3.0", - "Zlib", ] From 3674538157fcddf59db54dfec8f863f6c64e3a55 Mon Sep 17 00:00:00 2001 From: Arch-AI-tech Date: Thu, 29 Oct 2026 07:39:39 +0100 Subject: [PATCH 16/54] fix: build musl panic probe as dynamic library Co-Authored-By: Claude Sonnet 5.5 --- .github/workflows/musllinux-evidence.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/musllinux-evidence.yml b/.github/workflows/musllinux-evidence.yml index 96f8b16..81a5392 100644 --- a/.github/workflows/musllinux-evidence.yml +++ b/.github/workflows/musllinux-evidence.yml @@ -56,7 +56,7 @@ jobs: export CARGO_TARGET_AARCH64_UNKNOWN_LINUX_MUSL_LINKER=/tmp/ktav-static-unwind-linker cargo metadata --locked --format-version 1 | python3 -c 'import json,sys; d=json.load(sys.stdin); v=[p["version"] for p in d["packages"] if p["name"]=="pyo3"]; assert v==["0.28.3"],v' cargo generate-lockfile --manifest-path tests/fixtures/unwind-probe/Cargo.toml - cargo build --locked --release --manifest-path tests/fixtures/unwind-probe/Cargo.toml --target "${RUST_MUSL_CROSS_TARGET:?}" + RUSTFLAGS="-C target-feature=-crt-static" cargo build --locked --release --manifest-path tests/fixtures/unwind-probe/Cargo.toml --target "${RUST_MUSL_CROSS_TARGET:?}" mkdir -p probe cp "tests/fixtures/unwind-probe/target/${RUST_MUSL_CROSS_TARGET}/release/lib_unwind_probe.so" probe/_unwind_probe.so cp tests/fixtures/unwind-probe/Cargo.lock probe/Cargo.lock From e07d45dea8b78abb015b96508160e14952fd1c5f Mon Sep 17 00:00:00 2001 From: Arch-AI-tech Date: Thu, 29 Oct 2026 07:54:36 +0100 Subject: [PATCH 17/54] test: run native corpus on musllinux Co-Authored-By: Claude Sonnet 5.5 --- .github/workflows/musllinux-evidence.yml | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/.github/workflows/musllinux-evidence.yml b/.github/workflows/musllinux-evidence.yml index 81a5392..9912ad0 100644 --- a/.github/workflows/musllinux-evidence.yml +++ b/.github/workflows/musllinux-evidence.yml @@ -109,22 +109,41 @@ jobs: runner: ubuntu-24.04-arm image: quay.io/pypa/musllinux_1_2_aarch64 steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + persist-credentials: false + - uses: actions/setup-python@v5 + with: + python-version: '3.12' - uses: actions/download-artifact@v4 with: name: musllinux-static-libgcc-${{ matrix.target }} path: payload + - name: Download pure Python test dependencies + run: python -m pip download --only-binary=:all: --dest payload/testdeps pytest - name: Verify native parsing and PyO3 panic conversion shell: bash env: MUSL_IMAGE: ${{ matrix.image }} run: | set -euo pipefail + stage="$(mktemp -d)" + mkdir -p "$stage/spec" + cp -R tests "$stage/tests" + cp -R spec/versions "$stage/spec/versions" + cp pyproject.toml "$stage/pyproject.toml" docker pull "$MUSL_IMAGE" docker run --rm -i --network none \ --mount "type=bind,source=$PWD/payload,target=/payload,readonly" \ - "$MUSL_IMAGE" /bin/sh -c ' + --mount "type=bind,source=$stage,target=/work,readonly" \ + --workdir /work "$MUSL_IMAGE" /bin/sh -ec ' + /opt/python/cp312-cp312/bin/python -m pip install --no-index --find-links=/payload/testdeps pytest /opt/python/cp312-cp312/bin/python -m pip install --no-index --no-deps /payload/dist/*.whl PYTHONPATH=/payload/probe /opt/python/cp312-cp312/bin/python - + KTAV_REQUIRE_NATIVE=1 PYTHONDONTWRITEBYTECODE=1 PYTHONPATH=/payload/probe \ + /opt/python/cp312-cp312/bin/python -m pytest -p no:cacheprovider -q \ + tests/test_conformance.py tests/edit tests/parse tests/serialize tests/roundtrip ' <<'PY' import _unwind_probe import ktav From 4e3f4dc281bf6fc972fa552ccde6d326859bab7c Mon Sep 17 00:00:00 2001 From: Arch-AI-tech Date: Thu, 29 Oct 2026 07:55:40 +0100 Subject: [PATCH 18/54] ci: collect Cargo dependency notices Co-Authored-By: Claude Sonnet 5.5 --- .github/workflows/licenses.yml | 4 + scripts/collect_cargo_notices.py | 107 ++++++++++++++++++++++++ tests/ci/test_collect_cargo_notices.py | 108 +++++++++++++++++++++++++ 3 files changed, 219 insertions(+) create mode 100644 scripts/collect_cargo_notices.py create mode 100644 tests/ci/test_collect_cargo_notices.py diff --git a/.github/workflows/licenses.yml b/.github/workflows/licenses.yml index 0520a05..7064d96 100644 --- a/.github/workflows/licenses.yml +++ b/.github/workflows/licenses.yml @@ -62,6 +62,10 @@ jobs: print(f"unverified {entry.get('id')}: {packages}", file=sys.stderr) raise SystemExit('canonical license fallback lacks verified source text') PY + python3 scripts/collect_cargo_notices.py \ + "$RUNNER_TEMP/cargo-license-evidence/metadata.json" \ + "$RUNNER_TEMP/cargo-license-evidence/licenses.json" \ + "$RUNNER_TEMP/cargo-license-evidence/notices.json" - uses: actions/upload-artifact@v4 if: always() with: diff --git a/scripts/collect_cargo_notices.py b/scripts/collect_cargo_notices.py new file mode 100644 index 0000000..37723b4 --- /dev/null +++ b/scripts/collect_cargo_notices.py @@ -0,0 +1,107 @@ +"""Собрать лицензионные уведомления разрешённого Cargo-графа.""" + +from __future__ import annotations + +import hashlib +import json +import re +import sys +from pathlib import Path + +NOTICE_NAME = re.compile( + r"^(?:notice|notices|copyright|copying|patents|third[-_ ]party[-_ ]notices?)" + r"(?:[-_].+|\.(?:txt|md|rst|html|lesser))?$", + re.IGNORECASE, +) +MAX_NOTICE_BYTES = 4 * 1024 * 1024 +MAX_TOTAL_BYTES = 32 * 1024 * 1024 + + +def _packages(metadata: dict, report: dict) -> list[dict]: + packages = metadata.get("packages") + crates = report.get("crates") + licenses = report.get("licenses") + if not isinstance(packages, list) or not packages: + raise ValueError("Cargo metadata has no packages") + if not isinstance(crates, list) or not isinstance(licenses, list) or not licenses: + raise ValueError("license report is incomplete") + package_ids = {p["id"] for p in packages} + reported_ids = {c["package"]["id"] for c in crates} + licensed_ids = {u["crate"]["id"] for lic in licenses for u in lic["used_by"]} + if ( + len(package_ids) != len(packages) + or package_ids != reported_ids + or package_ids != licensed_ids + ): + raise ValueError("Cargo graph and license report coverage differ") + if any(not lic.get("source_path") or not lic.get("text") for lic in licenses): + raise ValueError("license report contains an unverified fallback") + return sorted(packages, key=lambda p: p["id"]) + + +def _notice_files(root: Path) -> list[Path]: + if root.is_symlink() or not root.is_dir(): + raise ValueError(f"invalid package source directory: {root}") + pending = [root] + found = [] + while pending: + directory = pending.pop() + for path in sorted(directory.iterdir()): + if path.name in {".git", "target"}: + continue + if path.is_symlink(): + if NOTICE_NAME.fullmatch(path.name) or path.is_dir(): + raise ValueError(f"unverified symlink in package source: {path}") + continue + if path.is_dir(): + pending.append(path) + elif path.is_file() and NOTICE_NAME.fullmatch(path.name): + found.append(path) + return sorted(found) + + +def _collect(metadata: dict, report: dict) -> dict: + packages = _packages(metadata, report) + notices = [] + total = 0 + for package in packages: + root = Path(package["manifest_path"]).parent + for path in _notice_files(root): + size = path.stat().st_size + if size > MAX_NOTICE_BYTES or total + size > MAX_TOTAL_BYTES: + raise ValueError(f"notice size limit exceeded: {path}") + data = path.read_bytes() + if len(data) != size: + raise ValueError(f"notice changed while reading: {path}") + total += len(data) + notices.append( + { + "package": package["name"], + "version": package["version"], + "source": package.get("source"), + "path": path.relative_to(root).as_posix(), + "sha256": hashlib.sha256(data).hexdigest(), + "text": data.decode("utf-8"), + } + ) + return {"schema_version": 1, "package_count": len(packages), "notices": notices} + + +def main() -> None: + """Проверить покрытие и сохранить уведомления вместе с их хешами.""" + if len(sys.argv) != 4: + raise SystemExit("usage: collect_cargo_notices.py METADATA LICENSES OUTPUT") + try: + metadata = json.loads(Path(sys.argv[1]).read_text(encoding="utf-8")) + report = json.loads(Path(sys.argv[2]).read_text(encoding="utf-8")) + result = _collect(metadata, report) + Path(sys.argv[3]).write_text( + json.dumps(result, ensure_ascii=False, indent=2) + "\n", encoding="utf-8" + ) + except (OSError, ValueError, KeyError, TypeError) as exc: + raise SystemExit(str(exc)) from exc + print(f"Collected {len(result['notices'])} notices from {result['package_count']} packages") + + +if __name__ == "__main__": + main() diff --git a/tests/ci/test_collect_cargo_notices.py b/tests/ci/test_collect_cargo_notices.py new file mode 100644 index 0000000..78d919e --- /dev/null +++ b/tests/ci/test_collect_cargo_notices.py @@ -0,0 +1,108 @@ +"""Проверить инвентаризацию Cargo notices.""" + +from __future__ import annotations + +import hashlib +import json +import subprocess +import sys +from pathlib import Path + +import pytest + +ROOT = Path(__file__).resolve().parents[2] +CHECKER = ROOT / "scripts" / "collect_cargo_notices.py" + + +def _inputs(tmp_path: Path) -> tuple[Path, dict, dict]: + source = tmp_path / "crate" + source.mkdir() + (source / "Cargo.toml").write_text("[package]\n", encoding="utf-8") + package = { + "id": "registry+example#fixture@1.0.0", + "name": "fixture", + "version": "1.0.0", + "source": "registry+example", + "manifest_path": str(source / "Cargo.toml"), + } + metadata = {"packages": [package]} + report = { + "crates": [{"package": {"id": package["id"]}}], + "licenses": [ + {"source_path": "LICENSE", "text": "license", "used_by": [{"crate": package}]} + ], + } + return source, metadata, report + + +def _run( + tmp_path: Path, metadata: dict, report: dict +) -> tuple[subprocess.CompletedProcess[str], Path]: + metadata_path = tmp_path / "metadata.json" + report_path = tmp_path / "licenses.json" + output_path = tmp_path / "notices.json" + metadata_path.write_text(json.dumps(metadata), encoding="utf-8") + report_path.write_text(json.dumps(report), encoding="utf-8") + result = subprocess.run( + [sys.executable, str(CHECKER), str(metadata_path), str(report_path), str(output_path)], + capture_output=True, + text=True, + ) + return result, output_path + + +def test_collects_nested_notices_with_hashes(tmp_path: Path) -> None: + source, metadata, report = _inputs(tmp_path) + (source / "nested").mkdir() + (source / "nested" / "NOTICE.txt").write_text("notice text", encoding="utf-8") + (source / "COPYRIGHT").write_text("copyright text", encoding="utf-8") + (source / "target").mkdir() + (source / "target" / "NOTICE").write_text("ignored", encoding="utf-8") + result, output = _run(tmp_path, metadata, report) + assert result.returncode == 0, result.stderr + collected = json.loads(output.read_text(encoding="utf-8")) + assert collected["package_count"] == 1 + notices = {entry["path"]: entry for entry in collected["notices"]} + assert set(notices) == {"COPYRIGHT", "nested/NOTICE.txt"} + assert notices["nested/NOTICE.txt"]["text"] == "notice text" + assert notices["nested/NOTICE.txt"]["sha256"] == hashlib.sha256(b"notice text").hexdigest() + + +def test_rejects_uncovered_package(tmp_path: Path) -> None: + _, metadata, report = _inputs(tmp_path) + report["licenses"][0]["used_by"] = [] + result, output = _run(tmp_path, metadata, report) + assert result.returncode != 0 + assert "coverage differ" in result.stderr + assert not output.exists() + + +def test_rejects_generic_license_fallback(tmp_path: Path) -> None: + _, metadata, report = _inputs(tmp_path) + report["licenses"][0]["source_path"] = None + result, output = _run(tmp_path, metadata, report) + assert result.returncode != 0 + assert "unverified fallback" in result.stderr + assert not output.exists() + + +def test_rejects_non_utf8_notice(tmp_path: Path) -> None: + source, metadata, report = _inputs(tmp_path) + (source / "NOTICE").write_bytes(b"\xff") + result, output = _run(tmp_path, metadata, report) + assert result.returncode != 0 + assert not output.exists() + + +def test_rejects_linked_notice(tmp_path: Path) -> None: + source, metadata, report = _inputs(tmp_path) + external = tmp_path / "external" + external.write_text("outside", encoding="utf-8") + try: + (source / "NOTICE").symlink_to(external) + except OSError: + pytest.skip("symlinks unavailable") + result, output = _run(tmp_path, metadata, report) + assert result.returncode != 0 + assert "unverified symlink" in result.stderr + assert not output.exists() From 4065b7661e881ce5cc0755baa50cbb104a61a571 Mon Sep 17 00:00:00 2001 From: Arch-AI-tech Date: Thu, 29 Oct 2026 08:14:34 +0100 Subject: [PATCH 19/54] fix: validate musllinux runtime workflow Co-Authored-By: Claude Sonnet 5.5 --- .github/workflows/musllinux-evidence.yml | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/.github/workflows/musllinux-evidence.yml b/.github/workflows/musllinux-evidence.yml index 9912ad0..deb2e09 100644 --- a/.github/workflows/musllinux-evidence.yml +++ b/.github/workflows/musllinux-evidence.yml @@ -32,6 +32,13 @@ jobs: before-script-linux: | archive="$("${TARGET_CC:?}" -print-file-name=libgcc_eh.a)" test -f "$archive" + mkdir -p probe + { + "${TARGET_CC}" --version + "${TARGET_CC}" -v 2>&1 + sha256sum "$archive" + "${TARGET_AR:?}" t "$archive" + } > probe/libgcc-evidence.txt cat > /tmp/ktav-static-unwind-linker <<'SH' #!/usr/bin/env bash set -euo pipefail @@ -79,11 +86,22 @@ jobs: raise SystemExit(f'unexpected wheel native payload: {native!r}') module = Path(os.environ['RUNNER_TEMP']) / 'ktav-native.so' module.write_bytes(wheel.read(native[0])) - for binary in (module, Path('probe/_unwind_probe.so')): + for binary, expected in ( + (module, 'PyInit__core'), + (Path('probe/_unwind_probe.so'), 'PyInit__unwind_probe'), + ): dynamic = subprocess.check_output(['readelf', '-d', str(binary)], text=True) print(f'{binary}:\n{dynamic}') if re.search(r'Shared library: \[libgcc_s(?:\.|-)', dynamic): raise SystemExit(f'{binary} still dynamically requires libgcc_s') + symbols = subprocess.check_output( + ['readelf', '--dyn-syms', '--wide', str(binary)], text=True + ) + exports = [parts[7] for line in symbols.splitlines() if len(parts := line.split()) >= 8 + and parts[4] in ('GLOBAL', 'WEAK') and parts[5] in ('DEFAULT', 'PROTECTED') + and parts[6] != 'UND'] + if exports != [expected]: + raise SystemExit(f'{binary}: unexpected exported symbols: {exports!r}') - uses: actions/upload-artifact@v4 with: name: musllinux-static-libgcc-${{ matrix.target }} @@ -92,6 +110,7 @@ jobs: Cargo.lock probe/_unwind_probe.so probe/Cargo.lock + probe/libgcc-evidence.txt if-no-files-found: error musllinux-runtime: @@ -121,7 +140,7 @@ jobs: name: musllinux-static-libgcc-${{ matrix.target }} path: payload - name: Download pure Python test dependencies - run: python -m pip download --only-binary=:all: --dest payload/testdeps pytest + run: 'python -m pip download --only-binary=:all: --dest payload/testdeps pytest' - name: Verify native parsing and PyO3 panic conversion shell: bash env: From a53d563c0b8cb1c7461ef1f1b3cc6bc90c00d977 Mon Sep 17 00:00:00 2001 From: Arch-AI-tech Date: Thu, 29 Oct 2026 08:15:30 +0100 Subject: [PATCH 20/54] fix: harden Cargo notice evidence checks Co-Authored-By: Claude Sonnet 5.5 --- .github/workflows/licenses.yml | 10 ++++++++++ scripts/collect_cargo_notices.py | 5 +++-- tests/ci/test_collect_cargo_notices.py | 4 +++- tests/fixtures/unwind-probe/Cargo.toml | 1 + 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/.github/workflows/licenses.yml b/.github/workflows/licenses.yml index 7064d96..3b7048d 100644 --- a/.github/workflows/licenses.yml +++ b/.github/workflows/licenses.yml @@ -18,6 +18,16 @@ jobs: with: command: check licenses + unwind-probe-licenses: + name: Unwind probe dependency licenses + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: EmbarkStudios/cargo-deny-action@b66acf5e9fe20f8aba065be86778a8a4c846f902 + with: + manifest-path: ./tests/fixtures/unwind-probe/Cargo.toml + command: check licenses + cargo-license-evidence: name: Cargo license evidence runs-on: ubuntu-latest diff --git a/scripts/collect_cargo_notices.py b/scripts/collect_cargo_notices.py index 37723b4..79da382 100644 --- a/scripts/collect_cargo_notices.py +++ b/scripts/collect_cargo_notices.py @@ -9,7 +9,8 @@ from pathlib import Path NOTICE_NAME = re.compile( - r"^(?:notice|notices|copyright|copying|patents|third[-_ ]party[-_ ]notices?)" + r"^(?:notice|notices|copyright|copying|patents|authors|credits|acknowledg(?:e)?ments|" + r"third[-_ ]?party[-_ ]?(?:notices?|licen[cs]es?))" r"(?:[-_].+|\.(?:txt|md|rst|html|lesser))?$", re.IGNORECASE, ) @@ -88,7 +89,7 @@ def _collect(metadata: dict, report: dict) -> dict: def main() -> None: - """Проверить покрытие и сохранить уведомления вместе с их хешами.""" + """Проверить покрытие, сохранить уведомления и хеши.""" if len(sys.argv) != 4: raise SystemExit("usage: collect_cargo_notices.py METADATA LICENSES OUTPUT") try: diff --git a/tests/ci/test_collect_cargo_notices.py b/tests/ci/test_collect_cargo_notices.py index 78d919e..b6b90a2 100644 --- a/tests/ci/test_collect_cargo_notices.py +++ b/tests/ci/test_collect_cargo_notices.py @@ -56,6 +56,8 @@ def test_collects_nested_notices_with_hashes(tmp_path: Path) -> None: (source / "nested").mkdir() (source / "nested" / "NOTICE.txt").write_text("notice text", encoding="utf-8") (source / "COPYRIGHT").write_text("copyright text", encoding="utf-8") + (source / "ThirdPartyNotices.txt").write_text("third party", encoding="utf-8") + (source / "CREDITS").write_text("contributors", encoding="utf-8") (source / "target").mkdir() (source / "target" / "NOTICE").write_text("ignored", encoding="utf-8") result, output = _run(tmp_path, metadata, report) @@ -63,7 +65,7 @@ def test_collects_nested_notices_with_hashes(tmp_path: Path) -> None: collected = json.loads(output.read_text(encoding="utf-8")) assert collected["package_count"] == 1 notices = {entry["path"]: entry for entry in collected["notices"]} - assert set(notices) == {"COPYRIGHT", "nested/NOTICE.txt"} + assert set(notices) == {"COPYRIGHT", "CREDITS", "ThirdPartyNotices.txt", "nested/NOTICE.txt"} assert notices["nested/NOTICE.txt"]["text"] == "notice text" assert notices["nested/NOTICE.txt"]["sha256"] == hashlib.sha256(b"notice text").hexdigest() diff --git a/tests/fixtures/unwind-probe/Cargo.toml b/tests/fixtures/unwind-probe/Cargo.toml index 592152d..581ca1f 100644 --- a/tests/fixtures/unwind-probe/Cargo.toml +++ b/tests/fixtures/unwind-probe/Cargo.toml @@ -2,6 +2,7 @@ name = "ktav-unwind-probe" version = "0.0.0" edition = "2021" +license = "MIT OR Apache-2.0" publish = false [workspace] From f22f38885d99bb2b10d7865efa704f68bba1542e Mon Sep 17 00:00:00 2001 From: Arch-AI-tech Date: Thu, 29 Oct 2026 08:39:44 +0100 Subject: [PATCH 21/54] ci: pin and audit musllinux unwind toolchains Co-Authored-By: Claude Sonnet 5.5 --- .github/workflows/CI.yml | 3 ++ .github/workflows/musllinux-evidence.yml | 53 ++++++++++++------------ scripts/musllinux-unwind-linker.sh | 18 ++++++++ 3 files changed, 48 insertions(+), 26 deletions(-) create mode 100755 scripts/musllinux-unwind-linker.sh diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 983c8cc..ae0acec 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -64,6 +64,9 @@ jobs: run: ruff format --check . - name: Mypy run: mypy python + - name: Validate musllinux workflow syntax + run: | + ruby -e 'require "yaml"; workflow = YAML.load_file(ARGV.fetch(0)); jobs = workflow.fetch("jobs"); abort "missing musllinux jobs" unless %w[static-libgcc musllinux-runtime].all? { |name| jobs.key?(name) }' .github/workflows/musllinux-evidence.yml rust-lint: name: Rust fmt + clippy diff --git a/.github/workflows/musllinux-evidence.yml b/.github/workflows/musllinux-evidence.yml index deb2e09..e7b9bfe 100644 --- a/.github/workflows/musllinux-evidence.yml +++ b/.github/workflows/musllinux-evidence.yml @@ -14,13 +14,17 @@ jobs: fail-fast: false max-parallel: 1 matrix: - target: [x86_64, aarch64] + include: + - target: x86_64 + image: ghcr.io/rust-cross/rust-musl-cross@sha256:ce75e9174325d4fbb3de85c309e2d7ca29f7500169bc4b5d2c611ff7e86d549a + - target: aarch64 + image: ghcr.io/rust-cross/rust-musl-cross@sha256:ecae5dd62d1c938c14f8071d36c16fa699860aace03bfb5284fb1216474d2643 steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: python-version: '3.12' - - uses: dtolnay/rust-toolchain@stable + - uses: dtolnay/rust-toolchain@1.99.1 - name: Resolve Cargo dependencies run: cargo generate-lockfile - name: Build musllinux wheel with static libgcc @@ -29,38 +33,34 @@ jobs: target: ${{ matrix.target }} args: --release --out dist --locked manylinux: musllinux_1_2 + container: ${{ matrix.image }} + rust-toolchain: '1.99.1' before-script-linux: | archive="$("${TARGET_CC:?}" -print-file-name=libgcc_eh.a)" + core_archive="$("${TARGET_CC}" -print-file-name=libgcc.a)" test -f "$archive" + test -f "$core_archive" + case "${RUST_MUSL_CROSS_TARGET:?}" in + x86_64-unknown-linux-musl) expected=975e964745276d7669f389473d5150223be491ff40217faf5676f98cd18e914a ;; + aarch64-unknown-linux-musl) expected=0ef3ef20cc85454372d957fddf83f5fcf9a18ac6166aee251d776e3765ef64c8 ;; + *) printf '%s\n' 'unexpected musl target' >&2; exit 1 ;; + esac + printf '%s %s\n' "$expected" "$archive" | sha256sum --check mkdir -p probe + rustc --version --verbose > probe/rustc.txt + grep -Fx 'release: 1.99.1' probe/rustc.txt + grep -Fx 'commit-hash: 58cf8f9c35b355199727627d07ae34a4e2846d67' probe/rustc.txt { "${TARGET_CC}" --version "${TARGET_CC}" -v 2>&1 - sha256sum "$archive" - "${TARGET_AR:?}" t "$archive" + for library in "$archive" "$core_archive"; do + sha256sum "$library" + "${TARGET_AR:?}" t "$library" + done } > probe/libgcc-evidence.txt - cat > /tmp/ktav-static-unwind-linker <<'SH' - #!/usr/bin/env bash - set -euo pipefail - args=() - replaced=0 - for arg in "$@"; do - if [[ "$arg" == -lgcc_s ]]; then - args+=("-Wl,-Bstatic" "-lgcc_eh" "-Wl,-Bdynamic") - replaced=1 - else - args+=("$arg") - fi - done - if [[ "$replaced" != 1 ]]; then - printf '%s\n' 'expected -lgcc_s in target link' >&2 - exit 1 - fi - exec "${TARGET_CC:?}" -static-libgcc "${args[@]}" - SH - chmod +x /tmp/ktav-static-unwind-linker - export CARGO_TARGET_X86_64_UNKNOWN_LINUX_MUSL_LINKER=/tmp/ktav-static-unwind-linker - export CARGO_TARGET_AARCH64_UNKNOWN_LINUX_MUSL_LINKER=/tmp/ktav-static-unwind-linker + test -x scripts/musllinux-unwind-linker.sh + export CARGO_TARGET_X86_64_UNKNOWN_LINUX_MUSL_LINKER="$GITHUB_WORKSPACE/scripts/musllinux-unwind-linker.sh" + export CARGO_TARGET_AARCH64_UNKNOWN_LINUX_MUSL_LINKER="$GITHUB_WORKSPACE/scripts/musllinux-unwind-linker.sh" cargo metadata --locked --format-version 1 | python3 -c 'import json,sys; d=json.load(sys.stdin); v=[p["version"] for p in d["packages"] if p["name"]=="pyo3"]; assert v==["0.28.3"],v' cargo generate-lockfile --manifest-path tests/fixtures/unwind-probe/Cargo.toml RUSTFLAGS="-C target-feature=-crt-static" cargo build --locked --release --manifest-path tests/fixtures/unwind-probe/Cargo.toml --target "${RUST_MUSL_CROSS_TARGET:?}" @@ -111,6 +111,7 @@ jobs: probe/_unwind_probe.so probe/Cargo.lock probe/libgcc-evidence.txt + probe/rustc.txt if-no-files-found: error musllinux-runtime: diff --git a/scripts/musllinux-unwind-linker.sh b/scripts/musllinux-unwind-linker.sh new file mode 100755 index 0000000..e9be86c --- /dev/null +++ b/scripts/musllinux-unwind-linker.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env bash +set -euo pipefail + +args=() +replaced=0 +for arg in "$@"; do + if [[ "$arg" == -lgcc_s ]]; then + args+=("-Wl,-Bstatic" "-lgcc_eh" "-Wl,-Bdynamic") + replaced=1 + else + args+=("$arg") + fi +done +if [[ "$replaced" != 1 ]]; then + printf '%s\n' 'expected -lgcc_s in target link' >&2 + exit 1 +fi +exec "${TARGET_CC:?}" -static-libgcc "${args[@]}" From c536572aa99d1c1a83f72e56fb3e526bbe61e0ca Mon Sep 17 00:00:00 2001 From: Arch-AI-tech Date: Thu, 29 Oct 2026 08:51:39 +0100 Subject: [PATCH 22/54] ci: verify musllinux wheel ABI and provenance Co-Authored-By: Claude Sonnet 5.5 --- .github/workflows/musllinux-evidence.yml | 66 ++++++++--------- scripts/check_musllinux_wheel.py | 92 ++++++++++++++++++++++++ scripts/musllinux-unwind-linker.sh | 6 ++ scripts/setup-musllinux-unwind.sh | 14 ++++ tests/ci/test_musllinux_wheel_check.py | 60 ++++++++++++++++ 5 files changed, 203 insertions(+), 35 deletions(-) create mode 100644 scripts/check_musllinux_wheel.py create mode 100644 scripts/setup-musllinux-unwind.sh create mode 100644 tests/ci/test_musllinux_wheel_check.py diff --git a/.github/workflows/musllinux-evidence.yml b/.github/workflows/musllinux-evidence.yml index e7b9bfe..983259d 100644 --- a/.github/workflows/musllinux-evidence.yml +++ b/.github/workflows/musllinux-evidence.yml @@ -27,6 +27,18 @@ jobs: - uses: dtolnay/rust-toolchain@1.99.1 - name: Resolve Cargo dependencies run: cargo generate-lockfile + - name: Check linker guard failures + shell: bash + run: | + set -euo pipefail + if TARGET_CC=/bin/false scripts/musllinux-unwind-linker.sh -lnot-gcc; then + printf '%s\n' 'linker accepted a missing gcc_s input' >&2 + exit 1 + fi + if RUST_MUSL_CROSS_TARGET=unknown bash -c 'source scripts/setup-musllinux-unwind.sh'; then + printf '%s\n' 'setup accepted an unknown target' >&2 + exit 1 + fi - name: Build musllinux wheel with static libgcc uses: PyO3/maturin-action@v1 with: @@ -36,16 +48,10 @@ jobs: container: ${{ matrix.image }} rust-toolchain: '1.99.1' before-script-linux: | + source scripts/setup-musllinux-unwind.sh archive="$("${TARGET_CC:?}" -print-file-name=libgcc_eh.a)" core_archive="$("${TARGET_CC}" -print-file-name=libgcc.a)" - test -f "$archive" test -f "$core_archive" - case "${RUST_MUSL_CROSS_TARGET:?}" in - x86_64-unknown-linux-musl) expected=975e964745276d7669f389473d5150223be491ff40217faf5676f98cd18e914a ;; - aarch64-unknown-linux-musl) expected=0ef3ef20cc85454372d957fddf83f5fcf9a18ac6166aee251d776e3765ef64c8 ;; - *) printf '%s\n' 'unexpected musl target' >&2; exit 1 ;; - esac - printf '%s %s\n' "$expected" "$archive" | sha256sum --check mkdir -p probe rustc --version --verbose > probe/rustc.txt grep -Fx 'release: 1.99.1' probe/rustc.txt @@ -58,9 +64,7 @@ jobs: "${TARGET_AR:?}" t "$library" done } > probe/libgcc-evidence.txt - test -x scripts/musllinux-unwind-linker.sh - export CARGO_TARGET_X86_64_UNKNOWN_LINUX_MUSL_LINKER="$GITHUB_WORKSPACE/scripts/musllinux-unwind-linker.sh" - export CARGO_TARGET_AARCH64_UNKNOWN_LINUX_MUSL_LINKER="$GITHUB_WORKSPACE/scripts/musllinux-unwind-linker.sh" + export KTAV_LINK_MAP_DIR="$GITHUB_WORKSPACE/probe/link-maps" cargo metadata --locked --format-version 1 | python3 -c 'import json,sys; d=json.load(sys.stdin); v=[p["version"] for p in d["packages"] if p["name"]=="pyo3"]; assert v==["0.28.3"],v' cargo generate-lockfile --manifest-path tests/fixtures/unwind-probe/Cargo.toml RUSTFLAGS="-C target-feature=-crt-static" cargo build --locked --release --manifest-path tests/fixtures/unwind-probe/Cargo.toml --target "${RUST_MUSL_CROSS_TARGET:?}" @@ -70,38 +74,29 @@ jobs: - name: Verify wheel native payload shell: python run: | - import os import re import subprocess - import zipfile + import sys from pathlib import Path wheels = list(Path('dist').glob('*.whl')) if len(wheels) != 1: raise SystemExit(f'expected one wheel, got {wheels!r}') - with zipfile.ZipFile(wheels[0]) as wheel: - native = [name for name in wheel.namelist() - if re.search(r'\.so(?:\.\d+)*$', name)] - if native != ['ktav/_core.abi3.so']: - raise SystemExit(f'unexpected wheel native payload: {native!r}') - module = Path(os.environ['RUNNER_TEMP']) / 'ktav-native.so' - module.write_bytes(wheel.read(native[0])) - for binary, expected in ( - (module, 'PyInit__core'), - (Path('probe/_unwind_probe.so'), 'PyInit__unwind_probe'), - ): - dynamic = subprocess.check_output(['readelf', '-d', str(binary)], text=True) - print(f'{binary}:\n{dynamic}') - if re.search(r'Shared library: \[libgcc_s(?:\.|-)', dynamic): - raise SystemExit(f'{binary} still dynamically requires libgcc_s') - symbols = subprocess.check_output( - ['readelf', '--dyn-syms', '--wide', str(binary)], text=True - ) - exports = [parts[7] for line in symbols.splitlines() if len(parts := line.split()) >= 8 - and parts[4] in ('GLOBAL', 'WEAK') and parts[5] in ('DEFAULT', 'PROTECTED') - and parts[6] != 'UND'] - if exports != [expected]: - raise SystemExit(f'{binary}: unexpected exported symbols: {exports!r}') + subprocess.run([sys.executable, 'scripts/check_musllinux_wheel.py', str(wheels[0])], check=True) + binary = Path('probe/_unwind_probe.so') + dynamic = subprocess.check_output(['readelf', '-d', str(binary)], text=True) + print(f'{binary}:\n{dynamic}') + if re.search(r'Shared library: \[libgcc_s(?:\.|-)', dynamic): + raise SystemExit(f'{binary} still dynamically requires libgcc_s') + symbols = subprocess.check_output(['readelf', '--dyn-syms', '--wide', str(binary)], text=True) + exports = [parts[7] for line in symbols.splitlines() if len(parts := line.split()) >= 8 + and parts[4] in ('GLOBAL', 'WEAK') and parts[5] in ('DEFAULT', 'PROTECTED') + and parts[6] != 'UND'] + if exports != ['PyInit__unwind_probe']: + raise SystemExit(f'{binary}: unexpected exported symbols: {exports!r}') + maps = list(Path('probe/link-maps').glob('link.*')) + if len(maps) < 2 or any('libgcc_eh.a' not in path.read_text() for path in maps): + raise SystemExit(f'missing static unwinder link maps: {maps!r}') - uses: actions/upload-artifact@v4 with: name: musllinux-static-libgcc-${{ matrix.target }} @@ -112,6 +107,7 @@ jobs: probe/Cargo.lock probe/libgcc-evidence.txt probe/rustc.txt + probe/link-maps/ if-no-files-found: error musllinux-runtime: diff --git a/scripts/check_musllinux_wheel.py b/scripts/check_musllinux_wheel.py new file mode 100644 index 0000000..3ea5249 --- /dev/null +++ b/scripts/check_musllinux_wheel.py @@ -0,0 +1,92 @@ +"""Проверить ABI и содержимое musllinux wheel.""" + +from __future__ import annotations + +import base64 +import csv +import hashlib +import io +import re +import subprocess +import sys +import tempfile +import zipfile +from pathlib import Path + +MODULE = "ktav/_core.abi3.so" +MAX_MODULE_BYTES = 64 * 1024 * 1024 + + +def _expected_machine(name: str) -> str: + if re.search(r"-musllinux_\d+_\d+_x86_64\.whl$", name): + return "Advanced Micro Devices X86-64" + if re.search(r"-musllinux_\d+_\d+_aarch64\.whl$", name): + return "AArch64" + raise ValueError(f"unexpected musllinux wheel name: {name}") + + +def _module_bytes(path: Path) -> bytes: + with zipfile.ZipFile(path) as wheel: + names = wheel.namelist() + native = [name for name in names if re.search(r"\.so(?:\.\d+)*$", name)] + if native != [MODULE]: + raise ValueError(f"unexpected wheel native payload: {native!r}") + info = wheel.getinfo(MODULE) + if info.file_size <= 0 or info.file_size > MAX_MODULE_BYTES: + raise ValueError("invalid native module size") + data = wheel.read(info) + records = [name for name in names if name.endswith(".dist-info/RECORD")] + if len(records) != 1 or records[0].count("/") != 1: + raise ValueError(f"unexpected wheel RECORD: {records!r}") + rows = list(csv.reader(io.StringIO(wheel.read(records[0]).decode("utf-8")))) + matching = [row for row in rows if row and row[0] == MODULE] + digest = base64.urlsafe_b64encode(hashlib.sha256(data).digest()).rstrip(b"=").decode() + if matching != [[MODULE, f"sha256={digest}", str(len(data))]]: + raise ValueError("native module does not match wheel RECORD") + return data + + +def _verify_elf(data: bytes, expected_machine: str) -> None: + with tempfile.TemporaryDirectory() as temp: + binary = Path(temp) / "native.so" + binary.write_bytes(data) + header = subprocess.check_output(["readelf", "-h", str(binary)], text=True) + machine = re.search(r"^\s*Machine:\s*(.+)$", header, re.MULTILINE) + if not machine or machine.group(1).strip() != expected_machine: + raise ValueError("unexpected ELF machine") + dynamic = subprocess.check_output(["readelf", "-d", str(binary)], text=True) + needed = re.findall(r"Shared library: \[([^]]+)\]", dynamic) + if needed != ["libc.so"]: + raise ValueError(f"unexpected shared libraries: {needed!r}") + versions = subprocess.check_output(["readelf", "--version-info", str(binary)], text=True) + if re.search(r"\bGLIBC_[0-9]", versions): + raise ValueError("glibc symbol version in musllinux module") + symbols = subprocess.check_output( + ["readelf", "--dyn-syms", "--wide", str(binary)], text=True + ) + exports = [ + parts[7] + for line in symbols.splitlines() + if len(parts := line.split()) >= 8 + and parts[4] in ("GLOBAL", "WEAK") + and parts[5] in ("DEFAULT", "PROTECTED") + and parts[6] != "UND" + ] + if exports != ["PyInit__core"]: + raise ValueError(f"unexpected exported symbols: {exports!r}") + + +def main() -> None: + """Проверить wheel, ELF-зависимости и экспортируемые символы.""" + if len(sys.argv) != 2: + raise SystemExit("usage: check_musllinux_wheel.py WHEEL") + try: + wheel = Path(sys.argv[1]) + _verify_elf(_module_bytes(wheel), _expected_machine(wheel.name)) + except (OSError, ValueError, subprocess.CalledProcessError, zipfile.BadZipFile) as exc: + raise SystemExit(str(exc)) from exc + print(f"musllinux wheel ABI verified: {wheel.name}") + + +if __name__ == "__main__": + main() diff --git a/scripts/musllinux-unwind-linker.sh b/scripts/musllinux-unwind-linker.sh index e9be86c..b640509 100755 --- a/scripts/musllinux-unwind-linker.sh +++ b/scripts/musllinux-unwind-linker.sh @@ -15,4 +15,10 @@ if [[ "$replaced" != 1 ]]; then printf '%s\n' 'expected -lgcc_s in target link' >&2 exit 1 fi +if [[ -n "${KTAV_LINK_MAP_DIR:-}" ]]; then + mkdir -p "$KTAV_LINK_MAP_DIR" + map="$(mktemp "$KTAV_LINK_MAP_DIR/link.XXXXXXXX")" + chmod 644 "$map" + args+=("-Wl,-Map=$map") +fi exec "${TARGET_CC:?}" -static-libgcc "${args[@]}" diff --git a/scripts/setup-musllinux-unwind.sh b/scripts/setup-musllinux-unwind.sh new file mode 100644 index 0000000..f9ee123 --- /dev/null +++ b/scripts/setup-musllinux-unwind.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash +set -euo pipefail + +case "${RUST_MUSL_CROSS_TARGET:?}" in + x86_64-unknown-linux-musl) expected=975e964745276d7669f389473d5150223be491ff40217faf5676f98cd18e914a ;; + aarch64-unknown-linux-musl) expected=0ef3ef20cc85454372d957fddf83f5fcf9a18ac6166aee251d776e3765ef64c8 ;; + *) printf '%s\n' 'unexpected musllinux target' >&2; return 1 ;; +esac +archive="$("${TARGET_CC:?}" -print-file-name=libgcc_eh.a)" +test -f "$archive" +printf '%s %s\n' "$expected" "$archive" | sha256sum --check +test -x "$GITHUB_WORKSPACE/scripts/musllinux-unwind-linker.sh" +export CARGO_TARGET_X86_64_UNKNOWN_LINUX_MUSL_LINKER="$GITHUB_WORKSPACE/scripts/musllinux-unwind-linker.sh" +export CARGO_TARGET_AARCH64_UNKNOWN_LINUX_MUSL_LINKER="$GITHUB_WORKSPACE/scripts/musllinux-unwind-linker.sh" diff --git a/tests/ci/test_musllinux_wheel_check.py b/tests/ci/test_musllinux_wheel_check.py new file mode 100644 index 0000000..2442e5e --- /dev/null +++ b/tests/ci/test_musllinux_wheel_check.py @@ -0,0 +1,60 @@ +"""Проверить защиту musllinux wheel от неверного состава.""" + +from __future__ import annotations + +import base64 +import csv +import hashlib +import io +import subprocess +import sys +import zipfile +from pathlib import Path + +import pytest + +ROOT = Path(__file__).resolve().parents[2] +CHECKER = ROOT / "scripts" / "check_musllinux_wheel.py" +MODULE = "ktav/_core.abi3.so" + + +def _wheel(tmp_path: Path, *, extra: str | None = None, corrupt: bool = False) -> Path: + path = tmp_path / "ktav-0.8.1-cp39-abi3-musllinux_1_2_x86_64.whl" + data = b"not an ELF file" + digest = base64.urlsafe_b64encode(hashlib.sha256(data).digest()).rstrip(b"=").decode() + record = io.StringIO(newline="") + csv.writer(record, lineterminator="\n").writerow( + [MODULE, "sha256=wrong" if corrupt else f"sha256={digest}", str(len(data))] + ) + with zipfile.ZipFile(path, "w") as archive: + archive.writestr(MODULE, data) + archive.writestr("ktav-0.8.1.dist-info/RECORD", record.getvalue()) + if extra: + archive.writestr(extra, b"additional binary") + return path + + +def _run(path: Path) -> subprocess.CompletedProcess[str]: + return subprocess.run([sys.executable, str(CHECKER), str(path)], capture_output=True, text=True) + + +def test_rejects_bundled_libgcc(tmp_path: Path) -> None: + result = _run(_wheel(tmp_path, extra="ktav.libs/libgcc_s-abcd.so.1")) + assert result.returncode != 0 + assert "unexpected wheel native payload" in result.stderr + + +def test_rejects_corrupt_record(tmp_path: Path) -> None: + result = _run(_wheel(tmp_path, corrupt=True)) + assert result.returncode != 0 + assert "does not match wheel RECORD" in result.stderr + + +def test_rejects_duplicate_native_module(tmp_path: Path) -> None: + path = _wheel(tmp_path) + with pytest.warns(UserWarning, match="Duplicate name"): + with zipfile.ZipFile(path, "a") as archive: + archive.writestr(MODULE, b"duplicate") + result = _run(path) + assert result.returncode != 0 + assert "unexpected wheel native payload" in result.stderr From e4957dd1996ce7c6686f59fed91db370eba0a7a3 Mon Sep 17 00:00:00 2001 From: Arch-AI-tech Date: Thu, 29 Oct 2026 09:04:54 +0100 Subject: [PATCH 23/54] test: fix musllinux verifier lint Co-Authored-By: Claude Sonnet 5.5 --- tests/ci/test_musllinux_wheel_check.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/ci/test_musllinux_wheel_check.py b/tests/ci/test_musllinux_wheel_check.py index 2442e5e..9130a36 100644 --- a/tests/ci/test_musllinux_wheel_check.py +++ b/tests/ci/test_musllinux_wheel_check.py @@ -52,9 +52,8 @@ def test_rejects_corrupt_record(tmp_path: Path) -> None: def test_rejects_duplicate_native_module(tmp_path: Path) -> None: path = _wheel(tmp_path) - with pytest.warns(UserWarning, match="Duplicate name"): - with zipfile.ZipFile(path, "a") as archive: - archive.writestr(MODULE, b"duplicate") + with pytest.warns(UserWarning, match="Duplicate name"), zipfile.ZipFile(path, "a") as archive: + archive.writestr(MODULE, b"duplicate") result = _run(path) assert result.returncode != 0 assert "unexpected wheel native payload" in result.stderr From 0bea71d984f49a027835aa0644048c69a7a6e11d Mon Sep 17 00:00:00 2001 From: Arch-AI-tech Date: Thu, 29 Oct 2026 09:34:28 +0100 Subject: [PATCH 24/54] ci: reject hidden native wheel payloads Inspect binary signatures as well as native file extensions so an extensionless runtime library cannot bypass the musllinux inventory. Cover additional native formats and extensionless binaries. Co-Authored-By: Claude Opus 5.5 --- scripts/check_musllinux_wheel.py | 28 +++++++++++++++++++++++++- tests/ci/test_musllinux_wheel_check.py | 23 +++++++++++++++++++-- 2 files changed, 48 insertions(+), 3 deletions(-) diff --git a/scripts/check_musllinux_wheel.py b/scripts/check_musllinux_wheel.py index 3ea5249..d2827a7 100644 --- a/scripts/check_musllinux_wheel.py +++ b/scripts/check_musllinux_wheel.py @@ -28,7 +28,33 @@ def _expected_machine(name: str) -> str: def _module_bytes(path: Path) -> bytes: with zipfile.ZipFile(path) as wheel: names = wheel.namelist() - native = [name for name in names if re.search(r"\.so(?:\.\d+)*$", name)] + native = [] + for info in wheel.infolist(): + if info.is_dir(): + continue + with wheel.open(info) as source: + magic = source.read(8) + binary_name = re.search( + r"(?:\.so(?:\.\d+)*|\.pyd|\.dll|\.dylib|\.node|\.a|\.lib|\.exe)$", + info.filename, + re.IGNORECASE, + ) + binary_magic = magic.startswith( + ( + b"\x7fELF", + b"MZ", + b"\x00asm", + b"!\n", + b"\xcf\xfa\xed\xfe", + b"\xfe\xed\xfa\xcf", + b"\xce\xfa\xed\xfe", + b"\xfe\xed\xfa\xce", + b"\xca\xfe\xba\xbe", + b"\xbe\xba\xfe\xca", + ) + ) + if binary_name or binary_magic: + native.append(info.filename) if native != [MODULE]: raise ValueError(f"unexpected wheel native payload: {native!r}") info = wheel.getinfo(MODULE) diff --git a/tests/ci/test_musllinux_wheel_check.py b/tests/ci/test_musllinux_wheel_check.py index 9130a36..c2729ea 100644 --- a/tests/ci/test_musllinux_wheel_check.py +++ b/tests/ci/test_musllinux_wheel_check.py @@ -18,7 +18,9 @@ MODULE = "ktav/_core.abi3.so" -def _wheel(tmp_path: Path, *, extra: str | None = None, corrupt: bool = False) -> Path: +def _wheel( + tmp_path: Path, *, extra: str | None = None, extra_data: bytes = b"extra", corrupt: bool = False +) -> Path: path = tmp_path / "ktav-0.8.1-cp39-abi3-musllinux_1_2_x86_64.whl" data = b"not an ELF file" digest = base64.urlsafe_b64encode(hashlib.sha256(data).digest()).rstrip(b"=").decode() @@ -30,7 +32,7 @@ def _wheel(tmp_path: Path, *, extra: str | None = None, corrupt: bool = False) - archive.writestr(MODULE, data) archive.writestr("ktav-0.8.1.dist-info/RECORD", record.getvalue()) if extra: - archive.writestr(extra, b"additional binary") + archive.writestr(extra, extra_data) return path @@ -44,6 +46,23 @@ def test_rejects_bundled_libgcc(tmp_path: Path) -> None: assert "unexpected wheel native payload" in result.stderr +@pytest.mark.parametrize( + "name", + ["ktav/hidden.dll", "ktav/hidden.pyd", "ktav/hidden.dylib", "ktav/hidden.a"], +) +def test_rejects_other_native_formats(tmp_path: Path, name: str) -> None: + result = _run(_wheel(tmp_path, extra=name)) + assert result.returncode != 0 + assert "unexpected wheel native payload" in result.stderr + + +@pytest.mark.parametrize("magic", [b"\x7fELF", b"MZ", b"\x00asm", b"!\n"]) +def test_rejects_extensionless_binary(tmp_path: Path, magic: bytes) -> None: + result = _run(_wheel(tmp_path, extra="ktav/hidden", extra_data=magic + b"fake")) + assert result.returncode != 0 + assert "unexpected wheel native payload" in result.stderr + + def test_rejects_corrupt_record(tmp_path: Path) -> None: result = _run(_wheel(tmp_path, corrupt=True)) assert result.returncode != 0 From e2c9b3e12bedd1920924aa35f412685fc4ba0be6 Mon Sep 17 00:00:00 2001 From: Arch-AI-tech Date: Thu, 29 Oct 2026 09:39:16 +0100 Subject: [PATCH 25/54] ci: verify Cargo notice bundles inside wheels Generate a deterministic bundle of source-backed Cargo license texts, additional notices and the actual toolchain's Rust copyright report. Build a wheel with the same lock and check exact bundle bytes and RECORD. Keep local source paths out of the bundle. This proves packaging for the conservative CI Cargo graph, not linked code coverage or licensing of non-Cargo runtimes. Production release integration remains separate. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/licenses.yml | 18 ++++ scripts/cargo_notice_bundle.py | 111 ++++++++++++++++++++ tests/ci/test_cargo_notice_bundle.py | 145 +++++++++++++++++++++++++++ 3 files changed, 274 insertions(+) create mode 100644 scripts/cargo_notice_bundle.py create mode 100644 tests/ci/test_cargo_notice_bundle.py diff --git a/.github/workflows/licenses.yml b/.github/workflows/licenses.yml index 3b7048d..99e1fbe 100644 --- a/.github/workflows/licenses.yml +++ b/.github/workflows/licenses.yml @@ -76,6 +76,24 @@ jobs: "$RUNNER_TEMP/cargo-license-evidence/metadata.json" \ "$RUNNER_TEMP/cargo-license-evidence/licenses.json" \ "$RUNNER_TEMP/cargo-license-evidence/notices.json" + - uses: actions/setup-python@v5 + with: + python-version: '3.12' + - name: Build and verify a wheel carrying the exact notice bundle + shell: bash + run: | + set -euo pipefail + python -m pip install 'maturin>=1.7,<2.0' pytest + python -m pytest -q tests/ci/test_cargo_notice_bundle.py + evidence="$RUNNER_TEMP/cargo-license-evidence" + bundle="python/ktav/_third_party/NOTICE.json" + python scripts/cargo_notice_bundle.py build "$evidence" "$bundle" + cp "$bundle" "$evidence/NOTICE.json" + maturin build --release --locked --out "$evidence/wheels" + cmp Cargo.lock "$evidence/Cargo.lock" + for wheel in "$evidence"/wheels/*.whl; do + python scripts/cargo_notice_bundle.py verify "$wheel" "$evidence/NOTICE.json" + done - uses: actions/upload-artifact@v4 if: always() with: diff --git a/scripts/cargo_notice_bundle.py b/scripts/cargo_notice_bundle.py new file mode 100644 index 0000000..144408d --- /dev/null +++ b/scripts/cargo_notice_bundle.py @@ -0,0 +1,111 @@ +"""Собрать notices без локальных путей и проверить их в wheel.""" + +from __future__ import annotations + +import base64 +import csv +import hashlib +import io +import json +import sys +import zipfile +from pathlib import Path, PurePosixPath + +from collect_cargo_notices import _collect, _packages + +MEMBER = "ktav/_third_party/NOTICE.json" +MAX_BUNDLE_BYTES = 64 * 1024 * 1024 + + +def _text(data: bytes) -> dict: + return {"sha256": hashlib.sha256(data).hexdigest(), "text": data.decode("utf-8")} + + +def _package(package: dict) -> dict: + return {key: package.get(key) for key in ("name", "version", "source")} + + +def _build(evidence: Path) -> bytes: + metadata = json.loads((evidence / "metadata.json").read_text(encoding="utf-8")) + report = json.loads((evidence / "licenses.json").read_text(encoding="utf-8")) + packages = _packages(metadata, report) + identities = {package["id"]: _package(package) for package in packages} + licenses = [] + for entry in report["licenses"]: + used_by = [identities[item["crate"]["id"]] for item in entry["used_by"]] + licenses.append( + { + **_text(entry["text"].encode("utf-8")), + "used_by": sorted(used_by, key=lambda item: json.dumps(item, sort_keys=True)), + } + ) + notices = _collect(metadata, report) + for entry in notices["notices"]: + path = PurePosixPath(entry["path"]) + if path.is_absolute() or ".." in path.parts or "\\" in entry["path"]: + raise ValueError("unsafe notice path") + rustc = (evidence / "rustc.txt").read_bytes() + if not rustc.startswith(b"rustc ") or b"\ncommit-hash: " not in rustc: + raise ValueError("missing verbose rustc provenance") + stdlib = (evidence / "COPYRIGHT-library.html").read_bytes() + if not stdlib.strip(): + raise ValueError("empty Rust standard library report") + lock = (evidence / "Cargo.lock").read_bytes() + if not lock.strip(): + raise ValueError("empty Cargo.lock") + result = { + "schema_version": 1, + "coverage": "Resolved Cargo graph and Rust standard library; not a linked-code inventory. " + "Non-Cargo runtimes require separate evidence.", + "cargo_lock_sha256": hashlib.sha256(lock).hexdigest(), + "packages": sorted(identities.values(), key=lambda item: json.dumps(item, sort_keys=True)), + "licenses": sorted(licenses, key=lambda item: json.dumps(item, sort_keys=True)), + "notices": notices["notices"], + "rustc": rustc.decode("utf-8"), + "rust_standard_library": _text(stdlib), + } + data = (json.dumps(result, ensure_ascii=False, sort_keys=True, indent=2) + "\n").encode() + if len(data) > MAX_BUNDLE_BYTES: + raise ValueError("notice bundle size limit exceeded") + return data + + +def _verify(wheel_path: Path, expected: bytes) -> None: + if not expected or len(expected) > MAX_BUNDLE_BYTES: + raise ValueError("invalid expected bundle size") + with zipfile.ZipFile(wheel_path) as wheel: + members = [item for item in wheel.infolist() if item.filename == MEMBER] + if len(members) != 1 or members[0].file_size != len(expected): + raise ValueError("missing, duplicate or wrong-sized wheel notice bundle") + if wheel.read(members[0]) != expected: + raise ValueError("wheel notice bundle differs from build evidence") + records = [item for item in wheel.infolist() if item.filename.endswith(".dist-info/RECORD")] + if len(records) != 1 or records[0].filename.count("/") != 1: + raise ValueError("unexpected wheel RECORD") + rows = csv.reader(io.StringIO(wheel.read(records[0]).decode("utf-8"))) + matching = [row for row in rows if row and row[0] == MEMBER] + digest = base64.urlsafe_b64encode(hashlib.sha256(expected).digest()).rstrip(b"=").decode() + if matching != [[MEMBER, f"sha256={digest}", str(len(expected))]]: + raise ValueError("notice bundle does not match wheel RECORD") + + +def main() -> None: + """Собрать bundle или сверить точные байты упакованного файла.""" + if len(sys.argv) != 4 or sys.argv[1] not in {"build", "verify"}: + raise SystemExit("usage: cargo_notice_bundle.py build EVIDENCE OUTPUT | verify WHEEL BUNDLE") + try: + if sys.argv[1] == "build": + data = _build(Path(sys.argv[2])) + output = Path(sys.argv[3]) + output.parent.mkdir(parents=True, exist_ok=True) + with output.open("xb") as destination: + destination.write(data) + else: + _verify(Path(sys.argv[2]), Path(sys.argv[3]).read_bytes()) + except (OSError, ValueError, KeyError, TypeError, zipfile.BadZipFile) as exc: + raise SystemExit(str(exc)) from exc + print(f"notice bundle {sys.argv[1]} verified") + + +if __name__ == "__main__": + main() diff --git a/tests/ci/test_cargo_notice_bundle.py b/tests/ci/test_cargo_notice_bundle.py new file mode 100644 index 0000000..4019f31 --- /dev/null +++ b/tests/ci/test_cargo_notice_bundle.py @@ -0,0 +1,145 @@ +"""Проверить bundle и его присутствие в фактическом wheel.""" + +from __future__ import annotations + +import base64 +import csv +import hashlib +import io +import json +import subprocess +import sys +import zipfile +from pathlib import Path + +import pytest + +ROOT = Path(__file__).resolve().parents[2] +SCRIPT = ROOT / "scripts" / "cargo_notice_bundle.py" +MEMBER = "ktav/_third_party/NOTICE.json" + + +def _run(*args: str | Path) -> subprocess.CompletedProcess[str]: + return subprocess.run( + [sys.executable, str(SCRIPT), *map(str, args)], capture_output=True, text=True + ) + + +def _evidence(tmp_path: Path) -> Path: + source = tmp_path / "source" + source.mkdir() + (source / "NOTICE").write_bytes(b"Third-party copyright\r\n") + package = { + "id": "registry+example#fixture@1.0.0", + "name": "fixture", + "version": "1.0.0", + "source": "registry+example", + "manifest_path": str(source / "Cargo.toml"), + } + evidence = tmp_path / "evidence" + evidence.mkdir() + (evidence / "metadata.json").write_text(json.dumps({"packages": [package]}), encoding="utf-8") + report = { + "crates": [{"package": {"id": package["id"]}}], + "licenses": [ + { + "source_path": str(source / "LICENSE"), + "text": "MIT copyright\n", + "used_by": [{"crate": package}], + } + ], + } + (evidence / "licenses.json").write_text(json.dumps(report), encoding="utf-8") + (evidence / "Cargo.lock").write_bytes(b"version = 4\n") + (evidence / "rustc.txt").write_bytes(b"rustc fixture\ncommit-hash: abc\n") + (evidence / "COPYRIGHT-library.html").write_bytes(b"Rust copyright\r\n") + return evidence + + +def _wheel(tmp_path: Path, data: bytes, *, corrupt_record: bool = False) -> Path: + path = tmp_path / "fixture.whl" + record = io.StringIO(newline="") + digest = base64.urlsafe_b64encode(hashlib.sha256(data).digest()).rstrip(b"=").decode() + csv.writer(record, lineterminator="\n").writerow( + [MEMBER, "sha256=wrong" if corrupt_record else f"sha256={digest}", str(len(data))] + ) + with zipfile.ZipFile(path, "w") as wheel: + wheel.writestr(MEMBER, data) + wheel.writestr("ktav-1.0.dist-info/RECORD", record.getvalue()) + return path + + +def test_build_preserves_text_and_omits_local_paths(tmp_path: Path) -> None: + evidence = _evidence(tmp_path) + first = tmp_path / "first.json" + second = tmp_path / "second.json" + for output in (first, second): + result = _run("build", evidence, output) + assert result.returncode == 0, result.stderr + assert first.read_bytes() == second.read_bytes() + bundle = json.loads(first.read_bytes()) + assert str(tmp_path) not in first.read_text(encoding="utf-8") + assert "manifest_path" not in bundle["packages"][0] + assert bundle["licenses"][0]["text"] == "MIT copyright\n" + assert bundle["notices"][0]["text"] == "Third-party copyright\r\n" + assert bundle["rust_standard_library"]["text"] == "Rust copyright\r\n" + assert bundle["cargo_lock_sha256"] == hashlib.sha256(b"version = 4\n").hexdigest() + assert "not a linked-code inventory" in bundle["coverage"] + result = _run("verify", _wheel(tmp_path, first.read_bytes()), first) + assert result.returncode == 0, result.stderr + + +def test_build_rejects_fallback(tmp_path: Path) -> None: + evidence = _evidence(tmp_path) + path = evidence / "licenses.json" + report = json.loads(path.read_text(encoding="utf-8")) + report["licenses"][0]["source_path"] = None + path.write_text(json.dumps(report), encoding="utf-8") + output = tmp_path / "bundle.json" + assert _run("build", evidence, output).returncode != 0 + assert not output.exists() + + +@pytest.mark.parametrize("name", ["Cargo.lock", "rustc.txt", "COPYRIGHT-library.html"]) +def test_build_rejects_empty_provenance(tmp_path: Path, name: str) -> None: + evidence = _evidence(tmp_path) + (evidence / name).write_bytes(b"") + assert _run("build", evidence, tmp_path / "bundle.json").returncode != 0 + + +def test_verify_rejects_modified_bundle(tmp_path: Path) -> None: + expected = tmp_path / "expected.json" + expected.write_bytes(b"original") + result = _run("verify", _wheel(tmp_path, b"modified"), expected) + assert result.returncode != 0 + assert "differs from build evidence" in result.stderr + + +def test_verify_rejects_corrupt_record(tmp_path: Path) -> None: + expected = tmp_path / "expected.json" + expected.write_bytes(b"bundle") + result = _run("verify", _wheel(tmp_path, b"bundle", corrupt_record=True), expected) + assert result.returncode != 0 + assert "does not match wheel RECORD" in result.stderr + + +def test_verify_rejects_missing_bundle(tmp_path: Path) -> None: + expected = tmp_path / "expected.json" + expected.write_bytes(b"bundle") + path = tmp_path / "empty.whl" + with zipfile.ZipFile(path, "w"): + pass + result = _run("verify", path, expected) + assert result.returncode != 0 + assert "missing, duplicate or wrong-sized" in result.stderr + + +def test_verify_rejects_duplicate_bundle(tmp_path: Path) -> None: + expected = tmp_path / "expected.json" + expected.write_bytes(b"bundle") + path = _wheel(tmp_path, b"bundle") + with pytest.warns(UserWarning, match="Duplicate name"), zipfile.ZipFile(path, "a") as wheel: + wheel.writestr(MEMBER, b"bundle") + result = _run("verify", path, expected) + assert result.returncode != 0 + assert "missing, duplicate or wrong-sized" in result.stderr From 5bc6ad38a2256750c83a8bacff3fe5464ab86d7a Mon Sep 17 00:00:00 2001 From: Arch-AI-tech Date: Thu, 29 Oct 2026 09:45:50 +0100 Subject: [PATCH 26/54] test: isolate notice bundle CLI checks Avoid loading the native binding conftest before the evidence wheel is built. Keep full binding tests in the main CI matrix. Fix Ruff line length and ambiguous docstring characters. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/licenses.yml | 2 +- scripts/cargo_notice_bundle.py | 4 +++- tests/ci/test_cargo_notice_bundle.py | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/licenses.yml b/.github/workflows/licenses.yml index 99e1fbe..76731ab 100644 --- a/.github/workflows/licenses.yml +++ b/.github/workflows/licenses.yml @@ -84,7 +84,7 @@ jobs: run: | set -euo pipefail python -m pip install 'maturin>=1.7,<2.0' pytest - python -m pytest -q tests/ci/test_cargo_notice_bundle.py + python -m pytest --confcutdir=tests/ci -q tests/ci/test_cargo_notice_bundle.py evidence="$RUNNER_TEMP/cargo-license-evidence" bundle="python/ktav/_third_party/NOTICE.json" python scripts/cargo_notice_bundle.py build "$evidence" "$bundle" diff --git a/scripts/cargo_notice_bundle.py b/scripts/cargo_notice_bundle.py index 144408d..7196de5 100644 --- a/scripts/cargo_notice_bundle.py +++ b/scripts/cargo_notice_bundle.py @@ -92,7 +92,9 @@ def _verify(wheel_path: Path, expected: bytes) -> None: def main() -> None: """Собрать bundle или сверить точные байты упакованного файла.""" if len(sys.argv) != 4 or sys.argv[1] not in {"build", "verify"}: - raise SystemExit("usage: cargo_notice_bundle.py build EVIDENCE OUTPUT | verify WHEEL BUNDLE") + raise SystemExit( + "usage: cargo_notice_bundle.py build EVIDENCE OUTPUT | verify WHEEL BUNDLE" + ) try: if sys.argv[1] == "build": data = _build(Path(sys.argv[2])) diff --git a/tests/ci/test_cargo_notice_bundle.py b/tests/ci/test_cargo_notice_bundle.py index 4019f31..3a3b7f8 100644 --- a/tests/ci/test_cargo_notice_bundle.py +++ b/tests/ci/test_cargo_notice_bundle.py @@ -1,4 +1,4 @@ -"""Проверить bundle и его присутствие в фактическом wheel.""" +"""Проверить упаковку лицензионных уведомлений внутри wheel.""" from __future__ import annotations From f2588172d048eca0b4c1cc6b29acdb498bd34144 Mon Sep 17 00:00:00 2001 From: Arch-AI-tech Date: Thu, 29 Oct 2026 09:59:09 +0100 Subject: [PATCH 27/54] ci: verify notices in both musllinux wheels Generate the conservative Cargo and Rust std notice bundle from the locked graph before building each musllinux wheel. Require the container compiler provenance and lock to match the inputs, then verify exact bundle bytes and RECORD in the actual wheel. Retain ABI, panic and native corpus checks. This does not cover the separate GCC runtime licensing assessment or change production release configuration. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/musllinux-evidence.yml | 31 ++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/.github/workflows/musllinux-evidence.yml b/.github/workflows/musllinux-evidence.yml index 983259d..80ade52 100644 --- a/.github/workflows/musllinux-evidence.yml +++ b/.github/workflows/musllinux-evidence.yml @@ -25,8 +25,28 @@ jobs: with: python-version: '3.12' - uses: dtolnay/rust-toolchain@1.99.1 + with: + components: rust-docs - name: Resolve Cargo dependencies run: cargo generate-lockfile + - uses: taiki-e/install-action@v2 + with: + tool: cargo-about@0.9.3 + - name: Prepare notices for the locked build + shell: bash + run: | + set -euo pipefail + evidence="$RUNNER_TEMP/musllinux-notices" + mkdir -p "$evidence" probe + cp Cargo.lock "$evidence/Cargo.lock" + rustc --version --verbose > "$evidence/rustc.txt" + cp "$(rustc --print sysroot)/share/doc/rust/COPYRIGHT-library.html" "$evidence/COPYRIGHT-library.html" + cargo metadata --locked --all-features --format-version 1 > "$evidence/metadata.json" + cargo about generate --locked --all-features --fail --config ci/about.toml --format json > "$evidence/licenses.json" + python scripts/cargo_notice_bundle.py build "$evidence" python/ktav/_third_party/NOTICE.json + cp python/ktav/_third_party/NOTICE.json probe/NOTICE.json + cp "$evidence/rustc.txt" probe/bundle-rustc.txt + cp "$evidence/Cargo.lock" probe/bundle-Cargo.lock - name: Check linker guard failures shell: bash run: | @@ -54,6 +74,8 @@ jobs: test -f "$core_archive" mkdir -p probe rustc --version --verbose > probe/rustc.txt + cmp probe/rustc.txt probe/bundle-rustc.txt + cmp Cargo.lock probe/bundle-Cargo.lock grep -Fx 'release: 1.99.1' probe/rustc.txt grep -Fx 'commit-hash: 58cf8f9c35b355199727627d07ae34a4e2846d67' probe/rustc.txt { @@ -83,6 +105,12 @@ jobs: if len(wheels) != 1: raise SystemExit(f'expected one wheel, got {wheels!r}') subprocess.run([sys.executable, 'scripts/check_musllinux_wheel.py', str(wheels[0])], check=True) + subprocess.run([ + sys.executable, 'scripts/cargo_notice_bundle.py', 'verify', + str(wheels[0]), 'probe/NOTICE.json', + ], check=True) + if Path('Cargo.lock').read_bytes() != Path('probe/bundle-Cargo.lock').read_bytes(): + raise SystemExit('Cargo.lock changed after notice generation') binary = Path('probe/_unwind_probe.so') dynamic = subprocess.check_output(['readelf', '-d', str(binary)], text=True) print(f'{binary}:\n{dynamic}') @@ -107,6 +135,9 @@ jobs: probe/Cargo.lock probe/libgcc-evidence.txt probe/rustc.txt + probe/NOTICE.json + probe/bundle-rustc.txt + probe/bundle-Cargo.lock probe/link-maps/ if-no-files-found: error From eb2fd18d30a511c339473482f86ff4873885184a Mon Sep 17 00:00:00 2001 From: Arch-AI-tech Date: Thu, 29 Oct 2026 10:21:33 +0100 Subject: [PATCH 28/54] ci: test a source-pinned LLVM musl unwinder Build only static PIC libunwind from LLVM 20.1.8's exact source commit. Verify the checkout is unchanged and record the original license and archive hash inside the wheel's notice bundle. Replace Rust's explicit gcc_s link with that archive, retaining panic unwinding and native tests. Reject linked GCC runtime archive members in both link maps. This is a CI-only alternative to the source-unverified GCC unwinder. Historical packages and production release configuration are unchanged. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/musllinux-evidence.yml | 46 ++++++++++------ scripts/add_llvm_unwind_notice.py | 59 +++++++++++++++++++++ scripts/musllinux-unwind-linker.sh | 3 +- scripts/setup-llvm-unwind.sh | 34 ++++++++++++ scripts/setup-musllinux-unwind.sh | 14 ----- tests/ci/test_llvm_unwind_notice.py | 67 ++++++++++++++++++++++++ 6 files changed, 192 insertions(+), 31 deletions(-) create mode 100644 scripts/add_llvm_unwind_notice.py create mode 100644 scripts/setup-llvm-unwind.sh delete mode 100644 scripts/setup-musllinux-unwind.sh create mode 100644 tests/ci/test_llvm_unwind_notice.py diff --git a/.github/workflows/musllinux-evidence.yml b/.github/workflows/musllinux-evidence.yml index 80ade52..b21c513 100644 --- a/.github/workflows/musllinux-evidence.yml +++ b/.github/workflows/musllinux-evidence.yml @@ -8,7 +8,7 @@ permissions: jobs: static-libgcc: - name: Musllinux static libgcc (${{ matrix.target }}) + name: Musllinux source-built unwinder (${{ matrix.target }}) runs-on: ubuntu-latest strategy: fail-fast: false @@ -47,6 +47,18 @@ jobs: cp python/ktav/_third_party/NOTICE.json probe/NOTICE.json cp "$evidence/rustc.txt" probe/bundle-rustc.txt cp "$evidence/Cargo.lock" probe/bundle-Cargo.lock + - uses: actions/checkout@v4 + with: + repository: llvm/llvm-project + ref: 87f0227cb60147a26a1eeb4fb06e3b505e9c7261 + path: llvm-unwind-source + persist-credentials: false + sparse-checkout: | + cmake + runtimes + llvm/cmake + libcxx/include + libunwind - name: Check linker guard failures shell: bash run: | @@ -55,11 +67,11 @@ jobs: printf '%s\n' 'linker accepted a missing gcc_s input' >&2 exit 1 fi - if RUST_MUSL_CROSS_TARGET=unknown bash -c 'source scripts/setup-musllinux-unwind.sh'; then + if RUST_MUSL_CROSS_TARGET=unknown bash -c 'source scripts/setup-llvm-unwind.sh'; then printf '%s\n' 'setup accepted an unknown target' >&2 exit 1 fi - - name: Build musllinux wheel with static libgcc + - name: Build musllinux wheel with source-built LLVM unwinder uses: PyO3/maturin-action@v1 with: target: ${{ matrix.target }} @@ -68,10 +80,8 @@ jobs: container: ${{ matrix.image }} rust-toolchain: '1.99.1' before-script-linux: | - source scripts/setup-musllinux-unwind.sh - archive="$("${TARGET_CC:?}" -print-file-name=libgcc_eh.a)" - core_archive="$("${TARGET_CC}" -print-file-name=libgcc.a)" - test -f "$core_archive" + source scripts/setup-llvm-unwind.sh + archive="${KTAV_UNWIND_ARCHIVE:?}" mkdir -p probe rustc --version --verbose > probe/rustc.txt cmp probe/rustc.txt probe/bundle-rustc.txt @@ -81,11 +91,10 @@ jobs: { "${TARGET_CC}" --version "${TARGET_CC}" -v 2>&1 - for library in "$archive" "$core_archive"; do - sha256sum "$library" - "${TARGET_AR:?}" t "$library" - done - } > probe/libgcc-evidence.txt + cmake --version + sha256sum "$archive" + "${TARGET_AR:?}" t "$archive" + } > probe/unwinder-evidence.txt export KTAV_LINK_MAP_DIR="$GITHUB_WORKSPACE/probe/link-maps" cargo metadata --locked --format-version 1 | python3 -c 'import json,sys; d=json.load(sys.stdin); v=[p["version"] for p in d["packages"] if p["name"]=="pyo3"]; assert v==["0.28.3"],v' cargo generate-lockfile --manifest-path tests/fixtures/unwind-probe/Cargo.toml @@ -107,7 +116,7 @@ jobs: subprocess.run([sys.executable, 'scripts/check_musllinux_wheel.py', str(wheels[0])], check=True) subprocess.run([ sys.executable, 'scripts/cargo_notice_bundle.py', 'verify', - str(wheels[0]), 'probe/NOTICE.json', + str(wheels[0]), 'probe/NOTICE-LLVM.json', ], check=True) if Path('Cargo.lock').read_bytes() != Path('probe/bundle-Cargo.lock').read_bytes(): raise SystemExit('Cargo.lock changed after notice generation') @@ -123,8 +132,11 @@ jobs: if exports != ['PyInit__unwind_probe']: raise SystemExit(f'{binary}: unexpected exported symbols: {exports!r}') maps = list(Path('probe/link-maps').glob('link.*')) - if len(maps) < 2 or any('libgcc_eh.a' not in path.read_text() for path in maps): - raise SystemExit(f'missing static unwinder link maps: {maps!r}') + if len(maps) < 2 or any('libunwind.a(' not in path.read_text() for path in maps): + raise SystemExit(f'missing source-built unwinder link maps: {maps!r}') + for path in maps: + if re.search(r'libgcc(?:_eh|_s)?\.a\(', path.read_text()): + raise SystemExit(f'GCC runtime object linked in {path}') - uses: actions/upload-artifact@v4 with: name: musllinux-static-libgcc-${{ matrix.target }} @@ -133,8 +145,10 @@ jobs: Cargo.lock probe/_unwind_probe.so probe/Cargo.lock - probe/libgcc-evidence.txt + probe/unwinder-evidence.txt + probe/llvm-unwind-build/CMakeCache.txt probe/rustc.txt + probe/NOTICE-LLVM.json probe/NOTICE.json probe/bundle-rustc.txt probe/bundle-Cargo.lock diff --git a/scripts/add_llvm_unwind_notice.py b/scripts/add_llvm_unwind_notice.py new file mode 100644 index 0000000..6bcb26f --- /dev/null +++ b/scripts/add_llvm_unwind_notice.py @@ -0,0 +1,59 @@ +"""Дополнить Cargo bundle сведениями собранного LLVM unwinder.""" + +from __future__ import annotations + +import hashlib +import json +import sys +from pathlib import Path + +REVISION = "87f0227cb60147a26a1eeb4fb06e3b505e9c7261" +TARGETS = {"x86_64-unknown-linux-musl", "aarch64-unknown-linux-musl"} + + +def _file(path: Path) -> dict: + if path.is_symlink() or not path.is_file(): + raise ValueError(f"invalid unwinder evidence file: {path.name}") + data = path.read_bytes() + if not data or len(data) > 4 * 1024 * 1024: + raise ValueError(f"invalid unwinder evidence size: {path.name}") + return {"sha256": hashlib.sha256(data).hexdigest(), "text": data.decode("utf-8")} + + +def _add(source: Path, archive: Path, target: str, bundle: Path) -> bytes: + if target not in TARGETS: + raise ValueError("unexpected musllinux target") + result = json.loads(bundle.read_text(encoding="utf-8")) + if result.get("schema_version") != 1 or "native_runtime" in result: + raise ValueError("unexpected Cargo notice bundle") + if archive.is_symlink() or not archive.is_file() or archive.stat().st_size == 0: + raise ValueError("missing static LLVM unwinder") + result["native_runtime"] = { + "name": "LLVM libunwind", + "source": "https://github.com/llvm/llvm-project", + "revision": REVISION, + "target": target, + "archive_sha256": hashlib.sha256(archive.read_bytes()).hexdigest(), + "license": _file(source / "LICENSE.TXT"), + "upstream_credits": "The pinned libunwind tree has no CREDITS.TXT; " + "its LICENSE.TXT references that file in the legacy terms.", + "source_modifications": "none", + } + return (json.dumps(result, ensure_ascii=False, sort_keys=True, indent=2) + "\n").encode() + + +def main() -> None: + """Сохранить тексты уведомлений и происхождение статического архива.""" + if len(sys.argv) != 6: + raise SystemExit("usage: add_llvm_unwind_notice.py SOURCE ARCHIVE TARGET BUNDLE OUTPUT") + try: + data = _add(Path(sys.argv[1]), Path(sys.argv[2]), sys.argv[3], Path(sys.argv[4])) + with Path(sys.argv[5]).open("xb") as output: + output.write(data) + except (OSError, ValueError, KeyError, TypeError) as exc: + raise SystemExit(str(exc)) from exc + print("LLVM unwinder notice recorded") + + +if __name__ == "__main__": + main() diff --git a/scripts/musllinux-unwind-linker.sh b/scripts/musllinux-unwind-linker.sh index b640509..4fb5e6e 100755 --- a/scripts/musllinux-unwind-linker.sh +++ b/scripts/musllinux-unwind-linker.sh @@ -5,7 +5,8 @@ args=() replaced=0 for arg in "$@"; do if [[ "$arg" == -lgcc_s ]]; then - args+=("-Wl,-Bstatic" "-lgcc_eh" "-Wl,-Bdynamic") + test -s "${KTAV_UNWIND_ARCHIVE:?}" + args+=("$KTAV_UNWIND_ARCHIVE") replaced=1 else args+=("$arg") diff --git a/scripts/setup-llvm-unwind.sh b/scripts/setup-llvm-unwind.sh new file mode 100644 index 0000000..69776ea --- /dev/null +++ b/scripts/setup-llvm-unwind.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash +set -euo pipefail + +source_root="$GITHUB_WORKSPACE/llvm-unwind-source" +revision=87f0227cb60147a26a1eeb4fb06e3b505e9c7261 +case "${RUST_MUSL_CROSS_TARGET:?}" in + x86_64-unknown-linux-musl) processor=x86_64; flags= ;; + aarch64-unknown-linux-musl) processor=aarch64; flags=-mno-outline-atomics ;; + *) printf '%s\n' 'unexpected musllinux target' >&2; return 1 ;; +esac +test "$(git -c safe.directory="$source_root" -C "$source_root" rev-parse HEAD)" = "$revision" +test -z "$(git -c safe.directory="$source_root" -C "$source_root" status --porcelain)" +build="$GITHUB_WORKSPACE/probe/llvm-unwind-build" +cmake -S "$source_root/runtimes" -B "$build" \ + -DCMAKE_BUILD_TYPE=Release -DCMAKE_SYSTEM_NAME=Linux \ + -DCMAKE_SYSTEM_PROCESSOR="$processor" \ + -DCMAKE_C_COMPILER="${TARGET_CC:?}" -DCMAKE_CXX_COMPILER="${TARGET_CXX:?}" \ + -DCMAKE_ASM_COMPILER="$TARGET_CC" \ + -DCMAKE_C_FLAGS="$flags" -DCMAKE_CXX_FLAGS="$flags" \ + -DCMAKE_POSITION_INDEPENDENT_CODE=ON \ + -DLLVM_DEFAULT_TARGET_TRIPLE="$RUST_MUSL_CROSS_TARGET" \ + -DLLVM_ENABLE_RUNTIMES=libunwind -DLLVM_INCLUDE_TESTS=OFF -DLLVM_INCLUDE_DOCS=OFF \ + -DLIBUNWIND_ENABLE_SHARED=OFF -DLIBUNWIND_ENABLE_STATIC=ON \ + -DLIBUNWIND_HIDE_SYMBOLS=ON -DLIBUNWIND_ENABLE_ASSERTIONS=OFF +cmake --build "$build" --target unwind_static --parallel 2 +export KTAV_UNWIND_ARCHIVE="$build/lib/libunwind.a" +test -s "$KTAV_UNWIND_ARCHIVE" +test -z "$(git -c safe.directory="$source_root" -C "$source_root" status --porcelain)" +python3 scripts/add_llvm_unwind_notice.py "$source_root/libunwind" \ + "$KTAV_UNWIND_ARCHIVE" "$RUST_MUSL_CROSS_TARGET" \ + probe/NOTICE.json probe/NOTICE-LLVM.json +cp probe/NOTICE-LLVM.json python/ktav/_third_party/NOTICE.json +export CARGO_TARGET_X86_64_UNKNOWN_LINUX_MUSL_LINKER="$GITHUB_WORKSPACE/scripts/musllinux-unwind-linker.sh" +export CARGO_TARGET_AARCH64_UNKNOWN_LINUX_MUSL_LINKER="$GITHUB_WORKSPACE/scripts/musllinux-unwind-linker.sh" diff --git a/scripts/setup-musllinux-unwind.sh b/scripts/setup-musllinux-unwind.sh deleted file mode 100644 index f9ee123..0000000 --- a/scripts/setup-musllinux-unwind.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -case "${RUST_MUSL_CROSS_TARGET:?}" in - x86_64-unknown-linux-musl) expected=975e964745276d7669f389473d5150223be491ff40217faf5676f98cd18e914a ;; - aarch64-unknown-linux-musl) expected=0ef3ef20cc85454372d957fddf83f5fcf9a18ac6166aee251d776e3765ef64c8 ;; - *) printf '%s\n' 'unexpected musllinux target' >&2; return 1 ;; -esac -archive="$("${TARGET_CC:?}" -print-file-name=libgcc_eh.a)" -test -f "$archive" -printf '%s %s\n' "$expected" "$archive" | sha256sum --check -test -x "$GITHUB_WORKSPACE/scripts/musllinux-unwind-linker.sh" -export CARGO_TARGET_X86_64_UNKNOWN_LINUX_MUSL_LINKER="$GITHUB_WORKSPACE/scripts/musllinux-unwind-linker.sh" -export CARGO_TARGET_AARCH64_UNKNOWN_LINUX_MUSL_LINKER="$GITHUB_WORKSPACE/scripts/musllinux-unwind-linker.sh" diff --git a/tests/ci/test_llvm_unwind_notice.py b/tests/ci/test_llvm_unwind_notice.py new file mode 100644 index 0000000..b4a8935 --- /dev/null +++ b/tests/ci/test_llvm_unwind_notice.py @@ -0,0 +1,67 @@ +"""Проверить уведомления внешнего статического unwinder.""" + +from __future__ import annotations + +import hashlib +import json +import subprocess +import sys +from pathlib import Path + +import pytest + +ROOT = Path(__file__).resolve().parents[2] +SCRIPT = ROOT / "scripts" / "add_llvm_unwind_notice.py" + + +def _inputs(tmp_path: Path) -> tuple[Path, Path, Path]: + source = tmp_path / "source" + source.mkdir() + (source / "LICENSE.TXT").write_bytes(b"Full license\r\nLegacy terms\n") + archive = tmp_path / "libunwind.a" + archive.write_bytes(b"static archive") + bundle = tmp_path / "cargo.json" + bundle.write_text(json.dumps({"schema_version": 1, "licenses": ["Cargo text"]}), encoding="utf-8") + return source, archive, bundle + + +def _run( + source: Path, archive: Path, bundle: Path, output: Path, target: str = "x86_64-unknown-linux-musl" +) -> subprocess.CompletedProcess[str]: + return subprocess.run( + [sys.executable, str(SCRIPT), str(source), str(archive), target, str(bundle), str(output)], + capture_output=True, + text=True, + ) + + +def test_preserves_license_and_binds_archive(tmp_path: Path) -> None: + source, archive, bundle = _inputs(tmp_path) + output = tmp_path / "result.json" + result = _run(source, archive, bundle, output) + assert result.returncode == 0, result.stderr + content = json.loads(output.read_bytes()) + assert content["licenses"] == ["Cargo text"] + runtime = content["native_runtime"] + assert runtime["license"]["text"] == "Full license\r\nLegacy terms\n" + assert runtime["archive_sha256"] == hashlib.sha256(archive.read_bytes()).hexdigest() + assert runtime["revision"] == "87f0227cb60147a26a1eeb4fb06e3b505e9c7261" + assert str(tmp_path) not in output.read_text(encoding="utf-8") + + +@pytest.mark.parametrize("name", ["LICENSE.TXT", "archive", "target", "existing runtime"]) +def test_rejects_incomplete_or_overwritten_evidence(tmp_path: Path, name: str) -> None: + source, archive, bundle = _inputs(tmp_path) + target = "x86_64-unknown-linux-musl" + if name == "LICENSE.TXT": + (source / name).write_bytes(b"") + elif name == "archive": + archive.write_bytes(b"") + elif name == "target": + target = "unknown" + else: + bundle.write_text(json.dumps({"schema_version": 1, "native_runtime": {}}), encoding="utf-8") + output = tmp_path / "result.json" + result = _run(source, archive, bundle, output, target) + assert result.returncode != 0 + assert not output.exists() From 688b27bbbe66e4265ee424da54a2033032c11be0 Mon Sep 17 00:00:00 2001 From: Arch-AI-tech Date: Thu, 29 Oct 2026 10:25:26 +0100 Subject: [PATCH 29/54] test: format LLVM notice fixtures Co-Authored-By: Claude Opus 5.5 --- tests/ci/test_llvm_unwind_notice.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/tests/ci/test_llvm_unwind_notice.py b/tests/ci/test_llvm_unwind_notice.py index b4a8935..8ff33b2 100644 --- a/tests/ci/test_llvm_unwind_notice.py +++ b/tests/ci/test_llvm_unwind_notice.py @@ -21,12 +21,18 @@ def _inputs(tmp_path: Path) -> tuple[Path, Path, Path]: archive = tmp_path / "libunwind.a" archive.write_bytes(b"static archive") bundle = tmp_path / "cargo.json" - bundle.write_text(json.dumps({"schema_version": 1, "licenses": ["Cargo text"]}), encoding="utf-8") + bundle.write_text( + json.dumps({"schema_version": 1, "licenses": ["Cargo text"]}), encoding="utf-8" + ) return source, archive, bundle def _run( - source: Path, archive: Path, bundle: Path, output: Path, target: str = "x86_64-unknown-linux-musl" + source: Path, + archive: Path, + bundle: Path, + output: Path, + target: str = "x86_64-unknown-linux-musl", ) -> subprocess.CompletedProcess[str]: return subprocess.run( [sys.executable, str(SCRIPT), str(source), str(archive), target, str(bundle), str(output)], From 7146e329fcd4c8893bd827e9900e7ce9b040c125 Mon Sep 17 00:00:00 2001 From: Arch-AI-tech Date: Thu, 29 Oct 2026 10:36:12 +0100 Subject: [PATCH 30/54] ci: test musl wheels without GCC startup objects Supply a project-owned ELF DSO handle and finalization hook, linking without compiler startup files. Verify that Rust TLS destructors run at thread exit and C++ destructors run once at dlclose in real musl runtimes. Reject GCC crtbegin/crtend objects as well as archive members. Retain panic conversion, native corpus, ABI and notice-bundle checks. The replacement follows the Itanium C++ ABI DSO destruction contract. This remains CI-only; production release and historical wheels are unchanged. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/musllinux-evidence.yml | 20 ++++++++++++++- scripts/musllinux-dso.c | 10 ++++++++ scripts/musllinux-unwind-linker.sh | 3 ++- scripts/setup-llvm-unwind.sh | 6 +++++ tests/fixtures/unwind-probe/crt-probe.cpp | 15 +++++++++++ tests/fixtures/unwind-probe/src/lib.rs | 31 ++++++++++++++++++++++- 6 files changed, 82 insertions(+), 3 deletions(-) create mode 100644 scripts/musllinux-dso.c create mode 100644 tests/fixtures/unwind-probe/crt-probe.cpp diff --git a/.github/workflows/musllinux-evidence.yml b/.github/workflows/musllinux-evidence.yml index b21c513..c44c3fd 100644 --- a/.github/workflows/musllinux-evidence.yml +++ b/.github/workflows/musllinux-evidence.yml @@ -135,8 +135,11 @@ jobs: if len(maps) < 2 or any('libunwind.a(' not in path.read_text() for path in maps): raise SystemExit(f'missing source-built unwinder link maps: {maps!r}') for path in maps: - if re.search(r'libgcc(?:_eh|_s)?\.a\(', path.read_text()): + if re.search(r'libgcc(?:_eh|_s)?\.a\(|crt(?:begin|end)[^\s/]*\.o', path.read_text()): raise SystemExit(f'GCC runtime object linked in {path}') + dynamic = subprocess.check_output(['readelf', '-d', 'probe/crt_probe.so'], text=True) + if re.findall(r'Shared library: \[([^]]+)\]', dynamic) != ['libc.so']: + raise SystemExit('unexpected CRT probe runtime dependencies') - uses: actions/upload-artifact@v4 with: name: musllinux-static-libgcc-${{ matrix.target }} @@ -144,6 +147,8 @@ jobs: dist/*.whl Cargo.lock probe/_unwind_probe.so + probe/crt_probe.so + probe/crt-probe.map probe/Cargo.lock probe/unwinder-evidence.txt probe/llvm-unwind-build/CMakeCache.txt @@ -206,11 +211,15 @@ jobs: /opt/python/cp312-cp312/bin/python -m pytest -p no:cacheprovider -q \ tests/test_conformance.py tests/edit tests/parse tests/serialize tests/roundtrip ' <<'PY' + import _ctypes + import ctypes import _unwind_probe import ktav import ktav._core for _ in range(2): + if not _unwind_probe.tls_drop_probe(): + raise AssertionError('Rust TLS destructor did not run') try: _unwind_probe.panic_probe() except BaseException as exc: @@ -220,4 +229,13 @@ jobs: raise AssertionError('Rust panic was not converted to PanicException') if ktav.loads('answer: 42') != {'answer': 42}: raise AssertionError('native parser returned an unexpected value') + counter = ctypes.c_int(0) + cleanup = ctypes.CDLL('/payload/probe/crt_probe.so') + cleanup.set_target.argtypes = [ctypes.POINTER(ctypes.c_int)] + cleanup.set_target.restype = None + cleanup.set_target(ctypes.byref(counter)) + _ctypes.dlclose(cleanup._handle) + if counter.value != 1: + raise AssertionError(f'DSO destructor count: {counter.value}, expected 1') + print('TLS, panic conversion and DSO finalization verified') PY diff --git a/scripts/musllinux-dso.c b/scripts/musllinux-dso.c new file mode 100644 index 0000000..c40c0b8 --- /dev/null +++ b/scripts/musllinux-dso.c @@ -0,0 +1,10 @@ +/* Очистка ELF DSO: https://itanium-cxx-abi.github.io/cxx-abi/abi.html#dso-dtor */ +__attribute__((visibility("hidden"))) void *__dso_handle = &__dso_handle; + +extern void __cxa_finalize(void *) __attribute__((weak)); + +__attribute__((destructor)) static void finalize_dso(void) { + if (__cxa_finalize) { + __cxa_finalize(&__dso_handle); + } +} diff --git a/scripts/musllinux-unwind-linker.sh b/scripts/musllinux-unwind-linker.sh index 4fb5e6e..88e9ac3 100755 --- a/scripts/musllinux-unwind-linker.sh +++ b/scripts/musllinux-unwind-linker.sh @@ -22,4 +22,5 @@ if [[ -n "${KTAV_LINK_MAP_DIR:-}" ]]; then chmod 644 "$map" args+=("-Wl,-Map=$map") fi -exec "${TARGET_CC:?}" -static-libgcc "${args[@]}" +test -s "${KTAV_DSO_HANDLE_OBJECT:?}" +exec "${TARGET_CC:?}" -static-libgcc "${args[@]}" -nostartfiles "$KTAV_DSO_HANDLE_OBJECT" diff --git a/scripts/setup-llvm-unwind.sh b/scripts/setup-llvm-unwind.sh index 69776ea..33269df 100644 --- a/scripts/setup-llvm-unwind.sh +++ b/scripts/setup-llvm-unwind.sh @@ -25,6 +25,12 @@ cmake -S "$source_root/runtimes" -B "$build" \ cmake --build "$build" --target unwind_static --parallel 2 export KTAV_UNWIND_ARCHIVE="$build/lib/libunwind.a" test -s "$KTAV_UNWIND_ARCHIVE" +export KTAV_DSO_HANDLE_OBJECT="$GITHUB_WORKSPACE/probe/ktav-dso.o" +"$TARGET_CC" -fPIC -fvisibility=hidden -c scripts/musllinux-dso.c -o "$KTAV_DSO_HANDLE_OBJECT" +"$TARGET_CXX" -fPIC -fvisibility=hidden -fno-exceptions -fno-rtti -fuse-cxa-atexit \ + -c tests/fixtures/unwind-probe/crt-probe.cpp -o probe/crt-probe.o +"$TARGET_CC" -shared -nostartfiles -nodefaultlibs probe/crt-probe.o \ + "$KTAV_DSO_HANDLE_OBJECT" -lc -Wl,-Map=probe/crt-probe.map -o probe/crt_probe.so test -z "$(git -c safe.directory="$source_root" -C "$source_root" status --porcelain)" python3 scripts/add_llvm_unwind_notice.py "$source_root/libunwind" \ "$KTAV_UNWIND_ARCHIVE" "$RUST_MUSL_CROSS_TARGET" \ diff --git a/tests/fixtures/unwind-probe/crt-probe.cpp b/tests/fixtures/unwind-probe/crt-probe.cpp new file mode 100644 index 0000000..d929e04 --- /dev/null +++ b/tests/fixtures/unwind-probe/crt-probe.cpp @@ -0,0 +1,15 @@ +static int *counter; + +struct Cleanup { + ~Cleanup() { + if (counter) { + ++*counter; + } + } +}; + +static Cleanup cleanup; + +extern "C" __attribute__((visibility("default"))) void set_target(int *target) { + counter = target; +} diff --git a/tests/fixtures/unwind-probe/src/lib.rs b/tests/fixtures/unwind-probe/src/lib.rs index 7d80601..ed8cdee 100644 --- a/tests/fixtures/unwind-probe/src/lib.rs +++ b/tests/fixtures/unwind-probe/src/lib.rs @@ -1,4 +1,32 @@ use pyo3::prelude::*; +use std::cell::RefCell; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::Arc; + +struct TlsProbe(Arc); + +impl Drop for TlsProbe { + fn drop(&mut self) { + self.0.store(true, Ordering::Release); + } +} + +thread_local! { + static TLS_PROBE: RefCell> = const { RefCell::new(None) }; +} + +#[pyfunction] +fn tls_drop_probe() -> bool { + let dropped = Arc::new(AtomicBool::new(false)); + let marker = Arc::clone(&dropped); + let worker = std::thread::spawn(move || { + TLS_PROBE.with(|slot| *slot.borrow_mut() = Some(TlsProbe(marker))); + }); + if worker.join().is_err() { + return false; + } + dropped.load(Ordering::Acquire) +} #[pyfunction] fn panic_probe() { @@ -7,5 +35,6 @@ fn panic_probe() { #[pymodule] fn _unwind_probe(module: &Bound<'_, PyModule>) -> PyResult<()> { - module.add_function(wrap_pyfunction!(panic_probe, module)?) + module.add_function(wrap_pyfunction!(panic_probe, module)?)?; + module.add_function(wrap_pyfunction!(tls_drop_probe, module)?) } From 3e41455abce7c2c647ed57657d44aad066970b34 Mon Sep 17 00:00:00 2001 From: Arch-AI-tech Date: Thu, 29 Oct 2026 10:43:16 +0100 Subject: [PATCH 31/54] ci: retain failing unwinder link evidence Print matched GCC objects and upload link maps even when the gate fails, without relaxing the runtime check. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/musllinux-evidence.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/musllinux-evidence.yml b/.github/workflows/musllinux-evidence.yml index c44c3fd..60bf9be 100644 --- a/.github/workflows/musllinux-evidence.yml +++ b/.github/workflows/musllinux-evidence.yml @@ -135,12 +135,16 @@ jobs: if len(maps) < 2 or any('libunwind.a(' not in path.read_text() for path in maps): raise SystemExit(f'missing source-built unwinder link maps: {maps!r}') for path in maps: - if re.search(r'libgcc(?:_eh|_s)?\.a\(|crt(?:begin|end)[^\s/]*\.o', path.read_text()): + matches = [line for line in path.read_text().splitlines() + if re.search(r'libgcc(?:_eh|_s)?\.a\(|crt(?:begin|end)[^\s/]*\.o', line)] + if matches: + print('\n'.join(dict.fromkeys(matches[:20])), file=sys.stderr) raise SystemExit(f'GCC runtime object linked in {path}') dynamic = subprocess.check_output(['readelf', '-d', 'probe/crt_probe.so'], text=True) if re.findall(r'Shared library: \[([^]]+)\]', dynamic) != ['libc.so']: raise SystemExit('unexpected CRT probe runtime dependencies') - uses: actions/upload-artifact@v4 + if: always() with: name: musllinux-static-libgcc-${{ matrix.target }} path: | From df2e31c0b53e4ebc8aa324910d8a2c00d51a1088 Mon Sep 17 00:00:00 2001 From: Arch-AI-tech Date: Thu, 29 Oct 2026 10:47:07 +0100 Subject: [PATCH 32/54] ci: distinguish CRT inputs from linker patterns GNU maps contain crtbegin/crtend wildcards in linker-script selectors even when those objects are absent. Check actual LOAD records for CRT objects and archive-member records for GCC runtime code. Add positive and negative regression cases without allowing actual GCC inputs. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/musllinux-evidence.yml | 12 ++---- scripts/check_unwinder_maps.py | 40 +++++++++++++++++ tests/ci/test_unwinder_maps.py | 55 ++++++++++++++++++++++++ 3 files changed, 98 insertions(+), 9 deletions(-) create mode 100644 scripts/check_unwinder_maps.py create mode 100644 tests/ci/test_unwinder_maps.py diff --git a/.github/workflows/musllinux-evidence.yml b/.github/workflows/musllinux-evidence.yml index 60bf9be..4e49bbb 100644 --- a/.github/workflows/musllinux-evidence.yml +++ b/.github/workflows/musllinux-evidence.yml @@ -131,15 +131,9 @@ jobs: and parts[6] != 'UND'] if exports != ['PyInit__unwind_probe']: raise SystemExit(f'{binary}: unexpected exported symbols: {exports!r}') - maps = list(Path('probe/link-maps').glob('link.*')) - if len(maps) < 2 or any('libunwind.a(' not in path.read_text() for path in maps): - raise SystemExit(f'missing source-built unwinder link maps: {maps!r}') - for path in maps: - matches = [line for line in path.read_text().splitlines() - if re.search(r'libgcc(?:_eh|_s)?\.a\(|crt(?:begin|end)[^\s/]*\.o', line)] - if matches: - print('\n'.join(dict.fromkeys(matches[:20])), file=sys.stderr) - raise SystemExit(f'GCC runtime object linked in {path}') + subprocess.run([ + sys.executable, 'scripts/check_unwinder_maps.py', 'probe/link-maps', + ], check=True) dynamic = subprocess.check_output(['readelf', '-d', 'probe/crt_probe.so'], text=True) if re.findall(r'Shared library: \[([^]]+)\]', dynamic) != ['libc.so']: raise SystemExit('unexpected CRT probe runtime dependencies') diff --git a/scripts/check_unwinder_maps.py b/scripts/check_unwinder_maps.py new file mode 100644 index 0000000..b1d3877 --- /dev/null +++ b/scripts/check_unwinder_maps.py @@ -0,0 +1,40 @@ +"""Проверить входные объекты GNU линкера.""" + +from __future__ import annotations + +import re +import sys +from pathlib import Path + +GCC_MEMBER = re.compile(r"libgcc(?:_eh|_s)?\.a\([^()\n]+\)") +GCC_CRT = re.compile(r"(?:^|/)crt(?:begin|end)[^/\s]*\.o(?:\s|$)") + + +def _check(text: str) -> None: + lines = text.splitlines() + loads = [line[5:] for line in lines if line.startswith("LOAD ")] + if not loads or "libunwind.a(" not in text: + raise ValueError("missing GNU link map inputs or LLVM unwinder members") + offending = [line for line in lines if GCC_MEMBER.search(line)] + offending.extend(path for path in loads if GCC_CRT.search(path)) + if offending: + raise ValueError("GCC runtime input linked: " + "; ".join(dict.fromkeys(offending))) + + +def main() -> None: + """Отклонить GCC runtime в каждой сохранённой карте линковки.""" + if len(sys.argv) != 2: + raise SystemExit("usage: check_unwinder_maps.py MAP_DIRECTORY") + try: + maps = sorted(Path(sys.argv[1]).glob("link.*")) + if len(maps) < 2: + raise ValueError("missing wheel or panic probe link map") + for path in maps: + _check(path.read_text(encoding="utf-8")) + print(f"LLVM unwinder link inputs verified: {path.name}") + except (OSError, ValueError) as exc: + raise SystemExit(str(exc)) from exc + + +if __name__ == "__main__": + main() diff --git a/tests/ci/test_unwinder_maps.py b/tests/ci/test_unwinder_maps.py new file mode 100644 index 0000000..4c38ff4 --- /dev/null +++ b/tests/ci/test_unwinder_maps.py @@ -0,0 +1,55 @@ +"""Проверить различение входных объектов и шаблонов линкера.""" + +from __future__ import annotations + +import subprocess +import sys +from pathlib import Path + +import pytest + +ROOT = Path(__file__).resolve().parents[2] +SCRIPT = ROOT / "scripts" / "check_unwinder_maps.py" +BASE = "LOAD /work/libunwind.a\n/work/libunwind.a(UnwindLevel1.c.o)\n" + + +def _run(tmp_path: Path, extra: str) -> subprocess.CompletedProcess[str]: + for name in ("link.wheel", "link.probe"): + (tmp_path / name).write_text(BASE + extra, encoding="utf-8") + return subprocess.run( + [sys.executable, str(SCRIPT), str(tmp_path)], capture_output=True, text=True + ) + + +def test_accepts_unused_crt_patterns_and_archives(tmp_path: Path) -> None: + result = _run( + tmp_path, + " *(.init_array EXCLUDE_FILE(*crtend?.o *crtbegin.o) .ctors)\n" + " *crtbegin.o(.ctors)\nLOAD /tool/libgcc.a\nLOAD /tool/libgcc_eh.a\n", + ) + assert result.returncode == 0, result.stderr + + +@pytest.mark.parametrize( + "entry", + [ + "LOAD /tool/crtbeginS.o\n", + "LOAD /tool/crtendS.o\n", + "LOAD crtbegin.o\n", + "/tool/libgcc.a(helper.o)\n", + "/tool/libgcc_eh.a(unwind-dw2.o)\n", + ], +) +def test_rejects_actual_gcc_inputs(tmp_path: Path, entry: str) -> None: + result = _run(tmp_path, entry) + assert result.returncode != 0 + assert "GCC runtime input linked" in result.stderr + + +def test_rejects_missing_probe_map(tmp_path: Path) -> None: + (tmp_path / "link.wheel").write_text(BASE, encoding="utf-8") + result = subprocess.run( + [sys.executable, str(SCRIPT), str(tmp_path)], capture_output=True, text=True + ) + assert result.returncode != 0 + assert "missing wheel or panic probe" in result.stderr From 03146373f9ec58b1a363a0e1d6d343c988013f2b Mon Sep 17 00:00:00 2001 From: Arch-AI-tech Date: Thu, 29 Oct 2026 11:00:57 +0100 Subject: [PATCH 33/54] test: respect musl DSO finalization semantics Musl deliberately makes dlclose a no-op and runs DSO destructors at process exit. Check this in an isolated runner with a static counter that remains alive through finalization. Check the same project-owned DSO hook with real glibc unloading as well, and require exactly one destructor call by exit on both platforms. Retain Rust TLS and panic probes, native corpus and the fail-closed runtime inventory. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/musllinux-evidence.yml | 29 +++++++++------ scripts/setup-llvm-unwind.sh | 1 + tests/fixtures/unwind-probe/crt-runner.c | 45 ++++++++++++++++++++++++ 3 files changed, 64 insertions(+), 11 deletions(-) create mode 100644 tests/fixtures/unwind-probe/crt-runner.c diff --git a/.github/workflows/musllinux-evidence.yml b/.github/workflows/musllinux-evidence.yml index 4e49bbb..904b25a 100644 --- a/.github/workflows/musllinux-evidence.yml +++ b/.github/workflows/musllinux-evidence.yml @@ -59,6 +59,18 @@ jobs: llvm/cmake libcxx/include libunwind + - name: Verify DSO finalization with glibc dlclose + shell: bash + run: | + set -euo pipefail + host="$RUNNER_TEMP/dso-lifecycle" + mkdir -p "$host" + cc -fPIC -fvisibility=hidden -c scripts/musllinux-dso.c -o "$host/dso.o" + c++ -fPIC -fvisibility=hidden -fno-exceptions -fno-rtti -fuse-cxa-atexit \ + -c tests/fixtures/unwind-probe/crt-probe.cpp -o "$host/probe.o" + cc -shared -nostartfiles -nodefaultlibs "$host/probe.o" "$host/dso.o" -lc -o "$host/probe.so" + cc tests/fixtures/unwind-probe/crt-runner.c -ldl -o "$host/runner" + "$host/runner" "$host/probe.so" - name: Check linker guard failures shell: bash run: | @@ -146,6 +158,7 @@ jobs: Cargo.lock probe/_unwind_probe.so probe/crt_probe.so + probe/crt_runner probe/crt-probe.map probe/Cargo.lock probe/unwinder-evidence.txt @@ -204,13 +217,15 @@ jobs: --workdir /work "$MUSL_IMAGE" /bin/sh -ec ' /opt/python/cp312-cp312/bin/python -m pip install --no-index --find-links=/payload/testdeps pytest /opt/python/cp312-cp312/bin/python -m pip install --no-index --no-deps /payload/dist/*.whl + runner_dir="$(mktemp -d)" + cp /payload/probe/crt_runner "$runner_dir/crt-runner" + chmod 755 "$runner_dir/crt-runner" + "$runner_dir/crt-runner" /payload/probe/crt_probe.so PYTHONPATH=/payload/probe /opt/python/cp312-cp312/bin/python - KTAV_REQUIRE_NATIVE=1 PYTHONDONTWRITEBYTECODE=1 PYTHONPATH=/payload/probe \ /opt/python/cp312-cp312/bin/python -m pytest -p no:cacheprovider -q \ tests/test_conformance.py tests/edit tests/parse tests/serialize tests/roundtrip ' <<'PY' - import _ctypes - import ctypes import _unwind_probe import ktav import ktav._core @@ -227,13 +242,5 @@ jobs: raise AssertionError('Rust panic was not converted to PanicException') if ktav.loads('answer: 42') != {'answer': 42}: raise AssertionError('native parser returned an unexpected value') - counter = ctypes.c_int(0) - cleanup = ctypes.CDLL('/payload/probe/crt_probe.so') - cleanup.set_target.argtypes = [ctypes.POINTER(ctypes.c_int)] - cleanup.set_target.restype = None - cleanup.set_target(ctypes.byref(counter)) - _ctypes.dlclose(cleanup._handle) - if counter.value != 1: - raise AssertionError(f'DSO destructor count: {counter.value}, expected 1') - print('TLS, panic conversion and DSO finalization verified') + print('TLS and panic conversion verified') PY diff --git a/scripts/setup-llvm-unwind.sh b/scripts/setup-llvm-unwind.sh index 33269df..00800d6 100644 --- a/scripts/setup-llvm-unwind.sh +++ b/scripts/setup-llvm-unwind.sh @@ -31,6 +31,7 @@ export KTAV_DSO_HANDLE_OBJECT="$GITHUB_WORKSPACE/probe/ktav-dso.o" -c tests/fixtures/unwind-probe/crt-probe.cpp -o probe/crt-probe.o "$TARGET_CC" -shared -nostartfiles -nodefaultlibs probe/crt-probe.o \ "$KTAV_DSO_HANDLE_OBJECT" -lc -Wl,-Map=probe/crt-probe.map -o probe/crt_probe.so +"$TARGET_CC" tests/fixtures/unwind-probe/crt-runner.c -ldl -o probe/crt_runner test -z "$(git -c safe.directory="$source_root" -C "$source_root" status --porcelain)" python3 scripts/add_llvm_unwind_notice.py "$source_root/libunwind" \ "$KTAV_UNWIND_ARCHIVE" "$RUST_MUSL_CROSS_TARGET" \ diff --git a/tests/fixtures/unwind-probe/crt-runner.c b/tests/fixtures/unwind-probe/crt-runner.c new file mode 100644 index 0000000..29b7373 --- /dev/null +++ b/tests/fixtures/unwind-probe/crt-runner.c @@ -0,0 +1,45 @@ +#include +#include +#include + +static int counter; + +static void verify_exit(void) { + if (counter != 1) { + fprintf(stderr, "DSO destructor count at exit: %d, expected 1\n", counter); + _Exit(1); + } + puts("DSO finalization verified"); +} + +int main(int argc, char **argv) { + if (argc != 2 || atexit(verify_exit) != 0) { + return 1; + } + void *handle = dlopen(argv[1], RTLD_NOW | RTLD_LOCAL); + if (!handle) { + fprintf(stderr, "dlopen failed: %s\n", dlerror()); + _Exit(1); + } + void (*set_target)(int *) = (void (*)(int *))dlsym(handle, "set_target"); + if (!set_target) { + fprintf(stderr, "missing set_target\n"); + _Exit(1); + } + set_target(&counter); + if (dlclose(handle) != 0) { + fprintf(stderr, "dlclose failed: %s\n", dlerror()); + _Exit(1); + } +#ifdef __GLIBC__ + const int expected = 1; +#else + /* В musl выгрузка DSO откладывается до завершения процесса. */ + const int expected = 0; +#endif + if (counter != expected) { + fprintf(stderr, "DSO destructor count after dlclose: %d, expected %d\n", counter, expected); + _Exit(1); + } + return 0; +} From 8111241e002becc74684e35320f998d30d15371a Mon Sep 17 00:00:00 2001 From: Arch-AI-tech Date: Thu, 29 Oct 2026 11:14:00 +0100 Subject: [PATCH 34/54] ci: retain LLVM source-level copyright notices Inventory pinned runtime source headers and their hashes in the notice bundle, including the additional DWARF copyright notice in dwarf2.h. Reject unreviewed source license headers and preserve the full original leading notices rather than relying only on SPDX metadata. Co-Authored-By: Claude Opus 5.5 --- scripts/add_llvm_unwind_notice.py | 30 +++++++++++++++++++++++++++++ tests/ci/test_llvm_unwind_notice.py | 21 +++++++++++++++++++- 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/scripts/add_llvm_unwind_notice.py b/scripts/add_llvm_unwind_notice.py index 6bcb26f..513b834 100644 --- a/scripts/add_llvm_unwind_notice.py +++ b/scripts/add_llvm_unwind_notice.py @@ -4,6 +4,7 @@ import hashlib import json +import re import sys from pathlib import Path @@ -20,6 +21,34 @@ def _file(path: Path) -> dict: return {"sha256": hashlib.sha256(data).hexdigest(), "text": data.decode("utf-8")} +def _source_notices(source: Path) -> list[dict]: + notices = [] + for directory in (source / "include", source / "src"): + if directory.is_symlink() or not directory.is_dir(): + raise ValueError("missing LLVM runtime source directory") + for path in sorted(directory.rglob("*")): + if path.suffix not in {".c", ".cpp", ".h", ".hpp", ".S"}: + continue + entry = _file(path) + match = re.match( + r"\A(?:\s+|//[^\n]*(?:\n|\Z)|/\*.*?\*/)*", entry["text"], re.DOTALL + ) + header = match.group() if match else "" + licenses = re.findall(r"SPDX-License-Identifier:\s*([^\r\n]+)", header) + if licenses != ["Apache-2.0 WITH LLVM-exception"]: + raise ValueError(f"unreviewed LLVM source license: {path.name}") + notices.append( + { + "path": path.relative_to(source).as_posix(), + "sha256": entry["sha256"], + "notice": header, + } + ) + if not notices: + raise ValueError("missing LLVM runtime source notices") + return notices + + def _add(source: Path, archive: Path, target: str, bundle: Path) -> bytes: if target not in TARGETS: raise ValueError("unexpected musllinux target") @@ -35,6 +64,7 @@ def _add(source: Path, archive: Path, target: str, bundle: Path) -> bytes: "target": target, "archive_sha256": hashlib.sha256(archive.read_bytes()).hexdigest(), "license": _file(source / "LICENSE.TXT"), + "source_notices": _source_notices(source), "upstream_credits": "The pinned libunwind tree has no CREDITS.TXT; " "its LICENSE.TXT references that file in the legacy terms.", "source_modifications": "none", diff --git a/tests/ci/test_llvm_unwind_notice.py b/tests/ci/test_llvm_unwind_notice.py index 8ff33b2..aad0449 100644 --- a/tests/ci/test_llvm_unwind_notice.py +++ b/tests/ci/test_llvm_unwind_notice.py @@ -18,6 +18,13 @@ def _inputs(tmp_path: Path) -> tuple[Path, Path, Path]: source = tmp_path / "source" source.mkdir() (source / "LICENSE.TXT").write_bytes(b"Full license\r\nLegacy terms\n") + (source / "src").mkdir() + (source / "include").mkdir() + (source / "src" / "fixture.c").write_text( + "// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n" + "/* Copyright fixture contributor */\nint fixture;\n", + encoding="utf-8", + ) archive = tmp_path / "libunwind.a" archive.write_bytes(b"static archive") bundle = tmp_path / "cargo.json" @@ -52,10 +59,18 @@ def test_preserves_license_and_binds_archive(tmp_path: Path) -> None: assert runtime["license"]["text"] == "Full license\r\nLegacy terms\n" assert runtime["archive_sha256"] == hashlib.sha256(archive.read_bytes()).hexdigest() assert runtime["revision"] == "87f0227cb60147a26a1eeb4fb06e3b505e9c7261" + notice = runtime["source_notices"][0] + assert notice["path"] == "src/fixture.c" + assert "Copyright fixture contributor" in notice["notice"] + assert "int fixture" not in notice["notice"] + expected = hashlib.sha256((source / "src" / "fixture.c").read_bytes()).hexdigest() + assert notice["sha256"] == expected assert str(tmp_path) not in output.read_text(encoding="utf-8") -@pytest.mark.parametrize("name", ["LICENSE.TXT", "archive", "target", "existing runtime"]) +@pytest.mark.parametrize( + "name", ["LICENSE.TXT", "archive", "target", "existing runtime", "source license"] +) def test_rejects_incomplete_or_overwritten_evidence(tmp_path: Path, name: str) -> None: source, archive, bundle = _inputs(tmp_path) target = "x86_64-unknown-linux-musl" @@ -65,6 +80,10 @@ def test_rejects_incomplete_or_overwritten_evidence(tmp_path: Path, name: str) - archive.write_bytes(b"") elif name == "target": target = "unknown" + elif name == "source license": + (source / "src" / "fixture.c").write_text( + "// SPDX-License-Identifier: unreviewed\nint fixture;\n", encoding="utf-8" + ) else: bundle.write_text(json.dumps({"schema_version": 1, "native_runtime": {}}), encoding="utf-8") output = tmp_path / "result.json" From 11c07f2c2fd116dd62f6765fbb0f4a53a8b30661 Mon Sep 17 00:00:00 2001 From: Arch-AI-tech Date: Thu, 29 Oct 2026 11:17:05 +0100 Subject: [PATCH 35/54] style: format LLVM source notice scan Co-Authored-By: Claude Opus 5.5 --- scripts/add_llvm_unwind_notice.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/scripts/add_llvm_unwind_notice.py b/scripts/add_llvm_unwind_notice.py index 513b834..44f9e4d 100644 --- a/scripts/add_llvm_unwind_notice.py +++ b/scripts/add_llvm_unwind_notice.py @@ -30,9 +30,7 @@ def _source_notices(source: Path) -> list[dict]: if path.suffix not in {".c", ".cpp", ".h", ".hpp", ".S"}: continue entry = _file(path) - match = re.match( - r"\A(?:\s+|//[^\n]*(?:\n|\Z)|/\*.*?\*/)*", entry["text"], re.DOTALL - ) + match = re.match(r"\A(?:\s+|//[^\n]*(?:\n|\Z)|/\*.*?\*/)*", entry["text"], re.DOTALL) header = match.group() if match else "" licenses = re.findall(r"SPDX-License-Identifier:\s*([^\r\n]+)", header) if licenses != ["Apache-2.0 WITH LLVM-exception"]: From b910e6c0098c6d795ccc5bbefbe71fd1f935ba8f Mon Sep 17 00:00:00 2001 From: Arch-AI-tech Date: Thu, 29 Oct 2026 11:33:46 +0100 Subject: [PATCH 36/54] ci: share locked native notice preparation Prepare Cargo license texts, notices and the actual Rust std report from an existing lock without resolving dependencies again. Keep evidence outside package sources, refuse overwrites and assert the lock remains unchanged. Use the same helper in native and musllinux evidence builds ahead of production release integration. Add staging guard tests; keep exact wheel bundle, ABI and runtime verification in the existing CI jobs. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/licenses.yml | 46 ++----------- .github/workflows/musllinux-evidence.yml | 9 +-- scripts/prepare_native_notices.py | 87 ++++++++++++++++++++++++ tests/ci/test_prepare_native_notices.py | 54 +++++++++++++++ 4 files changed, 148 insertions(+), 48 deletions(-) create mode 100644 scripts/prepare_native_notices.py create mode 100644 tests/ci/test_prepare_native_notices.py diff --git a/.github/workflows/licenses.yml b/.github/workflows/licenses.yml index 76731ab..85a642e 100644 --- a/.github/workflows/licenses.yml +++ b/.github/workflows/licenses.yml @@ -37,58 +37,22 @@ jobs: - uses: taiki-e/install-action@v2 with: tool: cargo-about@0.9.3 + - uses: actions/setup-python@v5 + with: + python-version: '3.12' - name: Record the resolved Cargo graph and license texts shell: bash run: | set -euo pipefail - mkdir -p "$RUNNER_TEMP/cargo-license-evidence" cargo generate-lockfile - cp Cargo.lock "$RUNNER_TEMP/cargo-license-evidence/Cargo.lock" - rustc --version --verbose > "$RUNNER_TEMP/cargo-license-evidence/rustc.txt" - stdlib_report="$(rustc --print sysroot)/share/doc/rust/COPYRIGHT-library.html" - test -s "$stdlib_report" - cp "$stdlib_report" "$RUNNER_TEMP/cargo-license-evidence/COPYRIGHT-library.html" - cargo metadata --locked --all-features --format-version 1 > "$RUNNER_TEMP/cargo-license-evidence/metadata.json" - cargo about generate --locked --all-features --fail --config ci/about.toml --format json > "$RUNNER_TEMP/cargo-license-evidence/licenses.json" - python3 - "$RUNNER_TEMP/cargo-license-evidence/licenses.json" <<'PY' - import json - import sys - - def missing_source(entry): - return not entry.get('source_path') or not entry.get('text') - - assert missing_source({'source_path': None, 'text': 'generic'}) - assert not missing_source({'source_path': 'LICENSE', 'text': 'text'}) - with open(sys.argv[1], encoding='utf-8') as source: - report = json.load(source) - licenses = report.get('licenses') - if not isinstance(licenses, list) or not licenses: - raise SystemExit('license report has no license texts') - fallback = [entry for entry in licenses if missing_source(entry)] - if fallback: - for entry in fallback: - packages = [(item['crate']['name'], item['crate']['version']) - for item in entry.get('used_by', [])] - print(f"unverified {entry.get('id')}: {packages}", file=sys.stderr) - raise SystemExit('canonical license fallback lacks verified source text') - PY - python3 scripts/collect_cargo_notices.py \ - "$RUNNER_TEMP/cargo-license-evidence/metadata.json" \ - "$RUNNER_TEMP/cargo-license-evidence/licenses.json" \ - "$RUNNER_TEMP/cargo-license-evidence/notices.json" - - uses: actions/setup-python@v5 - with: - python-version: '3.12' + python scripts/prepare_native_notices.py "$RUNNER_TEMP/cargo-license-evidence" - name: Build and verify a wheel carrying the exact notice bundle shell: bash run: | set -euo pipefail python -m pip install 'maturin>=1.7,<2.0' pytest - python -m pytest --confcutdir=tests/ci -q tests/ci/test_cargo_notice_bundle.py + python -m pytest --confcutdir=tests/ci -q tests/ci/test_cargo_notice_bundle.py tests/ci/test_prepare_native_notices.py evidence="$RUNNER_TEMP/cargo-license-evidence" - bundle="python/ktav/_third_party/NOTICE.json" - python scripts/cargo_notice_bundle.py build "$evidence" "$bundle" - cp "$bundle" "$evidence/NOTICE.json" maturin build --release --locked --out "$evidence/wheels" cmp Cargo.lock "$evidence/Cargo.lock" for wheel in "$evidence"/wheels/*.whl; do diff --git a/.github/workflows/musllinux-evidence.yml b/.github/workflows/musllinux-evidence.yml index 904b25a..fa23265 100644 --- a/.github/workflows/musllinux-evidence.yml +++ b/.github/workflows/musllinux-evidence.yml @@ -37,13 +37,8 @@ jobs: run: | set -euo pipefail evidence="$RUNNER_TEMP/musllinux-notices" - mkdir -p "$evidence" probe - cp Cargo.lock "$evidence/Cargo.lock" - rustc --version --verbose > "$evidence/rustc.txt" - cp "$(rustc --print sysroot)/share/doc/rust/COPYRIGHT-library.html" "$evidence/COPYRIGHT-library.html" - cargo metadata --locked --all-features --format-version 1 > "$evidence/metadata.json" - cargo about generate --locked --all-features --fail --config ci/about.toml --format json > "$evidence/licenses.json" - python scripts/cargo_notice_bundle.py build "$evidence" python/ktav/_third_party/NOTICE.json + mkdir -p probe + python scripts/prepare_native_notices.py "$evidence" cp python/ktav/_third_party/NOTICE.json probe/NOTICE.json cp "$evidence/rustc.txt" probe/bundle-rustc.txt cp "$evidence/Cargo.lock" probe/bundle-Cargo.lock diff --git a/scripts/prepare_native_notices.py b/scripts/prepare_native_notices.py new file mode 100644 index 0000000..80f9cfb --- /dev/null +++ b/scripts/prepare_native_notices.py @@ -0,0 +1,87 @@ +"""Подготовить notices существующей locked-сборки.""" + +from __future__ import annotations + +import json +import shutil +import subprocess +import sys +from pathlib import Path + +from cargo_notice_bundle import _build +from collect_cargo_notices import _collect + + +def _prepare(evidence: Path) -> None: + root = Path.cwd().resolve() + lock = root / "Cargo.lock" + output = root / "python" / "ktav" / "_third_party" / "NOTICE.json" + evidence = evidence.resolve() + if not lock.is_file() or lock.is_symlink(): + raise ValueError("missing regular Cargo.lock; resolve dependencies first") + if evidence.is_relative_to(root): + raise ValueError("notice evidence must be outside the project source directory") + if evidence.exists() and (not evidence.is_dir() or any(evidence.iterdir())): + raise ValueError("notice evidence directory must be empty") + if output.exists() or output.is_symlink(): + raise ValueError("existing native notice bundle must not be overwritten") + original_lock = lock.read_bytes() + raw_metadata = subprocess.check_output( + ["cargo", "metadata", "--locked", "--all-features", "--format-version", "1"] + ) + metadata = json.loads(raw_metadata) + for package in metadata["packages"]: + source = Path(package["manifest_path"]).resolve().parent + if evidence.is_relative_to(source): + raise ValueError("notice evidence must be outside every Cargo package source") + evidence.mkdir(parents=True, exist_ok=True) + (evidence / "Cargo.lock").write_bytes(original_lock) + (evidence / "metadata.json").write_bytes(raw_metadata) + rustc = subprocess.check_output(["rustc", "--version", "--verbose"]) + (evidence / "rustc.txt").write_bytes(rustc) + sysroot = subprocess.check_output(["rustc", "--print", "sysroot"], text=True).strip() + shutil.copyfile( + Path(sysroot) / "share" / "doc" / "rust" / "COPYRIGHT-library.html", + evidence / "COPYRIGHT-library.html", + ) + raw_licenses = subprocess.check_output( + [ + "cargo", + "about", + "generate", + "--locked", + "--all-features", + "--fail", + "--config", + "ci/about.toml", + "--format", + "json", + ] + ) + (evidence / "licenses.json").write_bytes(raw_licenses) + notices = _collect(metadata, json.loads(raw_licenses)) + (evidence / "notices.json").write_text( + json.dumps(notices, ensure_ascii=False, indent=2) + "\n", encoding="utf-8" + ) + data = _build(evidence) + if lock.read_bytes() != original_lock: + raise ValueError("Cargo.lock changed while generating notices") + (evidence / "NOTICE.json").write_bytes(data) + output.parent.mkdir(parents=True, exist_ok=True) + with output.open("xb") as destination: + destination.write(data) + print(f"Locked native notice bundle prepared for {len(metadata['packages'])} packages") + + +def main() -> None: + """Подготовить evidence без изменения Cargo.lock и версии core.""" + if len(sys.argv) != 2: + raise SystemExit("usage: prepare_native_notices.py EMPTY_EVIDENCE_DIRECTORY") + try: + _prepare(Path(sys.argv[1])) + except (OSError, ValueError, KeyError, TypeError, subprocess.CalledProcessError) as exc: + raise SystemExit(str(exc)) from exc + + +if __name__ == "__main__": + main() diff --git a/tests/ci/test_prepare_native_notices.py b/tests/ci/test_prepare_native_notices.py new file mode 100644 index 0000000..5d27220 --- /dev/null +++ b/tests/ci/test_prepare_native_notices.py @@ -0,0 +1,54 @@ +"""Проверить защиту подготовки release notices.""" + +from __future__ import annotations + +import subprocess +import sys +from pathlib import Path + +import pytest + +ROOT = Path(__file__).resolve().parents[2] +SCRIPT = ROOT / "scripts" / "prepare_native_notices.py" + + +def _run(project: Path, evidence: Path) -> subprocess.CompletedProcess[str]: + return subprocess.run( + [sys.executable, str(SCRIPT), str(evidence)], + cwd=project, + capture_output=True, + text=True, + ) + + +@pytest.mark.parametrize("condition", ["missing lock", "nested evidence", "nonempty", "bundle"]) +def test_rejects_unsafe_staging_before_cargo(tmp_path: Path, condition: str) -> None: + project = tmp_path / "project" + project.mkdir() + evidence = tmp_path / "evidence" + if condition != "missing lock": + (project / "Cargo.lock").write_bytes(b"version = 4\n") + if condition == "nested evidence": + evidence = project / "evidence" + elif condition == "nonempty": + evidence.mkdir() + (evidence / "keep.txt").write_bytes(b"preserve") + elif condition == "bundle": + bundle = project / "python" / "ktav" / "_third_party" / "NOTICE.json" + bundle.parent.mkdir(parents=True) + bundle.write_bytes(b"preserve") + result = _run(project, evidence) + assert result.returncode != 0 + expected = { + "missing lock": "missing regular Cargo.lock", + "nested evidence": "outside the project source directory", + "nonempty": "evidence directory must be empty", + "bundle": "existing native notice bundle", + } + assert expected[condition] in result.stderr + if condition == "nonempty": + assert (evidence / "keep.txt").read_bytes() == b"preserve" + if condition == "bundle": + assert bundle.read_bytes() == b"preserve" + if condition != "missing lock": + assert (project / "Cargo.lock").read_bytes() == b"version = 4\n" From 192476152c50924237ee26d13f3163b5d2da15f9 Mon Sep 17 00:00:00 2001 From: Arch-AI-tech Date: Thu, 29 Oct 2026 11:51:42 +0100 Subject: [PATCH 37/54] ci: verify native notices against release 0.8.1 source Build both musllinux architectures from the exact published Python source commit, overlaying only the PR's packaging tools and test-only fixtures. Leave release manifests and binding sources unchanged and verify registry core 0.8.1 provenance before generating locked notices. Run the release tag's native suites, ELF/RECORD/link-map gates and LLVM/TLS/panic/DSO lifecycle checks without publish permissions. Validate both evidence workflows in the main CI syntax guard. This is a new resolved build of release source, not a historical reproduction or a release workflow execution. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/CI.yml | 2 +- .../workflows/release-license-evidence.yml | 294 ++++++++++++++++++ 2 files changed, 295 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/release-license-evidence.yml diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index ae0acec..3a054f2 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -66,7 +66,7 @@ jobs: run: mypy python - name: Validate musllinux workflow syntax run: | - ruby -e 'require "yaml"; workflow = YAML.load_file(ARGV.fetch(0)); jobs = workflow.fetch("jobs"); abort "missing musllinux jobs" unless %w[static-libgcc musllinux-runtime].all? { |name| jobs.key?(name) }' .github/workflows/musllinux-evidence.yml + ruby -e 'require "yaml"; ARGV.each { |file| jobs = YAML.load_file(file).fetch("jobs"); abort "missing musllinux jobs in #{file}" unless %w[static-libgcc musllinux-runtime].all? { |name| jobs.key?(name) } }' .github/workflows/musllinux-evidence.yml .github/workflows/release-license-evidence.yml rust-lint: name: Rust fmt + clippy diff --git a/.github/workflows/release-license-evidence.yml b/.github/workflows/release-license-evidence.yml new file mode 100644 index 0000000..c486287 --- /dev/null +++ b/.github/workflows/release-license-evidence.yml @@ -0,0 +1,294 @@ +name: Release 0.8.1 musllinux license evidence + +on: + pull_request: + +permissions: + contents: read + +jobs: + static-libgcc: + name: Release 0.8.1 musllinux build (${{ matrix.target }}) + runs-on: ubuntu-latest + strategy: + fail-fast: false + max-parallel: 1 + matrix: + include: + - target: x86_64 + image: ghcr.io/rust-cross/rust-musl-cross@sha256:ce75e9174325d4fbb3de85c309e2d7ca29f7500169bc4b5d2c611ff7e86d549a + - target: aarch64 + image: ghcr.io/rust-cross/rust-musl-cross@sha256:ecae5dd62d1c938c14f8071d36c16fa699860aace03bfb5284fb1216474d2643 + steps: + - uses: actions/checkout@v4 + - name: Preserve packaging tools from the PR + shell: python + run: | + import os + import shutil + from pathlib import Path + stage = Path(os.environ['RUNNER_TEMP']) / 'release-packaging-tools' + stage.mkdir() + paths = [ + 'scripts/prepare_native_notices.py', 'scripts/cargo_notice_bundle.py', + 'scripts/collect_cargo_notices.py', 'scripts/add_llvm_unwind_notice.py', + 'scripts/check_unwinder_maps.py', 'scripts/check_musllinux_wheel.py', + 'scripts/check_rust_provenance.py', 'scripts/musllinux-unwind-linker.sh', + 'scripts/setup-llvm-unwind.sh', 'scripts/musllinux-dso.c', 'ci/about.toml', + ] + for name in paths: + target = stage / name + target.parent.mkdir(parents=True, exist_ok=True) + shutil.copy2(name, target) + shutil.copytree('tests/fixtures/unwind-probe', stage / 'tests/fixtures/unwind-probe') + (stage / 'packaging-tools-source.txt').write_text(os.environ['GITHUB_SHA'], encoding='utf-8') + - uses: actions/checkout@v4 + with: + ref: 566bbfce6c6b48387d7a0a089939a26744013dea + persist-credentials: false + - name: Restore packaging tools without changing release sources + shell: python + run: | + import os + import shutil + import subprocess + from pathlib import Path + stage = Path(os.environ['RUNNER_TEMP']) / 'release-packaging-tools' + for path in stage.rglob('*'): + if path.is_file(): + target = Path(path.relative_to(stage)) + target.parent.mkdir(parents=True, exist_ok=True) + shutil.copy2(path, target) + revision = subprocess.check_output(['git', 'rev-parse', 'HEAD'], text=True).strip() + if revision != '566bbfce6c6b48387d7a0a089939a26744013dea': + raise SystemExit('unexpected release source revision') + subprocess.run(['git', 'diff', '--exit-code', '--', 'Cargo.toml', 'src', 'python'], check=True) + Path('probe').mkdir(exist_ok=True) + Path('probe/release-source.txt').write_text(revision, encoding='utf-8') + shutil.copy2('packaging-tools-source.txt', 'probe/packaging-tools-source.txt') + - uses: actions/setup-python@v5 + with: + python-version: '3.12' + - uses: dtolnay/rust-toolchain@1.99.1 + with: + components: rust-docs + - name: Resolve and verify released Rust core + shell: bash + run: | + set -euo pipefail + cargo generate-lockfile + cargo update -p ktav --precise 0.8.1 + python scripts/check_rust_provenance.py + - uses: taiki-e/install-action@v2 + with: + tool: cargo-about@0.9.3 + - name: Prepare notices for the locked build + shell: bash + run: | + set -euo pipefail + evidence="$RUNNER_TEMP/musllinux-notices" + mkdir -p probe + python scripts/prepare_native_notices.py "$evidence" + cp python/ktav/_third_party/NOTICE.json probe/NOTICE.json + cp "$evidence/rustc.txt" probe/bundle-rustc.txt + cp "$evidence/Cargo.lock" probe/bundle-Cargo.lock + - uses: actions/checkout@v4 + with: + repository: llvm/llvm-project + ref: 87f0227cb60147a26a1eeb4fb06e3b505e9c7261 + path: llvm-unwind-source + persist-credentials: false + sparse-checkout: | + cmake + runtimes + llvm/cmake + libcxx/include + libunwind + - name: Verify DSO finalization with glibc dlclose + shell: bash + run: | + set -euo pipefail + host="$RUNNER_TEMP/dso-lifecycle" + mkdir -p "$host" + cc -fPIC -fvisibility=hidden -c scripts/musllinux-dso.c -o "$host/dso.o" + c++ -fPIC -fvisibility=hidden -fno-exceptions -fno-rtti -fuse-cxa-atexit \ + -c tests/fixtures/unwind-probe/crt-probe.cpp -o "$host/probe.o" + cc -shared -nostartfiles -nodefaultlibs "$host/probe.o" "$host/dso.o" -lc -o "$host/probe.so" + cc tests/fixtures/unwind-probe/crt-runner.c -ldl -o "$host/runner" + "$host/runner" "$host/probe.so" + - name: Check linker guard failures + shell: bash + run: | + set -euo pipefail + if TARGET_CC=/bin/false scripts/musllinux-unwind-linker.sh -lnot-gcc; then + printf '%s\n' 'linker accepted a missing gcc_s input' >&2 + exit 1 + fi + if RUST_MUSL_CROSS_TARGET=unknown bash -c 'source scripts/setup-llvm-unwind.sh'; then + printf '%s\n' 'setup accepted an unknown target' >&2 + exit 1 + fi + - name: Build musllinux wheel with source-built LLVM unwinder + uses: PyO3/maturin-action@v1 + with: + target: ${{ matrix.target }} + args: --release --out dist --locked + manylinux: musllinux_1_2 + container: ${{ matrix.image }} + rust-toolchain: '1.99.1' + before-script-linux: | + source scripts/setup-llvm-unwind.sh + archive="${KTAV_UNWIND_ARCHIVE:?}" + mkdir -p probe + rustc --version --verbose > probe/rustc.txt + cmp probe/rustc.txt probe/bundle-rustc.txt + cmp Cargo.lock probe/bundle-Cargo.lock + grep -Fx 'release: 1.99.1' probe/rustc.txt + grep -Fx 'commit-hash: 58cf8f9c35b355199727627d07ae34a4e2846d67' probe/rustc.txt + { + "${TARGET_CC}" --version + "${TARGET_CC}" -v 2>&1 + cmake --version + sha256sum "$archive" + "${TARGET_AR:?}" t "$archive" + } > probe/unwinder-evidence.txt + export KTAV_LINK_MAP_DIR="$GITHUB_WORKSPACE/probe/link-maps" + cargo metadata --locked --format-version 1 | python3 -c 'import json,sys; d=json.load(sys.stdin); v=[p["version"] for p in d["packages"] if p["name"]=="pyo3"]; assert v==["0.28.3"],v' + cargo generate-lockfile --manifest-path tests/fixtures/unwind-probe/Cargo.toml + RUSTFLAGS="-C target-feature=-crt-static" cargo build --locked --release --manifest-path tests/fixtures/unwind-probe/Cargo.toml --target "${RUST_MUSL_CROSS_TARGET:?}" + mkdir -p probe + cp "tests/fixtures/unwind-probe/target/${RUST_MUSL_CROSS_TARGET}/release/lib_unwind_probe.so" probe/_unwind_probe.so + cp tests/fixtures/unwind-probe/Cargo.lock probe/Cargo.lock + - name: Verify wheel native payload + shell: python + run: | + import re + import subprocess + import sys + from pathlib import Path + + wheels = list(Path('dist').glob('*.whl')) + if len(wheels) != 1: + raise SystemExit(f'expected one wheel, got {wheels!r}') + subprocess.run([sys.executable, 'scripts/check_musllinux_wheel.py', str(wheels[0])], check=True) + subprocess.run([ + sys.executable, 'scripts/cargo_notice_bundle.py', 'verify', + str(wheels[0]), 'probe/NOTICE-LLVM.json', + ], check=True) + if Path('Cargo.lock').read_bytes() != Path('probe/bundle-Cargo.lock').read_bytes(): + raise SystemExit('Cargo.lock changed after notice generation') + binary = Path('probe/_unwind_probe.so') + dynamic = subprocess.check_output(['readelf', '-d', str(binary)], text=True) + print(f'{binary}:\n{dynamic}') + if re.search(r'Shared library: \[libgcc_s(?:\.|-)', dynamic): + raise SystemExit(f'{binary} still dynamically requires libgcc_s') + symbols = subprocess.check_output(['readelf', '--dyn-syms', '--wide', str(binary)], text=True) + exports = [parts[7] for line in symbols.splitlines() if len(parts := line.split()) >= 8 + and parts[4] in ('GLOBAL', 'WEAK') and parts[5] in ('DEFAULT', 'PROTECTED') + and parts[6] != 'UND'] + if exports != ['PyInit__unwind_probe']: + raise SystemExit(f'{binary}: unexpected exported symbols: {exports!r}') + subprocess.run([ + sys.executable, 'scripts/check_unwinder_maps.py', 'probe/link-maps', + ], check=True) + dynamic = subprocess.check_output(['readelf', '-d', 'probe/crt_probe.so'], text=True) + if re.findall(r'Shared library: \[([^]]+)\]', dynamic) != ['libc.so']: + raise SystemExit('unexpected CRT probe runtime dependencies') + - uses: actions/upload-artifact@v4 + if: always() + with: + name: release-081-musllinux-${{ matrix.target }} + path: | + dist/*.whl + Cargo.lock + probe/_unwind_probe.so + probe/crt_probe.so + probe/crt_runner + probe/crt-probe.map + probe/Cargo.lock + probe/unwinder-evidence.txt + probe/llvm-unwind-build/CMakeCache.txt + probe/rustc.txt + probe/release-source.txt + probe/packaging-tools-source.txt + probe/NOTICE-LLVM.json + probe/NOTICE.json + probe/bundle-rustc.txt + probe/bundle-Cargo.lock + probe/link-maps/ + if-no-files-found: error + + musllinux-runtime: + name: Release 0.8.1 musllinux runtime (${{ matrix.target }}) + needs: static-libgcc + runs-on: ${{ matrix.runner }} + strategy: + fail-fast: false + matrix: + include: + - target: x86_64 + runner: ubuntu-24.04 + image: quay.io/pypa/musllinux_1_2_x86_64 + - target: aarch64 + runner: ubuntu-24.04-arm + image: quay.io/pypa/musllinux_1_2_aarch64 + steps: + - uses: actions/checkout@v4 + with: + ref: 566bbfce6c6b48387d7a0a089939a26744013dea + submodules: recursive + persist-credentials: false + - uses: actions/setup-python@v5 + with: + python-version: '3.12' + - uses: actions/download-artifact@v4 + with: + name: release-081-musllinux-${{ matrix.target }} + path: payload + - name: Download pure Python test dependencies + run: 'python -m pip download --only-binary=:all: --dest payload/testdeps pytest' + - name: Verify native parsing and PyO3 panic conversion + shell: bash + env: + MUSL_IMAGE: ${{ matrix.image }} + run: | + set -euo pipefail + stage="$(mktemp -d)" + mkdir -p "$stage/spec" + cp -R tests "$stage/tests" + cp -R spec/versions "$stage/spec/versions" + cp pyproject.toml "$stage/pyproject.toml" + docker pull "$MUSL_IMAGE" + docker run --rm -i --network none \ + --mount "type=bind,source=$PWD/payload,target=/payload,readonly" \ + --mount "type=bind,source=$stage,target=/work,readonly" \ + --workdir /work "$MUSL_IMAGE" /bin/sh -ec ' + /opt/python/cp312-cp312/bin/python -m pip install --no-index --find-links=/payload/testdeps pytest + /opt/python/cp312-cp312/bin/python -m pip install --no-index --no-deps /payload/dist/*.whl + runner_dir="$(mktemp -d)" + cp /payload/probe/crt_runner "$runner_dir/crt-runner" + chmod 755 "$runner_dir/crt-runner" + "$runner_dir/crt-runner" /payload/probe/crt_probe.so + PYTHONPATH=/payload/probe /opt/python/cp312-cp312/bin/python - + KTAV_REQUIRE_NATIVE=1 PYTHONDONTWRITEBYTECODE=1 PYTHONPATH=/payload/probe \ + /opt/python/cp312-cp312/bin/python -m pytest -p no:cacheprovider -q \ + tests/test_conformance.py tests/api tests/parse tests/serialize tests/roundtrip tests/spec + ' <<'PY' + import _unwind_probe + import ktav + import ktav._core + + for _ in range(2): + if not _unwind_probe.tls_drop_probe(): + raise AssertionError('Rust TLS destructor did not run') + try: + _unwind_probe.panic_probe() + except BaseException as exc: + if type(exc).__name__ != 'PanicException' or str(exc) != 'musllinux unwind probe': + raise + else: + raise AssertionError('Rust panic was not converted to PanicException') + if ktav.loads('answer: 42') != {'answer': 42}: + raise AssertionError('native parser returned an unexpected value') + print('TLS and panic conversion verified') + PY From 40f9ff9467723457e4893c6c8ba49ddcbb05439f Mon Sep 17 00:00:00 2001 From: Arch-AI-tech Date: Thu, 29 Oct 2026 12:12:56 +0100 Subject: [PATCH 38/54] test: stage release documentation for native checks Include the release tag's three README files in the read-only runtime stage so documentation example tests inspect the real release inputs. Do not skip tests or change documentation content. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/release-license-evidence.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/release-license-evidence.yml b/.github/workflows/release-license-evidence.yml index c486287..8f75578 100644 --- a/.github/workflows/release-license-evidence.yml +++ b/.github/workflows/release-license-evidence.yml @@ -258,6 +258,10 @@ jobs: cp -R tests "$stage/tests" cp -R spec/versions "$stage/spec/versions" cp pyproject.toml "$stage/pyproject.toml" + mkdir -p "$stage/docs/ru" "$stage/docs/zh" + cp README.md "$stage/README.md" + cp docs/ru/README.ru.md "$stage/docs/ru/README.ru.md" + cp docs/zh/README.zh.md "$stage/docs/zh/README.zh.md" docker pull "$MUSL_IMAGE" docker run --rm -i --network none \ --mount "type=bind,source=$PWD/payload,target=/payload,readonly" \ From 6668fb31bff28f05740d10cee7195cbabf793c4d Mon Sep 17 00:00:00 2001 From: Arch-AI-tech Date: Thu, 29 Oct 2026 12:29:30 +0100 Subject: [PATCH 39/54] ci: use verified musllinux pipeline for releases Make the proven source-specific musllinux build and runtime checks a reusable workflow. PR verification passes the pinned 0.8.1 source SHA; production passes its own commit instead of silently building another revision. Keep core provenance, notices, ABI and lifecycle gates. Gate publication on the reusable workflow. Upload distribution wheels separately from audit evidence and download only wheels-* artifacts for publication. Add CI assertions for both callers and publish wiring. No release or publication is invoked by this change. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/CI.yml | 26 ++++++++++++-- .github/workflows/release-license-check.yml | 13 +++++++ .../workflows/release-license-evidence.yml | 35 +++++++++++++------ .github/workflows/release.yml | 23 ++++++------ 4 files changed, 74 insertions(+), 23 deletions(-) create mode 100644 .github/workflows/release-license-check.yml diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 3a054f2..065c95c 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -64,9 +64,31 @@ jobs: run: ruff format --check . - name: Mypy run: mypy python - - name: Validate musllinux workflow syntax + - name: Validate native license workflow integration run: | - ruby -e 'require "yaml"; ARGV.each { |file| jobs = YAML.load_file(file).fetch("jobs"); abort "missing musllinux jobs in #{file}" unless %w[static-libgcc musllinux-runtime].all? { |name| jobs.key?(name) } }' .github/workflows/musllinux-evidence.yml .github/workflows/release-license-evidence.yml + ruby - <<'RUBY' + require 'yaml' + %w[musllinux-evidence.yml release-license-evidence.yml].each do |file| + jobs = YAML.load_file(".github/workflows/#{file}").fetch('jobs') + abort "missing musllinux jobs in #{file}" unless %w[static-libgcc musllinux-runtime].all? { |name| jobs.key?(name) } + end + path = './.github/workflows/release-license-evidence.yml' + reusable = YAML.load_file(path) + triggers = reusable['on'] || reusable[true] + abort 'missing workflow_call contract' unless triggers.fetch('workflow_call').fetch('inputs').fetch('source-ref').fetch('required') + caller = YAML.load_file('.github/workflows/release-license-check.yml').fetch('jobs').fetch('musllinux-release') + abort 'wrong release evidence caller' unless caller.fetch('uses') == path + abort 'release evidence source is not pinned' unless caller.fetch('with').fetch('source-ref').match?(/\A[0-9a-f]{40}\z/) + release = YAML.load_file('.github/workflows/release.yml').fetch('jobs') + job = release.fetch('musllinux') + abort 'release uses a different build path' unless job.fetch('uses') == path + abort 'release source must follow the caller commit' unless job.fetch('with').fetch('source-ref').include?('github.sha') + abort 'publish is not gated on musllinux verification' unless release.fetch('release').fetch('needs').include?('musllinux') + %w[release github-release].each do |name| + downloads = release.fetch(name).fetch('steps').select { |step| step['uses'].to_s.start_with?('actions/download-artifact@') } + abort "unsafe artifact download in #{name}" unless downloads.length == 1 && downloads[0].fetch('with').fetch('pattern') == 'wheels-*' + end + RUBY rust-lint: name: Rust fmt + clippy diff --git a/.github/workflows/release-license-check.yml b/.github/workflows/release-license-check.yml new file mode 100644 index 0000000..0da174b --- /dev/null +++ b/.github/workflows/release-license-check.yml @@ -0,0 +1,13 @@ +name: Release source license checks + +on: + pull_request: + +permissions: + contents: read + +jobs: + musllinux-release: + uses: ./.github/workflows/release-license-evidence.yml + with: + source-ref: 566bbfce6c6b48387d7a0a089939a26744013dea diff --git a/.github/workflows/release-license-evidence.yml b/.github/workflows/release-license-evidence.yml index 8f75578..5962dda 100644 --- a/.github/workflows/release-license-evidence.yml +++ b/.github/workflows/release-license-evidence.yml @@ -1,15 +1,22 @@ -name: Release 0.8.1 musllinux license evidence +name: Verified musllinux release build on: - pull_request: + workflow_call: + inputs: + source-ref: + description: Exact source commit to build + required: true + type: string permissions: contents: read jobs: static-libgcc: - name: Release 0.8.1 musllinux build (${{ matrix.target }}) + name: Verified musllinux build (${{ matrix.target }}) runs-on: ubuntu-latest + env: + SOURCE_REF: ${{ inputs.source-ref }} strategy: fail-fast: false max-parallel: 1 @@ -21,12 +28,15 @@ jobs: image: ghcr.io/rust-cross/rust-musl-cross@sha256:ecae5dd62d1c938c14f8071d36c16fa699860aace03bfb5284fb1216474d2643 steps: - uses: actions/checkout@v4 - - name: Preserve packaging tools from the PR + - name: Preserve packaging tools from the workflow revision shell: python run: | import os + import re import shutil from pathlib import Path + if not re.fullmatch(r'[0-9a-f]{40}', os.environ['SOURCE_REF']): + raise SystemExit('source-ref must be an exact commit SHA') stage = Path(os.environ['RUNNER_TEMP']) / 'release-packaging-tools' stage.mkdir() paths = [ @@ -44,7 +54,7 @@ jobs: (stage / 'packaging-tools-source.txt').write_text(os.environ['GITHUB_SHA'], encoding='utf-8') - uses: actions/checkout@v4 with: - ref: 566bbfce6c6b48387d7a0a089939a26744013dea + ref: ${{ inputs.source-ref }} persist-credentials: false - name: Restore packaging tools without changing release sources shell: python @@ -60,7 +70,7 @@ jobs: target.parent.mkdir(parents=True, exist_ok=True) shutil.copy2(path, target) revision = subprocess.check_output(['git', 'rev-parse', 'HEAD'], text=True).strip() - if revision != '566bbfce6c6b48387d7a0a089939a26744013dea': + if revision != os.environ['SOURCE_REF']: raise SystemExit('unexpected release source revision') subprocess.run(['git', 'diff', '--exit-code', '--', 'Cargo.toml', 'src', 'python'], check=True) Path('probe').mkdir(exist_ok=True) @@ -194,10 +204,15 @@ jobs: dynamic = subprocess.check_output(['readelf', '-d', 'probe/crt_probe.so'], text=True) if re.findall(r'Shared library: \[([^]]+)\]', dynamic) != ['libc.so']: raise SystemExit('unexpected CRT probe runtime dependencies') + - uses: actions/upload-artifact@v4 + with: + name: wheels-musllinux-${{ matrix.target }} + path: dist/*.whl + if-no-files-found: error - uses: actions/upload-artifact@v4 if: always() with: - name: release-081-musllinux-${{ matrix.target }} + name: musllinux-release-evidence-${{ matrix.target }} path: | dist/*.whl Cargo.lock @@ -219,7 +234,7 @@ jobs: if-no-files-found: error musllinux-runtime: - name: Release 0.8.1 musllinux runtime (${{ matrix.target }}) + name: Verified musllinux runtime (${{ matrix.target }}) needs: static-libgcc runs-on: ${{ matrix.runner }} strategy: @@ -235,7 +250,7 @@ jobs: steps: - uses: actions/checkout@v4 with: - ref: 566bbfce6c6b48387d7a0a089939a26744013dea + ref: ${{ inputs.source-ref }} submodules: recursive persist-credentials: false - uses: actions/setup-python@v5 @@ -243,7 +258,7 @@ jobs: python-version: '3.12' - uses: actions/download-artifact@v4 with: - name: release-081-musllinux-${{ matrix.target }} + name: musllinux-release-evidence-${{ matrix.target }} path: payload - name: Download pure Python test dependencies run: 'python -m pip download --only-binary=:all: --dest payload/testdeps pytest' diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index fc9663e..b811df9 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -19,7 +19,7 @@ env: jobs: # ------------------------------------------------------------------ - # Linux — manylinux + musllinux, x86_64 + aarch64. + # Linux — manylinux, x86_64 + aarch64. # ------------------------------------------------------------------ linux: name: wheels linux / ${{ matrix.target }} @@ -36,13 +36,13 @@ jobs: - uses: dtolnay/rust-toolchain@stable - # Single lock for both container builds; pinned to the released version. + # Lock закреплён на выпущенном core. - name: Generate Cargo.lock (no --locked) run: cargo generate-lockfile && cargo update -p ktav --precise 0.8.1 - name: Check Rust provenance run: python scripts/check_rust_provenance.py - # Both builds consume the one host-generated lock; --locked fails closed. + # Сборка использует тот же lock. - name: Build manylinux wheels uses: PyO3/maturin-action@v1 with: @@ -50,18 +50,17 @@ jobs: args: --release --out dist --locked manylinux: auto - - name: Build musllinux wheels - uses: PyO3/maturin-action@v1 - with: - target: ${{ matrix.target }} - args: --release --out dist --locked - manylinux: musllinux_1_2 - - uses: actions/upload-artifact@v7 with: name: wheels-linux-${{ matrix.target }} path: dist + musllinux: + name: verified musllinux wheels + uses: ./.github/workflows/release-license-evidence.yml + with: + source-ref: ${{ github.sha }} + # ------------------------------------------------------------------ # macOS — x86_64 + aarch64 (Apple Silicon). One wheel each; macOS # universal2 is available via `--target universal2-apple-darwin` but @@ -350,7 +349,7 @@ jobs: runs-on: ubuntu-latest # Both engines must be green at the exact published SHA: native # (verify-native) AND pure (verify-pure). - needs: [linux, macos, windows, sdist, pure-wheel, verify-pure, verify-native] + needs: [linux, musllinux, macos, windows, sdist, pure-wheel, verify-pure, verify-native] if: startsWith(github.ref, 'refs/tags/v') && inputs.dry-run != 'true' environment: name: pypi @@ -367,6 +366,7 @@ jobs: - uses: actions/download-artifact@v8 with: + pattern: wheels-* path: dist merge-multiple: true @@ -441,6 +441,7 @@ jobs: - uses: actions/download-artifact@v8 with: + pattern: wheels-* path: dist merge-multiple: true From 5dae44ba93073a4ec530e2778cc8fa3a0f53496b Mon Sep 17 00:00:00 2001 From: Arch-AI-tech Date: Thu, 29 Oct 2026 13:08:54 +0100 Subject: [PATCH 40/54] ci: verify notices in all native release targets Share native builds for GNU/Linux, macOS and Windows across both architectures between PR release-source checks and production. Keep source commits, registry core provenance and locked graph evidence explicit without changing binding sources or versions. Bind container compiler records to matching Rust std source evidence, verify wheel ABI/native format/architecture/RECORD and exact notice bytes, and retain native headers/imports separately for runtime audit. Smoke-check installed wheels only on matching runner architectures. Gate publication on the reusable native job; keep audit artifacts out of distribution downloads. Add regression tests and matrix/caller wiring assertions. Non-Cargo CRT/SDK licensing remains a separate audit. No release or publication is invoked. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/CI.yml | 14 ++ .github/workflows/native-release-build.yml | 186 ++++++++++++++++++++ .github/workflows/release-license-check.yml | 5 + .github/workflows/release.yml | 112 +----------- scripts/bind_native_compiler.py | 52 ++++++ scripts/check_musllinux_wheel.py | 10 +- scripts/check_native_wheel.py | 94 ++++++++++ tests/ci/test_bind_native_compiler.py | 51 ++++++ tests/ci/test_native_wheel.py | 69 ++++++++ 9 files changed, 482 insertions(+), 111 deletions(-) create mode 100644 .github/workflows/native-release-build.yml create mode 100644 scripts/bind_native_compiler.py create mode 100644 scripts/check_native_wheel.py create mode 100644 tests/ci/test_bind_native_compiler.py create mode 100644 tests/ci/test_native_wheel.py diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 065c95c..a365066 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -84,6 +84,20 @@ jobs: abort 'release uses a different build path' unless job.fetch('uses') == path abort 'release source must follow the caller commit' unless job.fetch('with').fetch('source-ref').include?('github.sha') abort 'publish is not gated on musllinux verification' unless release.fetch('release').fetch('needs').include?('musllinux') + native_path = './.github/workflows/native-release-build.yml' + native_workflow = YAML.load_file(native_path) + native_triggers = native_workflow['on'] || native_workflow[true] + abort 'missing native workflow contract' unless native_triggers.fetch('workflow_call').fetch('inputs').fetch('source-ref').fetch('required') + targets = native_workflow.fetch('jobs').fetch('native').fetch('strategy').fetch('matrix').fetch('include').map { |item| item.fetch('target') } + expected = %w[x86_64-unknown-linux-gnu aarch64-unknown-linux-gnu x86_64-apple-darwin aarch64-apple-darwin x86_64-pc-windows-msvc aarch64-pc-windows-msvc] + abort 'incomplete native release target matrix' unless targets.sort == expected.sort + caller = YAML.load_file('.github/workflows/release-license-check.yml').fetch('jobs').fetch('native-release') + abort 'wrong native release evidence caller' unless caller.fetch('uses') == native_path + abort 'native evidence source is not pinned' unless caller.fetch('with').fetch('source-ref').match?(/\A[0-9a-f]{40}\z/) + native = release.fetch('native') + abort 'production native build differs from CI' unless native.fetch('uses') == native_path + abort 'production native source must follow caller commit' unless native.fetch('with').fetch('source-ref').include?('github.sha') + abort 'publish is not gated on native verification' unless release.fetch('release').fetch('needs').include?('native') %w[release github-release].each do |name| downloads = release.fetch(name).fetch('steps').select { |step| step['uses'].to_s.start_with?('actions/download-artifact@') } abort "unsafe artifact download in #{name}" unless downloads.length == 1 && downloads[0].fetch('with').fetch('pattern') == 'wheels-*' diff --git a/.github/workflows/native-release-build.yml b/.github/workflows/native-release-build.yml new file mode 100644 index 0000000..1b61c3b --- /dev/null +++ b/.github/workflows/native-release-build.yml @@ -0,0 +1,186 @@ +name: Verified native release build + +on: + workflow_call: + inputs: + source-ref: + description: Exact source commit to build + required: true + type: string + +permissions: + contents: read + +jobs: + native: + name: Native release wheel (${{ matrix.label }}) + runs-on: ${{ matrix.runner }} + strategy: + fail-fast: false + max-parallel: 2 + matrix: + include: + - label: linux-x86_64 + target: x86_64-unknown-linux-gnu + runner: ubuntu-24.04 + - label: linux-aarch64 + target: aarch64-unknown-linux-gnu + runner: ubuntu-24.04-arm + - label: macos-x86_64 + target: x86_64-apple-darwin + runner: macos-latest + - label: macos-aarch64 + target: aarch64-apple-darwin + runner: macos-latest + - label: windows-x86_64 + target: x86_64-pc-windows-msvc + runner: windows-latest + - label: windows-aarch64 + target: aarch64-pc-windows-msvc + runner: windows-latest + env: + SOURCE_REF: ${{ inputs.source-ref }} + TARGET_TRIPLE: ${{ matrix.target }} + steps: + - uses: actions/checkout@v4 + - name: Preserve packaging tools from the workflow revision + shell: python + run: | + import os + import re + import shutil + from pathlib import Path + if not re.fullmatch(r'[0-9a-f]{40}', os.environ['SOURCE_REF']): + raise SystemExit('source-ref must be an exact commit SHA') + stage = Path(os.environ['RUNNER_TEMP']) / 'native-packaging-tools' + stage.mkdir() + paths = [ + 'scripts/prepare_native_notices.py', 'scripts/cargo_notice_bundle.py', + 'scripts/collect_cargo_notices.py', 'scripts/bind_native_compiler.py', + 'scripts/check_native_wheel.py', 'scripts/check_musllinux_wheel.py', + 'scripts/check_rust_provenance.py', 'ci/about.toml', + ] + for name in paths: + target = stage / name + target.parent.mkdir(parents=True, exist_ok=True) + shutil.copy2(name, target) + (stage / 'packaging-tools-source.txt').write_text(os.environ['GITHUB_SHA'], encoding='utf-8') + - uses: actions/checkout@v4 + with: + ref: ${{ inputs.source-ref }} + persist-credentials: false + - name: Restore tools without changing binding sources + shell: python + run: | + import os + import shutil + import subprocess + from pathlib import Path + stage = Path(os.environ['RUNNER_TEMP']) / 'native-packaging-tools' + for path in stage.rglob('*'): + if path.is_file(): + target = Path(path.relative_to(stage)) + target.parent.mkdir(parents=True, exist_ok=True) + shutil.copy2(path, target) + revision = subprocess.check_output(['git', 'rev-parse', 'HEAD'], text=True).strip() + if revision != os.environ['SOURCE_REF']: + raise SystemExit('unexpected release source revision') + subprocess.run(['git', 'diff', '--exit-code', '--', 'Cargo.toml', 'pyproject.toml', 'src', 'python'], check=True) + Path('probe').mkdir(exist_ok=True) + Path('probe/release-source.txt').write_text(revision, encoding='utf-8') + - uses: actions/setup-python@v5 + if: runner.os != 'Windows' + with: + python-version: '3.12' + - uses: actions/setup-python@v5 + if: runner.os == 'Windows' + with: + python-version: '3.12' + architecture: x64 + - uses: dtolnay/rust-toolchain@1.99.1 + with: + targets: ${{ matrix.target }} + components: rust-docs, llvm-tools-preview + - uses: taiki-e/install-action@v2 + with: + tool: cargo-about@0.9.3 + - name: Resolve and verify released Rust core + shell: python + run: | + import subprocess + import sys + subprocess.run(['cargo', 'generate-lockfile'], check=True) + subprocess.run(['cargo', 'update', '-p', 'ktav', '--precise', '0.8.1'], check=True) + subprocess.run([sys.executable, 'scripts/check_rust_provenance.py'], check=True) + - name: Prepare locked native notices + shell: python + run: | + import os + import shutil + import subprocess + import sys + from pathlib import Path + evidence = Path(os.environ['RUNNER_TEMP']) / 'native-license-evidence' + subprocess.run([sys.executable, 'scripts/prepare_native_notices.py', str(evidence)], check=True) + shutil.copy2(evidence / 'NOTICE.json', 'probe/NOTICE-host.json') + if sys.platform != 'linux': + shutil.copy2(evidence / 'NOTICE.json', 'probe/NOTICE-native.json') + - name: Build the native release wheel + uses: PyO3/maturin-action@v1 + with: + target: ${{ matrix.target }} + args: ${{ runner.os == 'Linux' && '--release --out dist --locked -i python3.12' || '--release --out dist --locked' }} + manylinux: ${{ runner.os == 'Linux' && 'auto' || '' }} + rust-toolchain: '1.99.1' + before-script-linux: | + python3.12 scripts/bind_native_compiler.py python/ktav/_third_party/NOTICE.json probe/NOTICE-native.json + cp probe/NOTICE-native.json python/ktav/_third_party/NOTICE.json + - name: Verify the exact wheel and native imports + shell: python + run: | + import os + import shutil + import subprocess + import sys + from pathlib import Path + evidence = Path(os.environ['RUNNER_TEMP']) / 'native-license-evidence' + wheels = list(Path('dist').glob('*.whl')) + if len(wheels) != 1: + raise SystemExit(f'expected one native wheel: {wheels!r}') + if Path('Cargo.lock').read_bytes() != (evidence / 'Cargo.lock').read_bytes(): + raise SystemExit('Cargo.lock changed after notice preparation') + subprocess.run([ + sys.executable, 'scripts/check_native_wheel.py', str(wheels[0]), + 'probe/NOTICE-native.json', str(evidence / 'native-report.txt'), + ], check=True) + shutil.copy2('probe/NOTICE-native.json', evidence / 'NOTICE-built.json') + shutil.copy2('probe/release-source.txt', evidence / 'release-source.txt') + shutil.copy2('packaging-tools-source.txt', evidence / 'packaging-tools-source.txt') + - name: Smoke-check the installed wheel when target matches runner + shell: python + run: | + import os + import platform + import subprocess + import sys + from pathlib import Path + host = {'amd64': 'x86_64', 'x86_64': 'x86_64', 'arm64': 'aarch64', 'aarch64': 'aarch64'}.get(platform.machine().lower()) + target = 'aarch64' if os.environ['TARGET_TRIPLE'].startswith('aarch64-') else 'x86_64' + if host != target: + print('Cross-target wheel inspected; execution requires matching architecture') + else: + wheel, = Path('dist').glob('*.whl') + subprocess.run([sys.executable, '-m', 'pip', 'install', '--no-index', '--no-deps', str(wheel)], check=True) + env = {**os.environ, 'PYTHONPATH': '', 'KTAV_REQUIRE_NATIVE': '1'} + subprocess.run([sys.executable, '-c', "import ktav, ktav._core; assert ktav.loads('answer: 42') == {'answer': 42}"], env=env, check=True) + - uses: actions/upload-artifact@v4 + with: + name: wheels-native-${{ matrix.label }} + path: dist/*.whl + if-no-files-found: error + - uses: actions/upload-artifact@v4 + if: always() + with: + name: native-license-evidence-${{ matrix.label }} + path: ${{ runner.temp }}/native-license-evidence/ + if-no-files-found: warn diff --git a/.github/workflows/release-license-check.yml b/.github/workflows/release-license-check.yml index 0da174b..78cf7d8 100644 --- a/.github/workflows/release-license-check.yml +++ b/.github/workflows/release-license-check.yml @@ -11,3 +11,8 @@ jobs: uses: ./.github/workflows/release-license-evidence.yml with: source-ref: 566bbfce6c6b48387d7a0a089939a26744013dea + + native-release: + uses: ./.github/workflows/native-release-build.yml + with: + source-ref: 566bbfce6c6b48387d7a0a089939a26744013dea diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b811df9..5e8c196 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -18,42 +18,11 @@ env: CARGO_TERM_COLOR: always jobs: - # ------------------------------------------------------------------ - # Linux — manylinux, x86_64 + aarch64. - # ------------------------------------------------------------------ - linux: - name: wheels linux / ${{ matrix.target }} - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - target: [x86_64, aarch64] - steps: - - uses: actions/checkout@v6 - - uses: actions/setup-python@v6 - with: - python-version: "3.12" - - - uses: dtolnay/rust-toolchain@stable - - # Lock закреплён на выпущенном core. - - name: Generate Cargo.lock (no --locked) - run: cargo generate-lockfile && cargo update -p ktav --precise 0.8.1 - - name: Check Rust provenance - run: python scripts/check_rust_provenance.py - - # Сборка использует тот же lock. - - name: Build manylinux wheels - uses: PyO3/maturin-action@v1 - with: - target: ${{ matrix.target }} - args: --release --out dist --locked - manylinux: auto - - - uses: actions/upload-artifact@v7 - with: - name: wheels-linux-${{ matrix.target }} - path: dist + native: + name: verified native wheels + uses: ./.github/workflows/native-release-build.yml + with: + source-ref: ${{ github.sha }} musllinux: name: verified musllinux wheels @@ -61,75 +30,6 @@ jobs: with: source-ref: ${{ github.sha }} - # ------------------------------------------------------------------ - # macOS — x86_64 + aarch64 (Apple Silicon). One wheel each; macOS - # universal2 is available via `--target universal2-apple-darwin` but - # splitting keeps download sizes smaller and is standard on PyPI. - # ------------------------------------------------------------------ - macos: - name: wheels macOS / ${{ matrix.target }} - runs-on: macos-latest - strategy: - fail-fast: false - matrix: - target: [x86_64, aarch64] - steps: - - uses: actions/checkout@v6 - - uses: actions/setup-python@v6 - with: - python-version: "3.12" - - - uses: dtolnay/rust-toolchain@stable - - name: Generate Cargo.lock (no --locked) - run: cargo generate-lockfile && cargo update -p ktav --precise 0.8.1 - - name: Check Rust provenance - run: python scripts/check_rust_provenance.py - - - name: Build wheels - uses: PyO3/maturin-action@v1 - with: - target: ${{ matrix.target }} - args: --release --out dist --locked - - - uses: actions/upload-artifact@v7 - with: - name: wheels-macos-${{ matrix.target }} - path: dist - - # ------------------------------------------------------------------ - # Windows — x64 + ARM64. - # ------------------------------------------------------------------ - windows: - name: wheels Windows / ${{ matrix.target }} - runs-on: windows-latest - strategy: - fail-fast: false - matrix: - target: [x64, aarch64] - steps: - - uses: actions/checkout@v6 - - uses: actions/setup-python@v6 - with: - python-version: "3.12" - architecture: x64 - - - uses: dtolnay/rust-toolchain@stable - - name: Generate Cargo.lock (no --locked) - run: cargo generate-lockfile && cargo update -p ktav --precise 0.8.1 - - name: Check Rust provenance - run: python scripts/check_rust_provenance.py - - - name: Build wheels - uses: PyO3/maturin-action@v1 - with: - target: ${{ matrix.target }} - args: --release --out dist --locked - - - uses: actions/upload-artifact@v7 - with: - name: wheels-windows-${{ matrix.target }} - path: dist - # ------------------------------------------------------------------ # Source distribution — pure tarball. Consumers without a matching # prebuilt wheel fall back to this and compile locally. @@ -349,7 +249,7 @@ jobs: runs-on: ubuntu-latest # Both engines must be green at the exact published SHA: native # (verify-native) AND pure (verify-pure). - needs: [linux, musllinux, macos, windows, sdist, pure-wheel, verify-pure, verify-native] + needs: [native, musllinux, sdist, pure-wheel, verify-pure, verify-native] if: startsWith(github.ref, 'refs/tags/v') && inputs.dry-run != 'true' environment: name: pypi diff --git a/scripts/bind_native_compiler.py b/scripts/bind_native_compiler.py new file mode 100644 index 0000000..917d7ac --- /dev/null +++ b/scripts/bind_native_compiler.py @@ -0,0 +1,52 @@ +"""Привязать notices к фактическому компилятору контейнера.""" + +from __future__ import annotations + +import json +import re +import subprocess +import sys +from pathlib import Path + + +def _identity(text: str) -> dict[str, str]: + fields = {} + for line in text.splitlines(): + key, separator, value = line.partition(": ") + if separator and key in {"release", "commit-hash", "LLVM version"}: + fields[key] = value + if set(fields) != {"release", "commit-hash", "LLVM version"}: + raise ValueError("incomplete compiler identity") + if not re.fullmatch(r"[0-9a-f]{40}", fields["commit-hash"]): + raise ValueError("unverified compiler source commit") + return fields + + +def _bind(bundle: dict, actual: str) -> bytes: + if bundle.get("schema_version") != 1: + raise ValueError("unexpected notice bundle schema") + original = bundle["rustc"] + if _identity(original) != _identity(actual): + raise ValueError("build compiler differs from Rust standard library evidence") + bundle["rust_standard_library"]["report_compiler"] = original + bundle["rustc"] = actual + return (json.dumps(bundle, ensure_ascii=False, sort_keys=True, indent=2) + "\n").encode() + + +def main() -> None: + """Сохранить compiler record без подмены версии std report.""" + if len(sys.argv) != 3: + raise SystemExit("usage: bind_native_compiler.py BUNDLE OUTPUT") + try: + bundle = json.loads(Path(sys.argv[1]).read_text(encoding="utf-8")) + actual = subprocess.check_output(["rustc", "--version", "--verbose"], text=True) + data = _bind(bundle, actual) + with Path(sys.argv[2]).open("xb") as output: + output.write(data) + except (OSError, ValueError, KeyError, TypeError, subprocess.CalledProcessError) as exc: + raise SystemExit(str(exc)) from exc + print("Native compiler source matches standard library evidence") + + +if __name__ == "__main__": + main() diff --git a/scripts/check_musllinux_wheel.py b/scripts/check_musllinux_wheel.py index d2827a7..08325d6 100644 --- a/scripts/check_musllinux_wheel.py +++ b/scripts/check_musllinux_wheel.py @@ -25,7 +25,7 @@ def _expected_machine(name: str) -> str: raise ValueError(f"unexpected musllinux wheel name: {name}") -def _module_bytes(path: Path) -> bytes: +def _module_bytes(path: Path, module: str = MODULE) -> bytes: with zipfile.ZipFile(path) as wheel: names = wheel.namelist() native = [] @@ -55,9 +55,9 @@ def _module_bytes(path: Path) -> bytes: ) if binary_name or binary_magic: native.append(info.filename) - if native != [MODULE]: + if native != [module]: raise ValueError(f"unexpected wheel native payload: {native!r}") - info = wheel.getinfo(MODULE) + info = wheel.getinfo(module) if info.file_size <= 0 or info.file_size > MAX_MODULE_BYTES: raise ValueError("invalid native module size") data = wheel.read(info) @@ -65,9 +65,9 @@ def _module_bytes(path: Path) -> bytes: if len(records) != 1 or records[0].count("/") != 1: raise ValueError(f"unexpected wheel RECORD: {records!r}") rows = list(csv.reader(io.StringIO(wheel.read(records[0]).decode("utf-8")))) - matching = [row for row in rows if row and row[0] == MODULE] + matching = [row for row in rows if row and row[0] == module] digest = base64.urlsafe_b64encode(hashlib.sha256(data).digest()).rstrip(b"=").decode() - if matching != [[MODULE, f"sha256={digest}", str(len(data))]]: + if matching != [[module, f"sha256={digest}", str(len(data))]]: raise ValueError("native module does not match wheel RECORD") return data diff --git a/scripts/check_native_wheel.py b/scripts/check_native_wheel.py new file mode 100644 index 0000000..91782a8 --- /dev/null +++ b/scripts/check_native_wheel.py @@ -0,0 +1,94 @@ +"""Проверить native payload и notices переносимого wheel.""" + +from __future__ import annotations + +import os +import re +import subprocess +import sys +import tempfile +import zipfile +from pathlib import Path + +from cargo_notice_bundle import _verify +from check_musllinux_wheel import _module_bytes + + +def _expected(name: str) -> tuple[str, str, set[str]]: + match = re.fullmatch(r"ktav-[^-]+-cp39-abi3-(.+)\.whl", name) + if not match: + raise ValueError("unexpected native wheel ABI tag") + platform = match.group(1) + if platform.startswith("win_"): + kind, module = "COFF", "ktav/_core.pyd" + elif platform.startswith("manylinux"): + kind, module = "ELF", "ktav/_core.abi3.so" + elif platform.startswith("macosx_"): + kind, module = "MachO", "ktav/_core.abi3.so" + else: + raise ValueError("unexpected native wheel platform") + if platform.endswith(("_arm64", "_aarch64")): + architectures = {"aarch64", "arm64"} + elif platform.endswith(("_amd64", "_x86_64")): + architectures = {"x86_64"} + else: + raise ValueError("unexpected native wheel architecture") + return kind, module, architectures + + +def _readobj() -> Path: + sysroot = subprocess.check_output(["rustc", "--print", "sysroot"], text=True).strip() + rustc = subprocess.check_output(["rustc", "--version", "--verbose"], text=True) + host = re.search(r"^host: (.+)$", rustc, re.MULTILINE) + if not host: + raise ValueError("missing Rust host triple") + name = "llvm-readobj.exe" if os.name == "nt" else "llvm-readobj" + tool = Path(sysroot) / "lib" / "rustlib" / host.group(1).strip() / "bin" / name + if not tool.is_file(): + raise ValueError("missing llvm-tools-preview component") + return tool + + +def _inspect(wheel: Path, bundle: Path) -> str: + kind, module, architectures = _expected(wheel.name) + _verify(wheel, bundle.read_bytes()) + data = _module_bytes(wheel, module) + magic = { + "COFF": (b"MZ",), + "ELF": (b"\x7fELF",), + "MachO": (b"\xcf\xfa\xed\xfe", b"\xfe\xed\xfa\xcf"), + } + if not data.startswith(magic[kind]): + raise ValueError("native payload format differs from wheel platform") + tool = _readobj() + with tempfile.TemporaryDirectory() as temp: + binary = Path(temp) / Path(module).name + binary.write_bytes(data) + header = subprocess.check_output([str(tool), "-h", str(binary)], text=True) + arch = re.search(r"^Arch:\s*(\S+)\s*$", header, re.MULTILINE) + if not arch or arch.group(1) not in architectures: + raise ValueError("native payload architecture differs from wheel tag") + if kind == "MachO": + command = ["otool", "-L", str(binary)] + else: + option = "--coff-imports" if kind == "COFF" else "--needed-libs" + command = [str(tool), option, str(binary)] + imports = subprocess.check_output(command, text=True) + return f"wheel: {wheel.name}\nmodule: {module}\n{header}\n{imports}" + + +def main() -> None: + """Сверить wheel и сохранить native headers/imports для аудита.""" + if len(sys.argv) != 4: + raise SystemExit("usage: check_native_wheel.py WHEEL BUNDLE REPORT") + try: + report = _inspect(Path(sys.argv[1]), Path(sys.argv[2])) + with Path(sys.argv[3]).open("x", encoding="utf-8") as output: + output.write(report) + except (OSError, ValueError, subprocess.CalledProcessError, zipfile.BadZipFile) as exc: + raise SystemExit(str(exc)) from exc + print(f"Native wheel payload and notices verified: {Path(sys.argv[1]).name}") + + +if __name__ == "__main__": + main() diff --git a/tests/ci/test_bind_native_compiler.py b/tests/ci/test_bind_native_compiler.py new file mode 100644 index 0000000..ee036c8 --- /dev/null +++ b/tests/ci/test_bind_native_compiler.py @@ -0,0 +1,51 @@ +"""Проверить соответствие compiler source и std evidence.""" + +from __future__ import annotations + +import importlib.util +import json +from pathlib import Path + +import pytest + +SCRIPT = Path(__file__).resolve().parents[2] / "scripts" / "bind_native_compiler.py" +spec = importlib.util.spec_from_file_location("bind_native_compiler", SCRIPT) +assert spec is not None and spec.loader is not None +module = importlib.util.module_from_spec(spec) +spec.loader.exec_module(module) +COMPILER = ( + "rustc 1.99.1\nhost: x86_64-unknown-linux-gnu\nrelease: 1.99.1\n" + f"commit-hash: {'a' * 40}\nLLVM version: 22.1.0\n" +) + + +def _bundle() -> dict: + return { + "schema_version": 1, + "rustc": COMPILER, + "rust_standard_library": {"text": "copyright", "sha256": "fixture"}, + "licenses": ["license text"], + } + + +def test_records_actual_host_without_replacing_std_source() -> None: + actual = COMPILER.replace("x86_64", "aarch64") + result = json.loads(module._bind(_bundle(), actual)) + assert result["rustc"] == actual + assert result["rust_standard_library"]["report_compiler"] == COMPILER + assert result["rust_standard_library"]["text"] == "copyright" + assert result["licenses"] == ["license text"] + + +@pytest.mark.parametrize( + ("before", "after"), + [("1.99.1", "1.99.2"), ("22.1.0", "23.0.0"), ("a" * 40, "b" * 40)], +) +def test_rejects_different_build_compiler(before: str, after: str) -> None: + with pytest.raises(ValueError, match="differs from Rust standard library evidence"): + module._bind(_bundle(), COMPILER.replace(before, after)) + + +def test_rejects_unknown_source_commit() -> None: + with pytest.raises(ValueError, match="unverified compiler source commit"): + module._bind(_bundle(), COMPILER.replace("a" * 40, "unknown")) diff --git a/tests/ci/test_native_wheel.py b/tests/ci/test_native_wheel.py new file mode 100644 index 0000000..9c1d0fe --- /dev/null +++ b/tests/ci/test_native_wheel.py @@ -0,0 +1,69 @@ +"""Проверить переносимые native wheel guards.""" + +from __future__ import annotations + +import base64 +import csv +import hashlib +import io +import subprocess +import sys +import zipfile +from pathlib import Path + +import pytest + +SCRIPT = Path(__file__).resolve().parents[2] / "scripts" / "check_native_wheel.py" +NOTICE = "ktav/_third_party/NOTICE.json" + + +def _run(wheel: Path, bundle: Path) -> subprocess.CompletedProcess[str]: + return subprocess.run( + [sys.executable, str(SCRIPT), str(wheel), str(bundle), str(bundle.parent / "report.txt")], + capture_output=True, + text=True, + ) + + +def _wheel(tmp_path: Path, platform: str, module: str, extra: str | None) -> tuple[Path, Path]: + wheel = tmp_path / f"ktav-0.8.1-cp39-abi3-{platform}.whl" + bundle = tmp_path / "NOTICE.json" + bundle.write_bytes(b"bundle") + payload = {NOTICE: b"bundle", module: b"not a native binary"} + if extra: + payload[extra] = b"unexpected" + rows = io.StringIO(newline="") + writer = csv.writer(rows, lineterminator="\n") + with zipfile.ZipFile(wheel, "w") as archive: + for name, data in payload.items(): + archive.writestr(name, data) + digest = base64.urlsafe_b64encode(hashlib.sha256(data).digest()).rstrip(b"=").decode() + writer.writerow([name, f"sha256={digest}", str(len(data))]) + archive.writestr("ktav-0.8.1.dist-info/RECORD", rows.getvalue()) + return wheel, bundle + + +@pytest.mark.parametrize( + ("platform", "module"), + [ + ("win_amd64", "ktav/_core.pyd"), + ("manylinux_2_17_x86_64", "ktav/_core.abi3.so"), + ("macosx_11_0_arm64", "ktav/_core.abi3.so"), + ], +) +def test_rejects_wrong_native_format(tmp_path: Path, platform: str, module: str) -> None: + result = _run(*_wheel(tmp_path, platform, module, None)) + assert result.returncode != 0 + assert "native payload format differs" in result.stderr + + +def test_rejects_additional_runtime_dll(tmp_path: Path) -> None: + result = _run(*_wheel(tmp_path, "win_amd64", "ktav/_core.pyd", "ktav/runtime.dll")) + assert result.returncode != 0 + assert "unexpected wheel native payload" in result.stderr + + +def test_rejects_unexpected_abi_tag(tmp_path: Path) -> None: + result = _run(tmp_path / "ktav-0.8.1-cp312-cp312-win_amd64.whl", tmp_path / "NOTICE.json") + assert result.returncode != 0 + assert "unexpected native wheel ABI tag" in result.stderr From a9e992f672e1d47bd99e2b483bac2d3f81849597 Mon Sep 17 00:00:00 2001 From: Arch-AI-tech Date: Thu, 29 Oct 2026 13:13:57 +0100 Subject: [PATCH 41/54] test: split compiler fixture import assertions Co-Authored-By: Claude Opus 5.5 --- tests/ci/test_bind_native_compiler.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/ci/test_bind_native_compiler.py b/tests/ci/test_bind_native_compiler.py index ee036c8..e48cdca 100644 --- a/tests/ci/test_bind_native_compiler.py +++ b/tests/ci/test_bind_native_compiler.py @@ -10,7 +10,8 @@ SCRIPT = Path(__file__).resolve().parents[2] / "scripts" / "bind_native_compiler.py" spec = importlib.util.spec_from_file_location("bind_native_compiler", SCRIPT) -assert spec is not None and spec.loader is not None +assert spec is not None +assert spec.loader is not None module = importlib.util.module_from_spec(spec) spec.loader.exec_module(module) COMPILER = ( From 162d061d6b1794a0b49e535e7764e7cb388e26ff Mon Sep 17 00:00:00 2001 From: Arch-AI-tech Date: Sat, 31 Oct 2026 17:58:30 +0100 Subject: [PATCH 42/54] fix: drop unused macOS wheel dylib dependencies Apply dead_strip_dylibs only to the shared macOS release wheel path, preserving existing Rust flags and all other platform behavior. The observed candidate libraries load libiconv without importing any iconv or charset symbol. Require actual wheel dependency reports to contain only libSystem, and reject iconv/charset undefined symbols or missing evidence. Retain CPU, archive, notice and matching-host smoke checks. Add regression cases without changing binding sources or versions. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/native-release-build.yml | 15 ++++++ scripts/check_native_wheel.py | 5 ++ scripts/macos_native_imports.py | 27 ++++++++++ tests/ci/test_macos_native_imports.py | 58 ++++++++++++++++++++++ 4 files changed, 105 insertions(+) create mode 100644 scripts/macos_native_imports.py create mode 100644 tests/ci/test_macos_native_imports.py diff --git a/.github/workflows/native-release-build.yml b/.github/workflows/native-release-build.yml index 1b61c3b..6ae1e3b 100644 --- a/.github/workflows/native-release-build.yml +++ b/.github/workflows/native-release-build.yml @@ -58,6 +58,7 @@ jobs: 'scripts/prepare_native_notices.py', 'scripts/cargo_notice_bundle.py', 'scripts/collect_cargo_notices.py', 'scripts/bind_native_compiler.py', 'scripts/check_native_wheel.py', 'scripts/check_musllinux_wheel.py', + 'scripts/macos_native_imports.py', 'scripts/check_rust_provenance.py', 'ci/about.toml', ] for name in paths: @@ -125,6 +126,20 @@ jobs: shutil.copy2(evidence / 'NOTICE.json', 'probe/NOTICE-host.json') if sys.platform != 'linux': shutil.copy2(evidence / 'NOTICE.json', 'probe/NOTICE-native.json') + - name: Remove unused macOS dylib dependencies + if: runner.os == 'macOS' + shell: python + run: | + import os + from pathlib import Path + if os.environ.get('CARGO_ENCODED_RUSTFLAGS'): + raise SystemExit('encoded Rust flags require explicit macOS linker review') + flags = os.environ.get('RUSTFLAGS', '') + if '\n' in flags or '\r' in flags: + raise SystemExit('multiline Rust flags require explicit review') + flags = (flags + ' -C link-arg=-Wl,-dead_strip_dylibs').strip() + with Path(os.environ['GITHUB_ENV']).open('a', encoding='utf-8') as output: + output.write('RUSTFLAGS=' + flags + '\n') - name: Build the native release wheel uses: PyO3/maturin-action@v1 with: diff --git a/scripts/check_native_wheel.py b/scripts/check_native_wheel.py index 91782a8..7f9fd46 100644 --- a/scripts/check_native_wheel.py +++ b/scripts/check_native_wheel.py @@ -12,6 +12,7 @@ from cargo_notice_bundle import _verify from check_musllinux_wheel import _module_bytes +from macos_native_imports import verify as verify_macos_imports def _expected(name: str) -> tuple[str, str, set[str]]: @@ -74,6 +75,10 @@ def _inspect(wheel: Path, bundle: Path) -> str: option = "--coff-imports" if kind == "COFF" else "--needed-libs" command = [str(tool), option, str(binary)] imports = subprocess.check_output(command, text=True) + if kind == "MachO": + symbols = subprocess.check_output(["nm", "-u", str(binary)], text=True) + verify_macos_imports(imports, symbols) + imports += "\nUndefined symbols:\n" + symbols return f"wheel: {wheel.name}\nmodule: {module}\n{header}\n{imports}" diff --git a/scripts/macos_native_imports.py b/scripts/macos_native_imports.py new file mode 100644 index 0000000..5b1ba76 --- /dev/null +++ b/scripts/macos_native_imports.py @@ -0,0 +1,27 @@ +"""Отклонить непроверенные macOS dylibs и iconv/charset imports.""" + +from __future__ import annotations + +import re + + +def verify(imports: str, undefined_symbols: str) -> None: + """Проверить only-libSystem и отсутствие iconv/charset imports.""" + lines = imports.splitlines() + if not lines or not lines[0].endswith(":"): + raise ValueError("missing macOS library report") + libraries = [] + for line in lines[1:]: + match = re.fullmatch( + r"\s+(\S+) \(compatibility version [0-9.]+, current version [0-9.]+\)", + line, + ) + if not match: + raise ValueError("unrecognized macOS library report") + libraries.append(match.group(1)) + if libraries != ["/usr/lib/libSystem.B.dylib"]: + raise ValueError("unreviewed macOS native dependency") + if not undefined_symbols.strip(): + raise ValueError("missing macOS undefined-symbol report") + if re.search(r"iconv|charset", undefined_symbols, re.IGNORECASE): + raise ValueError("unexpected macOS iconv/charset import") diff --git a/tests/ci/test_macos_native_imports.py b/tests/ci/test_macos_native_imports.py new file mode 100644 index 0000000..f76b1df --- /dev/null +++ b/tests/ci/test_macos_native_imports.py @@ -0,0 +1,58 @@ +"""Проверить fail-closed macOS dependencies без native исполнения.""" + +from __future__ import annotations + +import importlib.util +from pathlib import Path + +import pytest + +SCRIPT = Path(__file__).resolve().parents[2] / "scripts" / "macos_native_imports.py" +SPEC = importlib.util.spec_from_file_location("macos_native_imports", SCRIPT) +MODULE = importlib.util.module_from_spec(SPEC) +SPEC.loader.exec_module(MODULE) +SYSTEM = "\t/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1351.0.0)\n" +SYMBOLS = "_PyLong_FromLong\n_malloc\n" + + +def test_only_system_library_and_python_symbols_pass() -> None: + MODULE.verify("module.so:\n" + SYSTEM, SYMBOLS) + + +@pytest.mark.parametrize( + "library", + ["/usr/lib/libiconv.2.dylib", "@rpath/foreign.dylib", "/usr/local/lib/libcharset.dylib"], +) +def test_rejects_extra_libraries(library: str) -> None: + imports = ( + "module.so:\n" + + SYSTEM + + f"\t{library} (compatibility version 1.0.0, current version 1.0.0)\n" + ) + with pytest.raises(ValueError, match="unreviewed macOS native dependency"): + MODULE.verify(imports, SYMBOLS) + + +@pytest.mark.parametrize( + "symbol", ["_iconv", "_iconv_open", "_libiconv", "_locale_charset", "_ICONV_close"] +) +def test_rejects_iconv_charset_symbols(symbol: str) -> None: + with pytest.raises(ValueError, match="iconv/charset import"): + MODULE.verify("module.so:\n" + SYSTEM, SYMBOLS + symbol + "\n") + + +@pytest.mark.parametrize("imports", ["", "not a report\n", "module.so:\nmalformed\n"]) +def test_rejects_malformed_reports(imports: str) -> None: + with pytest.raises(ValueError, match="macOS library report"): + MODULE.verify(imports, SYMBOLS) + + +@pytest.mark.parametrize("imports", ["module.so:\n", "module.so:\n" + SYSTEM + SYSTEM]) +def test_rejects_missing_or_duplicate_system_library(imports: str) -> None: + with pytest.raises(ValueError, match="unreviewed macOS native dependency"): + MODULE.verify(imports, SYMBOLS) + + +def test_rejects_missing_symbol_evidence() -> None: + with pytest.raises(ValueError, match="missing macOS undefined-symbol report"): + MODULE.verify("module.so:\n" + SYSTEM, "") From 92e473138b178028dc76409a571010202aa1c31a Mon Sep 17 00:00:00 2001 From: Arch-AI-tech Date: Sat, 31 Oct 2026 18:18:06 +0100 Subject: [PATCH 43/54] fix: verify exact macOS and Python tooling identities Distinguish the dylib install name reported by otool -D from actual dependencies shown by otool -L. Exclude only the exact first identity entry; retain only-libSystem and no-iconv/charset guards, and reject missing, mismatched or duplicate self identities. Review nodeenv 1.11.1 from its digest-verified PyPI wheel. Its full BSD license matches the already reviewed 1.11.0 bytes. Add only that exact version/hash identity and retain unknown-version, changed-text and incompatible-SPDX failures. No dependency versions are changed. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/licenses.yml | 6 +++- scripts/check_native_wheel.py | 5 ++-- scripts/macos_native_imports.py | 12 ++++++-- tests/ci/test_macos_native_imports.py | 41 +++++++++++++++++++-------- 4 files changed, 46 insertions(+), 18 deletions(-) diff --git a/.github/workflows/licenses.yml b/.github/workflows/licenses.yml index 85a642e..0116160 100644 --- a/.github/workflows/licenses.yml +++ b/.github/workflows/licenses.yml @@ -178,6 +178,7 @@ jobs: } reviewed = { ('nodeenv', '1.11.0'): ('bsd', '606faf42d48b54d539dae99db6fecd48544535587e27275b319a3f36113cb6fa'), + ('nodeenv', '1.11.1'): ('bsd', '606faf42d48b54d539dae99db6fecd48544535587e27275b319a3f36113cb6fa'), ('colorama', '0.4.6'): ('', 'cac35c02686e5d04a5a7140bfb3b36e73aed496656e891102e428886d7930318'), ('exceptiongroup', '1.3.1'): ('', '6e5070d765031e0ad403a1cbf90ae6d19a0228f802e320b7acff460aa72ed47c'), ('pathspec', '1.1.1'): ('', 'fab3dd6bdab226f1c08630b1dd917e11fcb4ec5e1e020e2c16f83a0a13863e85'), @@ -195,7 +196,10 @@ jobs: expected = reviewed[('nodeenv', '1.11.0')][1] assert check('nodeenv', '1.11.0', None, 'BSD', [expected]) - assert not check('nodeenv', '1.11.1', None, 'BSD', [expected]) + assert check('nodeenv', '1.11.1', None, 'BSD', [expected]) + assert not check('nodeenv', '1.11.2', None, 'BSD', [expected]) + assert not check('nodeenv', '1.11.1', None, 'BSD', ['wrong']) + assert not check('nodeenv', '1.11.1', 'MIT AND GPL-3.0-only', 'BSD', [expected]) assert not check('nodeenv', '1.11.0', None, 'BSD', ['wrong']) assert not check('nodeenv', '1.11.0', 'MIT AND GPL-3.0-only', 'BSD', [expected]) assert not check('unknown', '1', None, None, []) diff --git a/scripts/check_native_wheel.py b/scripts/check_native_wheel.py index 7f9fd46..ee057fe 100644 --- a/scripts/check_native_wheel.py +++ b/scripts/check_native_wheel.py @@ -77,8 +77,9 @@ def _inspect(wheel: Path, bundle: Path) -> str: imports = subprocess.check_output(command, text=True) if kind == "MachO": symbols = subprocess.check_output(["nm", "-u", str(binary)], text=True) - verify_macos_imports(imports, symbols) - imports += "\nUndefined symbols:\n" + symbols + install_name = subprocess.check_output(["otool", "-D", str(binary)], text=True) + verify_macos_imports(imports, symbols, install_name) + imports += "\nInstall name:\n" + install_name + "\nUndefined symbols:\n" + symbols return f"wheel: {wheel.name}\nmodule: {module}\n{header}\n{imports}" diff --git a/scripts/macos_native_imports.py b/scripts/macos_native_imports.py index 5b1ba76..033b290 100644 --- a/scripts/macos_native_imports.py +++ b/scripts/macos_native_imports.py @@ -5,7 +5,7 @@ import re -def verify(imports: str, undefined_symbols: str) -> None: +def verify(imports: str, undefined_symbols: str, install_name_report: str) -> None: """Проверить only-libSystem и отсутствие iconv/charset imports.""" lines = imports.splitlines() if not lines or not lines[0].endswith(":"): @@ -19,8 +19,14 @@ def verify(imports: str, undefined_symbols: str) -> None: if not match: raise ValueError("unrecognized macOS library report") libraries.append(match.group(1)) - if libraries != ["/usr/lib/libSystem.B.dylib"]: - raise ValueError("unreviewed macOS native dependency") + identity = install_name_report.splitlines() + if len(identity) != 2 or not identity[0].endswith(":") or not identity[1].strip(): + raise ValueError("missing macOS install-name report") + if libraries[:1] != [identity[1].strip()]: + raise ValueError("unreviewed macOS native dependency: missing or misplaced install name") + dependencies = libraries[1:] + if dependencies != ["/usr/lib/libSystem.B.dylib"]: + raise ValueError(f"unreviewed macOS native dependency: {dependencies!r}") if not undefined_symbols.strip(): raise ValueError("missing macOS undefined-symbol report") if re.search(r"iconv|charset", undefined_symbols, re.IGNORECASE): diff --git a/tests/ci/test_macos_native_imports.py b/tests/ci/test_macos_native_imports.py index f76b1df..db3c10c 100644 --- a/tests/ci/test_macos_native_imports.py +++ b/tests/ci/test_macos_native_imports.py @@ -12,11 +12,14 @@ MODULE = importlib.util.module_from_spec(SPEC) SPEC.loader.exec_module(MODULE) SYSTEM = "\t/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1351.0.0)\n" +SELF = "\t@rpath/module.so (compatibility version 0.0.0, current version 0.0.0)\n" +REPORT = "module.so:\n" + SELF + SYSTEM +IDENTITY = "module.so:\n@rpath/module.so\n" SYMBOLS = "_PyLong_FromLong\n_malloc\n" def test_only_system_library_and_python_symbols_pass() -> None: - MODULE.verify("module.so:\n" + SYSTEM, SYMBOLS) + MODULE.verify(REPORT, SYMBOLS, IDENTITY) @pytest.mark.parametrize( @@ -24,13 +27,9 @@ def test_only_system_library_and_python_symbols_pass() -> None: ["/usr/lib/libiconv.2.dylib", "@rpath/foreign.dylib", "/usr/local/lib/libcharset.dylib"], ) def test_rejects_extra_libraries(library: str) -> None: - imports = ( - "module.so:\n" - + SYSTEM - + f"\t{library} (compatibility version 1.0.0, current version 1.0.0)\n" - ) + imports = REPORT + f"\t{library} (compatibility version 1.0.0, current version 1.0.0)\n" with pytest.raises(ValueError, match="unreviewed macOS native dependency"): - MODULE.verify(imports, SYMBOLS) + MODULE.verify(imports, SYMBOLS, IDENTITY) @pytest.mark.parametrize( @@ -38,21 +37,39 @@ def test_rejects_extra_libraries(library: str) -> None: ) def test_rejects_iconv_charset_symbols(symbol: str) -> None: with pytest.raises(ValueError, match="iconv/charset import"): - MODULE.verify("module.so:\n" + SYSTEM, SYMBOLS + symbol + "\n") + MODULE.verify(REPORT, SYMBOLS + symbol + "\n", IDENTITY) @pytest.mark.parametrize("imports", ["", "not a report\n", "module.so:\nmalformed\n"]) def test_rejects_malformed_reports(imports: str) -> None: with pytest.raises(ValueError, match="macOS library report"): - MODULE.verify(imports, SYMBOLS) + MODULE.verify(imports, SYMBOLS, IDENTITY) -@pytest.mark.parametrize("imports", ["module.so:\n", "module.so:\n" + SYSTEM + SYSTEM]) +@pytest.mark.parametrize( + "imports", ["module.so:\n" + SELF, "module.so:\n" + SELF + SYSTEM + SYSTEM] +) def test_rejects_missing_or_duplicate_system_library(imports: str) -> None: with pytest.raises(ValueError, match="unreviewed macOS native dependency"): - MODULE.verify(imports, SYMBOLS) + MODULE.verify(imports, SYMBOLS, IDENTITY) def test_rejects_missing_symbol_evidence() -> None: with pytest.raises(ValueError, match="missing macOS undefined-symbol report"): - MODULE.verify("module.so:\n" + SYSTEM, "") + MODULE.verify(REPORT, "", IDENTITY) + + +@pytest.mark.parametrize("identity", ["", "module.so:\n", "module.so:\nfirst\nsecond\n"]) +def test_rejects_missing_or_ambiguous_install_name(identity: str) -> None: + with pytest.raises(ValueError, match="missing macOS install-name report"): + MODULE.verify(REPORT, SYMBOLS, identity) + + +def test_rejects_mismatched_install_name() -> None: + with pytest.raises(ValueError, match="missing or misplaced install name"): + MODULE.verify(REPORT, SYMBOLS, "module.so:\n@rpath/foreign.so\n") + + +def test_duplicate_self_reference_is_not_ignored() -> None: + with pytest.raises(ValueError, match="unreviewed macOS native dependency"): + MODULE.verify("module.so:\n" + SELF + SELF + SYSTEM, SYMBOLS, IDENTITY) From 0807ed4ce9c4d7798c9e60ccf152f405dae7bcc7 Mon Sep 17 00:00:00 2001 From: Arch-AI-tech Date: Sun, 1 Nov 2026 07:39:03 +0100 Subject: [PATCH 44/54] fix(licenses): retain Python compiler runtime grants Add the verified pinned Rust compiler-builtins, LLVM compiler-rt and libm original materials to native wheel notice preparation. Preserve Cargo/std notices, exact compiler rebinding and the existing musl LLVM augmentation without runtime dependencies. Stage all materials in both reusable release producers. Pin the license-evidence compiler to the already reviewed Rust 1.99.1 source and reject unknown compiler revisions or changed grants. Add regression coverage for full original delivery and container compiler rebinding that preserves the complete source collection. The collection is conservative, not a linked-code inventory. No binding source, first-party license, API or version changes. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/licenses.yml | 6 +- .github/workflows/native-release-build.yml | 2 + .../workflows/release-license-evidence.yml | 2 + .../compiler-builtins-Cargo.toml.txt | 62 ++ .../compiler-builtins-LICENSE.txt | 275 ++++++++ ...er-builtins-aarch64-outline-atomics.rs.txt | 467 +++++++++++++ .../compiler-builtins-aarch64.rs.txt | 17 + .../compiler-builtins-build.rs.txt | 649 ++++++++++++++++++ .../compiler-builtins-float-conv.rs.txt | 508 ++++++++++++++ .../compiler-builtins-math-mod.rs.txt | 211 ++++++ .../compiler-rt-CREDITS.TXT | 36 + .../compiler-rt-LICENSE.TXT | 311 +++++++++ .../compiler-rt-aarch64-cpu-model.c.txt | 94 +++ .../compiler-rt-aarch64-lse.S.txt | 270 ++++++++ .../rust-compiler-originals/libm-LICENSE.txt | 258 +++++++ .../libm-generic-trunc.rs.txt | 36 + .../rust-compiler-originals/libm-trunc.rs.txt | 147 ++++ ci/rust-compiler-notices.json | 163 +++++ scripts/prepare_native_notices.py | 9 +- scripts/rust_compiler_notices.py | 65 ++ tests/ci/test_rust_compiler_notices.py | 121 ++++ 21 files changed, 3705 insertions(+), 4 deletions(-) create mode 100644 ci/licenses/rust-compiler-originals/compiler-builtins-Cargo.toml.txt create mode 100644 ci/licenses/rust-compiler-originals/compiler-builtins-LICENSE.txt create mode 100644 ci/licenses/rust-compiler-originals/compiler-builtins-aarch64-outline-atomics.rs.txt create mode 100644 ci/licenses/rust-compiler-originals/compiler-builtins-aarch64.rs.txt create mode 100644 ci/licenses/rust-compiler-originals/compiler-builtins-build.rs.txt create mode 100644 ci/licenses/rust-compiler-originals/compiler-builtins-float-conv.rs.txt create mode 100644 ci/licenses/rust-compiler-originals/compiler-builtins-math-mod.rs.txt create mode 100644 ci/licenses/rust-compiler-originals/compiler-rt-CREDITS.TXT create mode 100644 ci/licenses/rust-compiler-originals/compiler-rt-LICENSE.TXT create mode 100644 ci/licenses/rust-compiler-originals/compiler-rt-aarch64-cpu-model.c.txt create mode 100644 ci/licenses/rust-compiler-originals/compiler-rt-aarch64-lse.S.txt create mode 100644 ci/licenses/rust-compiler-originals/libm-LICENSE.txt create mode 100644 ci/licenses/rust-compiler-originals/libm-generic-trunc.rs.txt create mode 100644 ci/licenses/rust-compiler-originals/libm-trunc.rs.txt create mode 100644 ci/rust-compiler-notices.json create mode 100644 scripts/rust_compiler_notices.py create mode 100644 tests/ci/test_rust_compiler_notices.py diff --git a/.github/workflows/licenses.yml b/.github/workflows/licenses.yml index 0116160..aab0fe8 100644 --- a/.github/workflows/licenses.yml +++ b/.github/workflows/licenses.yml @@ -33,7 +33,9 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@stable + - uses: dtolnay/rust-toolchain@1.99.1 + with: + components: rust-docs - uses: taiki-e/install-action@v2 with: tool: cargo-about@0.9.3 @@ -51,7 +53,7 @@ jobs: run: | set -euo pipefail python -m pip install 'maturin>=1.7,<2.0' pytest - python -m pytest --confcutdir=tests/ci -q tests/ci/test_cargo_notice_bundle.py tests/ci/test_prepare_native_notices.py + python -m pytest --confcutdir=tests/ci -q tests/ci/test_cargo_notice_bundle.py tests/ci/test_prepare_native_notices.py tests/ci/test_rust_compiler_notices.py evidence="$RUNNER_TEMP/cargo-license-evidence" maturin build --release --locked --out "$evidence/wheels" cmp Cargo.lock "$evidence/Cargo.lock" diff --git a/.github/workflows/native-release-build.yml b/.github/workflows/native-release-build.yml index 6ae1e3b..6e5ce52 100644 --- a/.github/workflows/native-release-build.yml +++ b/.github/workflows/native-release-build.yml @@ -55,6 +55,8 @@ jobs: stage = Path(os.environ['RUNNER_TEMP']) / 'native-packaging-tools' stage.mkdir() paths = [ + 'scripts/rust_compiler_notices.py', 'ci/rust-compiler-notices.json', + *[str(path) for path in Path('ci/licenses/rust-compiler-originals').glob('*')], 'scripts/prepare_native_notices.py', 'scripts/cargo_notice_bundle.py', 'scripts/collect_cargo_notices.py', 'scripts/bind_native_compiler.py', 'scripts/check_native_wheel.py', 'scripts/check_musllinux_wheel.py', diff --git a/.github/workflows/release-license-evidence.yml b/.github/workflows/release-license-evidence.yml index 5962dda..24176da 100644 --- a/.github/workflows/release-license-evidence.yml +++ b/.github/workflows/release-license-evidence.yml @@ -40,6 +40,8 @@ jobs: stage = Path(os.environ['RUNNER_TEMP']) / 'release-packaging-tools' stage.mkdir() paths = [ + 'scripts/rust_compiler_notices.py', 'ci/rust-compiler-notices.json', + *[str(path) for path in Path('ci/licenses/rust-compiler-originals').glob('*')], 'scripts/prepare_native_notices.py', 'scripts/cargo_notice_bundle.py', 'scripts/collect_cargo_notices.py', 'scripts/add_llvm_unwind_notice.py', 'scripts/check_unwinder_maps.py', 'scripts/check_musllinux_wheel.py', diff --git a/ci/licenses/rust-compiler-originals/compiler-builtins-Cargo.toml.txt b/ci/licenses/rust-compiler-originals/compiler-builtins-Cargo.toml.txt new file mode 100644 index 0000000..b2f1443 --- /dev/null +++ b/ci/licenses/rust-compiler-originals/compiler-builtins-Cargo.toml.txt @@ -0,0 +1,62 @@ +# NOTE: Must be kept in sync with `../builtins-shim/Cargo.toml`. +# +# This manifest is actually used in-tree by rust-lang/rust, +# `../builtins-shim/Cargo.toml` is used by out-of-tree testing. See the other +# manifest for further details. + +[package] +name = "compiler_builtins" +version = "0.1.160" +authors = [ + "Alex Crichton ", + "Amanieu d'Antras ", + "Jorge Aparicio ", + "Trevor Gross ", +] +description = "Compiler intrinsics used by the Rust compiler." +repository = "https://github.com/rust-lang/compiler-builtins" +license = "MIT AND Apache-2.0 WITH LLVM-exception AND (MIT OR Apache-2.0)" +edition = "2024" +publish = false +links = "compiler-rt" + +[lib] +bench = false +doctest = false +test = false +# make sure this crate isn't included in public standard library docs +doc = false + +[dependencies] +core = { path = "../../core", optional = true } + +[build-dependencies] +cc = { version = "1.2", optional = true } + +[features] +default = ["arch"] + +# Enable architecture-specific features such as SIMD or assembly routines. If +# disabled, the generic version can be tested on any platform. +arch = [] + +# Enable compilation of C code in compiler-rt, filling in some more optimized +# implementations and also filling in unimplemented intrinsics +c = ["dep:cc"] + +# Flag this library as the unstable compiler-builtins lib. This must be enabled +# when using as `std`'s dependency.' +compiler-builtins = ["dep:core", "unmangled-names"] + +# Enable `no_mangle` symbols for memory-related intrinsics like memcpy. The +# mangled versions are always available. +mem = [] + +# Enable `no_mangle` symbols so this crate gets used as the runtime intrinsic +# implementation. Leave this disabled for testing to avoid conflicting with +# the system intrinsics. +unmangled-names = [] + +# This makes certain traits and function specializations public that +# are not normally public but are required by the `builtins-test` +unstable-public-internals = [] diff --git a/ci/licenses/rust-compiler-originals/compiler-builtins-LICENSE.txt b/ci/licenses/rust-compiler-originals/compiler-builtins-LICENSE.txt new file mode 100644 index 0000000..00ae614 --- /dev/null +++ b/ci/licenses/rust-compiler-originals/compiler-builtins-LICENSE.txt @@ -0,0 +1,275 @@ +The compiler-builtins crate is available for use under both the MIT license +and the Apache-2.0 license with the LLVM exception (MIT AND Apache-2.0 WITH +LLVM-exception). + +The libm crate is available for use under the MIT license. + +As a contributor, you agree that your code may be used under any of the +following: the MIT license, the Apache-2.0 license, or the Apache-2.0 license +with the LLVM exception. In other words, original (non-derivative) work is +licensed under MIT OR Apache-2.0 OR Apache-2.0 WITH LLVM-exception. This is +the default license for all other source in this repository. + +Text of the relevant licenses is provided below: + +------------------------------------------------------------------------------ +MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +---- LLVM Exceptions to the Apache 2.0 License ---- + +As an exception, if, as a result of your compiling your source code, portions +of this Software are embedded into an Object form of such source code, you +may redistribute such embedded portions in such Object form without complying +with the conditions of Sections 4(a), 4(b) and 4(d) of the License. + +In addition, if you combine or link compiled forms of this Software with +software that is licensed under the GPLv2 ("Combined Software") and if a +court of competent jurisdiction determines that the patent provision (Section +3), the indemnity provision (Section 9) or other Section of the License +conflicts with the conditions of the GPLv2, you may retroactively and +prospectively choose to deem waived or otherwise exclude such Section(s) of +the License, but only in their entirety and only with respect to the Combined +Software. +------------------------------------------------------------------------------ + +Portions of this software are derived from third-party works licensed under +terms compatible with the above Apache-2.0 WITH LLVM-exception AND MIT +license: + +* compiler-builtins is derived from LLVM's compiler-rt (https://llvm.org/). + Work derived from compiler-rt prior to 2019-01-19 is usable under the MIT + license, with the following copyright: + + Copyright (c) 2009-2016 by the contributors listed in CREDITS.TXT + + The relevant CREDITS.TXT is located at + https://github.com/llvm/llvm-project/blob/main/compiler-rt/CREDITS.TXT. + +* Work derived from compiler-rt after 2019-01-19 is usable under the + Apache-2.0 license with the LLVM exception. + +* The bundled `math` module is from the libm crate, usable under the MIT + license. For further details and copyrights, see see libm/LICENSE.txt at + https://github.com/rust-lang/compiler-builtins. + +Additionally, some source files may contain comments with specific copyrights +or licenses. diff --git a/ci/licenses/rust-compiler-originals/compiler-builtins-aarch64-outline-atomics.rs.txt b/ci/licenses/rust-compiler-originals/compiler-builtins-aarch64-outline-atomics.rs.txt new file mode 100644 index 0000000..367768e --- /dev/null +++ b/ci/licenses/rust-compiler-originals/compiler-builtins-aarch64-outline-atomics.rs.txt @@ -0,0 +1,467 @@ +//! Aarch64 targets have two possible implementations for atomics: +//! 1. Load-Locked, Store-Conditional (LL/SC), older and slower. +//! 2. Large System Extensions (LSE), newer and faster. +//! To avoid breaking backwards compat, C toolchains introduced a concept of "outlined atomics", +//! where atomic operations call into the compiler runtime to dispatch between two depending on +//! which is supported on the current CPU. +//! See for more discussion. +//! +//! Ported from `aarch64/lse.S` in LLVM's compiler-rt. +//! +//! Generate functions for each of the following symbols: +//! __aarch64_casM_ORDER +//! __aarch64_swpN_ORDER +//! __aarch64_ldaddN_ORDER +//! __aarch64_ldclrN_ORDER +//! __aarch64_ldeorN_ORDER +//! __aarch64_ldsetN_ORDER +//! for N = {1, 2, 4, 8}, M = {1, 2, 4, 8, 16}, ORDER = { relax, acq, rel, acq_rel } +//! +//! The original `lse.S` has some truly horrifying code that expects to be compiled multiple times with different constants. +//! We do something similar, but with macro arguments. +#![cfg_attr(feature = "c", allow(unused_macros))] // avoid putting the macros into a submodule + +use core::sync::atomic::{AtomicU8, Ordering}; + +/// non-zero if the host supports LSE atomics. +static HAVE_LSE_ATOMICS: AtomicU8 = AtomicU8::new(0); + +intrinsics! { + /// Call to enable LSE in outline atomic operations. The caller must verify + /// LSE operations are supported. + pub extern "C" fn __rust_enable_lse() { + HAVE_LSE_ATOMICS.store(1, Ordering::Relaxed); + } +} + +/// Function to enable/disable LSE. To be used only for testing purposes. +#[cfg(feature = "unstable-public-internals")] +pub unsafe fn set_have_lse_atomics(has_lse: bool) { + let lse_flag = if has_lse { 1 } else { 0 }; + HAVE_LSE_ATOMICS.store(lse_flag, Ordering::Relaxed); +} + +/// Function to obtain whether LSE is enabled or not. To be used only for testing purposes. +#[cfg(feature = "unstable-public-internals")] +pub fn get_have_lse_atomics() -> bool { + HAVE_LSE_ATOMICS.load(Ordering::Relaxed) != 0 +} + +/// Translate a byte size to a Rust type. +#[rustfmt::skip] +macro_rules! int_ty { + (1) => { u8 }; + (2) => { u16 }; + (4) => { u32 }; + (8) => { u64 }; + (16) => { u128 }; +} + +/// Given a byte size and a register number, return a register of the appropriate size. +/// +/// See . +#[rustfmt::skip] +macro_rules! reg { + (1, $num:literal) => { concat!("w", $num) }; + (2, $num:literal) => { concat!("w", $num) }; + (4, $num:literal) => { concat!("w", $num) }; + (8, $num:literal) => { concat!("x", $num) }; + (16, $num:literal) => { concat!("x", $num) }; +} + +/// Given an atomic ordering, translate it to the acquire suffix for the lxdr aarch64 ASM instruction. +#[rustfmt::skip] +macro_rules! acquire { + (Relaxed) => { "" }; + (Acquire) => { "a" }; + (Release) => { "" }; + (AcqRel) => { "a" }; +} + +/// Given an atomic ordering, translate it to the release suffix for the stxr aarch64 ASM instruction. +#[rustfmt::skip] +macro_rules! release { + (Relaxed) => { "" }; + (Acquire) => { "" }; + (Release) => { "l" }; + (AcqRel) => { "l" }; +} + +/// Given a size in bytes, translate it to the byte suffix for an aarch64 ASM instruction. +#[rustfmt::skip] +macro_rules! size { + (1) => { "b" }; + (2) => { "h" }; + (4) => { "" }; + (8) => { "" }; + (16) => { "" }; +} + +/// Given a byte size, translate it to an Unsigned eXTend instruction +/// with the correct semantics. +/// +/// See +#[rustfmt::skip] +macro_rules! uxt { + (1) => { "uxtb" }; + (2) => { "uxth" }; + ($_:tt) => { "mov" }; +} + +/// Given an atomic ordering and byte size, translate it to a LoaD eXclusive Register instruction +/// with the correct semantics. +/// +/// See . +macro_rules! ldxr { + ($ordering:ident, $bytes:tt) => { + concat!("ld", acquire!($ordering), "xr", size!($bytes)) + }; +} + +/// Given an atomic ordering and byte size, translate it to a STore eXclusive Register instruction +/// with the correct semantics. +/// +/// See . +macro_rules! stxr { + ($ordering:ident, $bytes:tt) => { + concat!("st", release!($ordering), "xr", size!($bytes)) + }; +} + +/// Given an atomic ordering and byte size, translate it to a LoaD eXclusive Pair of registers instruction +/// with the correct semantics. +/// +/// See +macro_rules! ldxp { + ($ordering:ident) => { + concat!("ld", acquire!($ordering), "xp") + }; +} + +/// Given an atomic ordering and byte size, translate it to a STore eXclusive Pair of registers instruction +/// with the correct semantics. +/// +/// See . +macro_rules! stxp { + ($ordering:ident) => { + concat!("st", release!($ordering), "xp") + }; +} + +// The AArch64 assembly syntax for relocation specifiers +// when accessing symbols changes depending on the target executable format. +// In ELF (used in Linux), we have a prefix notation surrounded by colons (:specifier:sym), +// while in Mach-O object files (used in MacOS), a postfix notation is used (sym@specifier). + +/// AArch64 ELF position-independent addressing: +/// +/// adrp xN, symbol +/// add xN, xN, :lo12:symbol +/// +/// The :lo12: modifier selects the low 12 bits of the symbol address +/// and emits an ELF relocation such as R_AARCH64_ADD_ABS_LO12_NC. +/// +/// Defined by the AArch64 ELF psABI. +/// See: . +#[cfg(not(target_vendor = "apple"))] +macro_rules! sym { + ($sym:literal) => { + $sym + }; +} + +#[cfg(not(target_vendor = "apple"))] +macro_rules! sym_off { + ($sym:literal) => { + concat!(":lo12:", $sym) + }; +} + +/// Mach-O ARM64 relocation types: +/// ARM64_RELOC_PAGE21 +/// ARM64_RELOC_PAGEOFF12 +/// +/// These relocations implement the @PAGE / @PAGEOFF split used by +/// adrp + add sequences on Apple platforms. +/// +/// adrp xN, symbol@PAGE -> ARM64_RELOC_PAGE21 +/// add xN, xN, symbol@PAGEOFF -> ARM64_RELOC_PAGEOFF12 +/// +/// Relocation types defined by Apple in XNU: . +/// See: . +#[cfg(target_vendor = "apple")] +macro_rules! sym { + ($sym:literal) => { + concat!($sym, "@PAGE") + }; +} + +#[cfg(target_vendor = "apple")] +macro_rules! sym_off { + ($sym:literal) => { + concat!($sym, "@PAGEOFF") + }; +} + +// If supported, perform the requested LSE op and return, or fallthrough. +macro_rules! try_lse_op { + ($op: literal, $ordering:ident, $bytes:tt, $($reg:literal,)* [ $mem:ident ] ) => { + concat!( + ".arch_extension lse\n", + concat!("adrp x16, ", sym!("{have_lse}"), "\n"), + concat!("ldrb w16, [x16, ", sym_off!("{have_lse}"), "]\n"), + "cbz w16, 8f\n", + // LSE_OP s(reg),* [$mem] + concat!(lse!($op, $ordering, $bytes), $( " ", reg!($bytes, $reg), ", " ,)* "[", stringify!($mem), "]\n",), + "ret + 8:" + ) + }; +} + +// Translate memory ordering to the LSE suffix +#[rustfmt::skip] +macro_rules! lse_mem_sfx { + (Relaxed) => { "" }; + (Acquire) => { "a" }; + (Release) => { "l" }; + (AcqRel) => { "al" }; +} + +// Generate the aarch64 LSE operation for memory ordering and width +macro_rules! lse { + ($op:literal, $order:ident, 16) => { + concat!($op, "p", lse_mem_sfx!($order)) + }; + ($op:literal, $order:ident, $bytes:tt) => { + concat!($op, lse_mem_sfx!($order), size!($bytes)) + }; +} + +/// See . +macro_rules! compare_and_swap { + ($ordering:ident, $bytes:tt, $name:ident) => { + intrinsics! { + #[maybe_use_optimized_c_shim] + #[unsafe(naked)] + pub unsafe extern "C" fn $name ( + expected: int_ty!($bytes), desired: int_ty!($bytes), ptr: *mut int_ty!($bytes) + ) -> int_ty!($bytes) { + // We can't use `AtomicI8::compare_and_swap`; we *are* compare_and_swap. + core::arch::naked_asm! { + // CAS s(0), s(1), [x2]; if LSE supported. + try_lse_op!("cas", $ordering, $bytes, 0, 1, [x2]), + // UXT s(tmp0), s(0) + concat!(uxt!($bytes), " ", reg!($bytes, 16), ", ", reg!($bytes, 0)), + "0:", + // LDXR s(0), [x2] + concat!(ldxr!($ordering, $bytes), " ", reg!($bytes, 0), ", [x2]"), + // cmp s(0), s(tmp0) + concat!("cmp ", reg!($bytes, 0), ", ", reg!($bytes, 16)), + "bne 1f", + // STXR w(tmp1), s(1), [x2] + concat!(stxr!($ordering, $bytes), " w17, ", reg!($bytes, 1), ", [x2]"), + "cbnz w17, 0b", + "1:", + "ret", + have_lse = sym crate::aarch64_outline_atomics::HAVE_LSE_ATOMICS, + } + } + } + }; +} + +// u128 uses a completely different impl, so it has its own macro. +macro_rules! compare_and_swap_u128 { + ($ordering:ident, $name:ident) => { + intrinsics! { + #[maybe_use_optimized_c_shim] + #[unsafe(naked)] + pub unsafe extern "C" fn $name ( + expected: u128, desired: u128, ptr: *mut u128 + ) -> u128 { + core::arch::naked_asm! { + // CASP x0, x1, x2, x3, [x4]; if LSE supported. + try_lse_op!("cas", $ordering, 16, 0, 1, 2, 3, [x4]), + "mov x16, x0", + "mov x17, x1", + "0:", + // LDXP x0, x1, [x4] + concat!(ldxp!($ordering), " x0, x1, [x4]"), + "cmp x0, x16", + "ccmp x1, x17, #0, eq", + "bne 1f", + // STXP w(tmp2), x2, x3, [x4] + concat!(stxp!($ordering), " w15, x2, x3, [x4]"), + "cbnz w15, 0b", + "1:", + "ret", + have_lse = sym crate::aarch64_outline_atomics::HAVE_LSE_ATOMICS, + } + } + } + }; +} + +/// See . +macro_rules! swap { + ($ordering:ident, $bytes:tt, $name:ident) => { + intrinsics! { + #[maybe_use_optimized_c_shim] + #[unsafe(naked)] + pub unsafe extern "C" fn $name ( + left: int_ty!($bytes), right_ptr: *mut int_ty!($bytes) + ) -> int_ty!($bytes) { + core::arch::naked_asm! { + // SWP s(0), s(0), [x1]; if LSE supported. + try_lse_op!("swp", $ordering, $bytes, 0, 0, [x1]), + // mov s(tmp0), s(0) + concat!("mov ", reg!($bytes, 16), ", ", reg!($bytes, 0)), + "0:", + // LDXR s(0), [x1] + concat!(ldxr!($ordering, $bytes), " ", reg!($bytes, 0), ", [x1]"), + // STXR w(tmp1), s(tmp0), [x1] + concat!(stxr!($ordering, $bytes), " w17, ", reg!($bytes, 16), ", [x1]"), + "cbnz w17, 0b", + "ret", + have_lse = sym crate::aarch64_outline_atomics::HAVE_LSE_ATOMICS, + } + } + } + }; +} + +/// See (e.g.) . +macro_rules! fetch_op { + ($ordering:ident, $bytes:tt, $name:ident, $op:literal, $lse_op:literal) => { + intrinsics! { + #[maybe_use_optimized_c_shim] + #[unsafe(naked)] + pub unsafe extern "C" fn $name ( + val: int_ty!($bytes), ptr: *mut int_ty!($bytes) + ) -> int_ty!($bytes) { + core::arch::naked_asm! { + // LSEOP s(0), s(0), [x1]; if LSE supported. + try_lse_op!($lse_op, $ordering, $bytes, 0, 0, [x1]), + // mov s(tmp0), s(0) + concat!("mov ", reg!($bytes, 16), ", ", reg!($bytes, 0)), + "0:", + // LDXR s(0), [x1] + concat!(ldxr!($ordering, $bytes), " ", reg!($bytes, 0), ", [x1]"), + // OP s(tmp1), s(0), s(tmp0) + concat!($op, " ", reg!($bytes, 17), ", ", reg!($bytes, 0), ", ", reg!($bytes, 16)), + // STXR w(tmp2), s(tmp1), [x1] + concat!(stxr!($ordering, $bytes), " w15, ", reg!($bytes, 17), ", [x1]"), + "cbnz w15, 0b", + "ret", + have_lse = sym crate::aarch64_outline_atomics::HAVE_LSE_ATOMICS, + } + } + } + } +} + +// We need a single macro to pass to `foreach_ldadd`. +macro_rules! add { + ($ordering:ident, $bytes:tt, $name:ident) => { + fetch_op! { $ordering, $bytes, $name, "add", "ldadd" } + }; +} + +macro_rules! and { + ($ordering:ident, $bytes:tt, $name:ident) => { + fetch_op! { $ordering, $bytes, $name, "bic", "ldclr" } + }; +} + +macro_rules! xor { + ($ordering:ident, $bytes:tt, $name:ident) => { + fetch_op! { $ordering, $bytes, $name, "eor", "ldeor" } + }; +} + +macro_rules! or { + ($ordering:ident, $bytes:tt, $name:ident) => { + fetch_op! { $ordering, $bytes, $name, "orr", "ldset" } + }; +} + +#[macro_export] +macro_rules! foreach_ordering { + ($macro:path, $bytes:tt, $name:ident) => { + $macro!( Relaxed, $bytes, ${concat($name, _relax)} ); + $macro!( Acquire, $bytes, ${concat($name, _acq)} ); + $macro!( Release, $bytes, ${concat($name, _rel)} ); + $macro!( AcqRel, $bytes, ${concat($name, _acq_rel)} ); + }; + ($macro:path, $name:ident) => { + $macro!( Relaxed, ${concat($name, _relax)} ); + $macro!( Acquire, ${concat($name, _acq)} ); + $macro!( Release, ${concat($name, _rel)} ); + $macro!( AcqRel, ${concat($name, _acq_rel)} ); + }; +} + +#[macro_export] +macro_rules! foreach_bytes { + ($macro:path, $name:ident) => { + foreach_ordering!( $macro, 1, ${concat(__aarch64_, $name, "1")} ); + foreach_ordering!( $macro, 2, ${concat(__aarch64_, $name, "2")} ); + foreach_ordering!( $macro, 4, ${concat(__aarch64_, $name, "4")} ); + foreach_ordering!( $macro, 8, ${concat(__aarch64_, $name, "8")} ); + }; +} + +/// Generate different macros for cas/swp/add/clr/eor/set so that we can test them separately. +#[macro_export] +macro_rules! foreach_cas { + ($macro:path) => { + foreach_bytes!($macro, cas); + }; +} + +/// Only CAS supports 16 bytes, and it has a different implementation that uses a different macro. +#[macro_export] +macro_rules! foreach_cas16 { + ($macro:path) => { + foreach_ordering!($macro, __aarch64_cas16); + }; +} +#[macro_export] +macro_rules! foreach_swp { + ($macro:path) => { + foreach_bytes!($macro, swp); + }; +} +#[macro_export] +macro_rules! foreach_ldadd { + ($macro:path) => { + foreach_bytes!($macro, ldadd); + }; +} +#[macro_export] +macro_rules! foreach_ldclr { + ($macro:path) => { + foreach_bytes!($macro, ldclr); + }; +} +#[macro_export] +macro_rules! foreach_ldeor { + ($macro:path) => { + foreach_bytes!($macro, ldeor); + }; +} +#[macro_export] +macro_rules! foreach_ldset { + ($macro:path) => { + foreach_bytes!($macro, ldset); + }; +} + +foreach_cas!(compare_and_swap); +foreach_cas16!(compare_and_swap_u128); +foreach_swp!(swap); +foreach_ldadd!(add); +foreach_ldclr!(and); +foreach_ldeor!(xor); +foreach_ldset!(or); diff --git a/ci/licenses/rust-compiler-originals/compiler-builtins-aarch64.rs.txt b/ci/licenses/rust-compiler-originals/compiler-builtins-aarch64.rs.txt new file mode 100644 index 0000000..2c12cb7 --- /dev/null +++ b/ci/licenses/rust-compiler-originals/compiler-builtins-aarch64.rs.txt @@ -0,0 +1,17 @@ +intrinsics! { + #[unsafe(naked)] + #[cfg(any(all(windows, target_env = "gnu"), target_os = "uefi"))] + pub unsafe extern "custom" fn __chkstk() { + core::arch::naked_asm!( + ".p2align 2", + "lsl x16, x15, #4", + "mov x17, sp", + "1:", + "sub x17, x17, 4096", + "subs x16, x16, 4096", + "ldr xzr, [x17]", + "b.gt 1b", + "ret", + ); + } +} diff --git a/ci/licenses/rust-compiler-originals/compiler-builtins-build.rs.txt b/ci/licenses/rust-compiler-originals/compiler-builtins-build.rs.txt new file mode 100644 index 0000000..e86c784 --- /dev/null +++ b/ci/licenses/rust-compiler-originals/compiler-builtins-build.rs.txt @@ -0,0 +1,649 @@ +#[path = "../libm/configure.rs"] +mod configure; + +use std::env; + +use configure::{Config, Library, set_cfg}; + +fn main() { + let cfg = Config::from_env(Library::CompilerBuiltins); + let llvm_target = &cfg.target_triple_split; + + // Work around building as part of `builtins-shim`: if only `build.rs` is used, Cargo always + // considers the build dirty because `builtins-shim/build.rs` does not exist. If only + // `../c-b/build.rs` is used, the same may happen if not built in the workspace. + // Note: this may be `None` if CARGO_MANIFEST_DIR doesn't end in a directory name (e.g. ".") + if let Some(dir) = cfg.manifest_dir.file_name() + && dir == "builtins-shim" + { + println!("cargo::rerun-if-changed=../compiler-builtins/build.rs"); + } else { + println!("cargo::rerun-if-changed=build.rs"); + } + + println!("cargo::rerun-if-changed=../libm/configure.rs"); + + configure::emit(&cfg); + configure_check_cfg(); + + let cwd = env::current_dir().unwrap(); + println!("cargo:compiler-rt={}", cwd.join("compiler-rt").display()); + + // Forcibly enable memory intrinsics on wasm & SGX as we don't have a libc to + // provide them. + if (cfg.target_triple.contains("wasm") && !cfg.target_triple.contains("wasi")) + || (cfg.target_triple.contains("sgx") && cfg.target_triple.contains("fortanix")) + || cfg.target_triple.contains("-none") + || cfg.target_triple.contains("nvptx") + || cfg.target_triple.contains("uefi") + || cfg.target_triple.contains("xous") + { + println!("cargo:rustc-cfg=feature=\"mem\""); + } + + // These targets have hardware unaligned access support. + let mem_unaligned = cfg.target_arch.contains("x86_64") + || cfg.target_arch.contains("x86") + || cfg.target_arch.contains("aarch64") + || cfg.target_arch.contains("bpf"); + set_cfg("mem_unaligned", mem_unaligned); + + // Only emit the ARM Linux atomic emulation on pre-ARMv6 architectures. This + // includes the old androideabi. It is deprecated but it is available as a + // rustc target (arm-linux-androideabi). + let kernel_user_helpers = llvm_target[0] == "armv4t" + || llvm_target[0] == "armv5te" + || cfg.target_triple == "arm-linux-androideabi"; + set_cfg("kernel_user_helpers", kernel_user_helpers); + + let mut maybe_build_c = true; + + // Emscripten's runtime includes all the builtins + if cfg.target_os == "emscripten" { + maybe_build_c = false; + } + + // OpenBSD provides compiler_rt by default, use it instead of rebuilding it from source + if cfg.target_os == "openbsd" { + println!("cargo:rustc-link-search=native=/usr/lib"); + println!("cargo:rustc-link-lib=compiler_rt"); + maybe_build_c = false; + } + + // Everything is LLVM bitcode, not compatible with mixed C/Rust + if cfg.target_arch.contains("nvptx") { + maybe_build_c = false; + } + + // Build missing intrinsics from compiler-rt C source code. If we're + // mangling names though we assume that we're also in test mode so we don't + // build anything and we rely on the upstream implementation of compiler-rt + // functions + if cfg!(feature = "unmangled-names") && cfg!(feature = "c") && maybe_build_c { + #[cfg(feature = "c")] + c::compile(&cfg); + } +} + +/// Emit directives for features we expect to support that aren't in `Cargo.toml`. +/// +/// These are mostly cfg elements emitted by this `build.rs`. +fn configure_check_cfg() { + // Functions where we can set the "optimized-c" flag + const HAS_OPTIMIZED_C: &[&str] = &[ + "__ashldi3", + "__ashlsi3", + "__ashrdi3", + "__ashrsi3", + "__bswapsi2", + "__bswapdi2", + "__bswapti2", + "__divdi3", + "__divsi3", + "__divmoddi4", + "__divmodsi4", + "__divmodsi4", + "__divmodti4", + "__lshrdi3", + "__lshrsi3", + "__moddi3", + "__modsi3", + "__muldi3", + "__udivdi3", + "__udivmoddi4", + "__udivmodsi4", + "__udivsi3", + "__umoddi3", + "__umodsi3", + ]; + + // Build a list of all aarch64 atomic operation functions + let mut aarch_atomic = Vec::new(); + for aarch_op in ["cas", "ldadd", "ldclr", "ldeor", "ldset", "swp"] { + let op_sizes = if aarch_op == "cas" { + [1, 2, 4, 8, 16].as_slice() + } else { + [1, 2, 4, 8].as_slice() + }; + + for op_size in op_sizes { + for ordering in ["relax", "acq", "rel", "acq_rel"] { + aarch_atomic.push(format!("__aarch64_{aarch_op}{op_size}_{ordering}")); + } + } + } + + for fn_name in HAS_OPTIMIZED_C + .iter() + .copied() + .chain(aarch_atomic.iter().map(|s| s.as_str())) + { + println!("cargo::rustc-check-cfg=cfg({fn_name}, values(\"optimized-c\"))",); + } + + // Rustc is unaware of sparc target features, but this does show up from + // `rustc --print target-features --target sparc64-unknown-linux-gnu`. + println!("cargo::rustc-check-cfg=cfg(target_feature, values(\"vis3\"))"); +} + +#[cfg(feature = "c")] +mod c { + use std::collections::{BTreeMap, HashSet}; + use std::env; + use std::fs::{self, File}; + use std::io::Write; + use std::path::{Path, PathBuf}; + + use super::Config; + + struct Sources { + // SYMBOL -> PATH TO SOURCE + map: BTreeMap<&'static str, &'static str>, + } + + impl Sources { + fn new() -> Sources { + Sources { + map: BTreeMap::new(), + } + } + + fn extend(&mut self, sources: &[(&'static str, &'static str)]) { + // NOTE Some intrinsics have both a generic implementation (e.g. + // `floatdidf.c`) and an arch optimized implementation + // (`x86_64/floatdidf.c`). In those cases, we keep the arch optimized + // implementation and discard the generic implementation. If we don't + // and keep both implementations, the linker will yell at us about + // duplicate symbols! + for (symbol, src) in sources { + if src.contains("/") { + // Arch-optimized implementation (preferred) + self.map.insert(symbol, src); + } else { + // Generic implementation + if !self.map.contains_key(symbol) { + self.map.insert(symbol, src); + } + } + } + } + + fn remove(&mut self, symbols: &[&str]) { + for symbol in symbols { + self.map.remove(*symbol).unwrap(); + } + } + } + + /// Compile intrinsics from the compiler-rt C source code + pub fn compile(cfg: &Config) { + let llvm_target = &cfg.target_triple_split; + let mut consider_float_intrinsics = true; + let build = &mut cc::Build::new(); + + // AArch64 GCCs exit with an error condition when they encounter any kind of floating point + // code if the `nofp` and/or `nosimd` compiler flags have been set. + // + // Therefore, evaluate if those flags are present and set a boolean that causes any + // compiler-rt intrinsics that contain floating point source to be excluded for this target. + if cfg.target_arch == "aarch64" { + let cflags_key = String::from("CFLAGS_") + &(cfg.target_triple.replace("-", "_")); + if let Ok(cflags_value) = env::var(cflags_key) { + if cflags_value.contains("+nofp") || cflags_value.contains("+nosimd") { + consider_float_intrinsics = false; + } + } + } + + // `compiler-rt` requires `COMPILER_RT_HAS_FLOAT16` to be defined to make it use the + // `_Float16` type for `f16` intrinsics. This shouldn't matter as all existing `f16` + // intrinsics have been ported to Rust in `compiler-builtins` as C compilers don't + // support `_Float16` on all targets (whereas Rust does). However, define the macro + // anyway to prevent issues like rust#118813 and rust#123885 silently reoccuring if more + // `f16` intrinsics get accidentally added here in the future. + build.define("COMPILER_RT_HAS_FLOAT16", None); + + build.warnings(false); + + if cfg.target_env == "msvc" { + // Don't pull in extra libraries on MSVC + build.flag("/Zl"); + + // Emulate C99 and C++11's __func__ for MSVC prior to 2013 CTP + build.define("__func__", Some("__FUNCTION__")); + } else { + // Turn off various features of gcc and such, mostly copying + // compiler-rt's build system already + build.flag("-fno-builtin"); + build.flag("-fvisibility=hidden"); + build.flag("-ffreestanding"); + // Avoid the following warning appearing once **per file**: + // clang: warning: optimization flag '-fomit-frame-pointer' is not supported for target 'armv7' [-Wignored-optimization-argument] + // + // Note that compiler-rt's build system also checks + // + // `check_cxx_compiler_flag(-fomit-frame-pointer COMPILER_RT_HAS_FOMIT_FRAME_POINTER_FLAG)` + // + // in https://github.com/rust-lang/compiler-rt/blob/c8fbcb3/cmake/config-ix.cmake#L19. + build.flag_if_supported("-fomit-frame-pointer"); + build.define("VISIBILITY_HIDDEN", None); + + if let "aarch64" | "arm64ec" = cfg.target_arch.as_str() { + // FIXME(llvm20): Older GCCs on A64 fail to build with + // -Werror=implicit-function-declaration due to a compiler-rt bug. + // With a newer LLVM we should be able to enable the flag everywhere. + // https://github.com/llvm/llvm-project/commit/8aa9d6206ce55bdaaf422839c351fbd63f033b89 + } else { + // Avoid implicitly creating references to undefined functions + build.flag("-Werror=implicit-function-declaration"); + } + } + + // int_util.c tries to include stdlib.h if `_WIN32` is defined, + // which it is when compiling UEFI targets with clang. This is + // at odds with compiling with `-ffreestanding`, as the header + // may be incompatible or not present. Create a minimal stub + // header to use instead. + if cfg.target_os == "uefi" { + let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); + let include_dir = out_dir.join("include"); + if !include_dir.exists() { + fs::create_dir(&include_dir).unwrap(); + } + fs::write(include_dir.join("stdlib.h"), "#include ").unwrap(); + build.flag(&format!("-I{}", include_dir.to_str().unwrap())); + } + + let mut sources = Sources::new(); + sources.extend(&[ + ("__absvdi2", "absvdi2.c"), + ("__absvsi2", "absvsi2.c"), + ("__addvdi3", "addvdi3.c"), + ("__addvsi3", "addvsi3.c"), + ("__cmpdi2", "cmpdi2.c"), + ("__int_util", "int_util.c"), + ("__mulvdi3", "mulvdi3.c"), + ("__mulvsi3", "mulvsi3.c"), + ("__negdi2", "negdi2.c"), + ("__negvdi2", "negvdi2.c"), + ("__negvsi2", "negvsi2.c"), + ("__paritydi2", "paritydi2.c"), + ("__paritysi2", "paritysi2.c"), + ("__popcountdi2", "popcountdi2.c"), + ("__popcountsi2", "popcountsi2.c"), + ("__subvdi3", "subvdi3.c"), + ("__subvsi3", "subvsi3.c"), + ("__ucmpdi2", "ucmpdi2.c"), + ]); + + if consider_float_intrinsics { + sources.extend(&[ + ("__divdc3", "divdc3.c"), + ("__divsc3", "divsc3.c"), + ("__muldc3", "muldc3.c"), + ("__mulsc3", "mulsc3.c"), + ("__negdf2", "negdf2.c"), + ("__negsf2", "negsf2.c"), + ]); + } + + // On iOS and 32-bit OSX these are all just empty intrinsics, no need to + // include them. + if cfg.target_vendor != "apple" || cfg.target_arch != "x86" { + sources.extend(&[ + ("__absvti2", "absvti2.c"), + ("__addvti3", "addvti3.c"), + ("__cmpti2", "cmpti2.c"), + ("__ffsti2", "ffsti2.c"), + ("__mulvti3", "mulvti3.c"), + ("__negti2", "negti2.c"), + ("__parityti2", "parityti2.c"), + ("__popcountti2", "popcountti2.c"), + ("__subvti3", "subvti3.c"), + ("__ucmpti2", "ucmpti2.c"), + ]); + + if consider_float_intrinsics { + sources.extend(&[("__negvti2", "negvti2.c")]); + } + } + + if cfg.target_vendor == "apple" { + sources.extend(&[ + ("atomic_flag_clear", "atomic_flag_clear.c"), + ("atomic_flag_clear_explicit", "atomic_flag_clear_explicit.c"), + ("atomic_flag_test_and_set", "atomic_flag_test_and_set.c"), + ( + "atomic_flag_test_and_set_explicit", + "atomic_flag_test_and_set_explicit.c", + ), + ("atomic_signal_fence", "atomic_signal_fence.c"), + ("atomic_thread_fence", "atomic_thread_fence.c"), + ]); + } + + if cfg.target_env != "msvc" { + if cfg.target_arch == "x86" { + sources.extend(&[ + ("__ashldi3", "i386/ashldi3.S"), + ("__ashrdi3", "i386/ashrdi3.S"), + ("__divdi3", "i386/divdi3.S"), + ("__lshrdi3", "i386/lshrdi3.S"), + ("__moddi3", "i386/moddi3.S"), + ("__muldi3", "i386/muldi3.S"), + ("__udivdi3", "i386/udivdi3.S"), + ("__umoddi3", "i386/umoddi3.S"), + ]); + } + } + + if cfg.target_arch == "arm" && cfg.target_vendor != "apple" && cfg.target_env != "msvc" { + sources.extend(&[ + ("__aeabi_div0", "arm/aeabi_div0.c"), + ("__aeabi_drsub", "arm/aeabi_drsub.c"), + ("__aeabi_frsub", "arm/aeabi_frsub.c"), + ("__bswapdi2", "arm/bswapdi2.S"), + ("__bswapsi2", "arm/bswapsi2.S"), + ("__divmodsi4", "arm/divmodsi4.S"), + ("__divsi3", "arm/divsi3.S"), + ("__modsi3", "arm/modsi3.S"), + ("__switch16", "arm/switch16.S"), + ("__switch32", "arm/switch32.S"), + ("__switch8", "arm/switch8.S"), + ("__switchu8", "arm/switchu8.S"), + ("__sync_synchronize", "arm/sync_synchronize.S"), + ("__udivmodsi4", "arm/udivmodsi4.S"), + ("__udivsi3", "arm/udivsi3.S"), + ("__umodsi3", "arm/umodsi3.S"), + ]); + + if cfg.target_os == "freebsd" { + sources.extend(&[("__clear_cache", "clear_cache.c")]); + } + + // First of all aeabi_cdcmp and aeabi_cfcmp are never called by LLVM. + // Second are little-endian only, so build fail on big-endian targets. + // Temporally workaround: exclude these files for big-endian targets. + if !llvm_target[0].starts_with("thumbeb") && !llvm_target[0].starts_with("armeb") { + sources.extend(&[ + ("__aeabi_cdcmp", "arm/aeabi_cdcmp.S"), + ("__aeabi_cdcmpeq_check_nan", "arm/aeabi_cdcmpeq_check_nan.c"), + ("__aeabi_cfcmp", "arm/aeabi_cfcmp.S"), + ("__aeabi_cfcmpeq_check_nan", "arm/aeabi_cfcmpeq_check_nan.c"), + ]); + } + } + + if llvm_target[0] == "armv7" { + sources.extend(&[ + ("__sync_fetch_and_add_4", "arm/sync_fetch_and_add_4.S"), + ("__sync_fetch_and_add_8", "arm/sync_fetch_and_add_8.S"), + ("__sync_fetch_and_and_4", "arm/sync_fetch_and_and_4.S"), + ("__sync_fetch_and_and_8", "arm/sync_fetch_and_and_8.S"), + ("__sync_fetch_and_max_4", "arm/sync_fetch_and_max_4.S"), + ("__sync_fetch_and_max_8", "arm/sync_fetch_and_max_8.S"), + ("__sync_fetch_and_min_4", "arm/sync_fetch_and_min_4.S"), + ("__sync_fetch_and_min_8", "arm/sync_fetch_and_min_8.S"), + ("__sync_fetch_and_nand_4", "arm/sync_fetch_and_nand_4.S"), + ("__sync_fetch_and_nand_8", "arm/sync_fetch_and_nand_8.S"), + ("__sync_fetch_and_or_4", "arm/sync_fetch_and_or_4.S"), + ("__sync_fetch_and_or_8", "arm/sync_fetch_and_or_8.S"), + ("__sync_fetch_and_sub_4", "arm/sync_fetch_and_sub_4.S"), + ("__sync_fetch_and_sub_8", "arm/sync_fetch_and_sub_8.S"), + ("__sync_fetch_and_umax_4", "arm/sync_fetch_and_umax_4.S"), + ("__sync_fetch_and_umax_8", "arm/sync_fetch_and_umax_8.S"), + ("__sync_fetch_and_umin_4", "arm/sync_fetch_and_umin_4.S"), + ("__sync_fetch_and_umin_8", "arm/sync_fetch_and_umin_8.S"), + ("__sync_fetch_and_xor_4", "arm/sync_fetch_and_xor_4.S"), + ("__sync_fetch_and_xor_8", "arm/sync_fetch_and_xor_8.S"), + ]); + } + + if llvm_target.last().unwrap().ends_with("eabihf") { + if !llvm_target[0].starts_with("thumbv7em") + && !llvm_target[0].starts_with("thumbv8m.main") + { + // The FPU option chosen for these architectures in cc-rs, ie: + // -mfpu=fpv4-sp-d16 for thumbv7em + // -mfpu=fpv5-sp-d16 for thumbv8m.main + // do not support double precision floating points conversions so the files + // that include such instructions are not included for these targets. + sources.extend(&[ + ("__fixdfsivfp", "arm/fixdfsivfp.S"), + ("__fixunsdfsivfp", "arm/fixunsdfsivfp.S"), + ("__floatsidfvfp", "arm/floatsidfvfp.S"), + ("__floatunssidfvfp", "arm/floatunssidfvfp.S"), + ]); + } + + sources.extend(&[ + ("__fixsfsivfp", "arm/fixsfsivfp.S"), + ("__fixunssfsivfp", "arm/fixunssfsivfp.S"), + ("__floatsisfvfp", "arm/floatsisfvfp.S"), + ("__floatunssisfvfp", "arm/floatunssisfvfp.S"), + ("__floatunssisfvfp", "arm/floatunssisfvfp.S"), + ("__restore_vfp_d8_d15_regs", "arm/restore_vfp_d8_d15_regs.S"), + ("__save_vfp_d8_d15_regs", "arm/save_vfp_d8_d15_regs.S"), + ("__negdf2vfp", "arm/negdf2vfp.S"), + ("__negsf2vfp", "arm/negsf2vfp.S"), + ]); + } + + if (cfg.target_arch == "aarch64" || cfg.target_arch == "arm64ec") + && consider_float_intrinsics + { + sources.extend(&[ + ("__fe_getround", "fp_mode.c"), + ("__fe_raise_inexact", "fp_mode.c"), + ]); + + if cfg.target_os != "windows" && cfg.target_os != "cygwin" { + sources.extend(&[("__multc3", "multc3.c")]); + } + } + + if cfg.target_arch == "mips" || cfg.target_arch == "riscv32" || cfg.target_arch == "riscv64" + { + sources.extend(&[("__bswapsi2", "bswapsi2.c")]); + } + + if cfg.target_arch == "mips64" { + sources.extend(&[("__fe_getround", "fp_mode.c")]); + } + + if cfg.target_arch == "loongarch64" { + sources.extend(&[("__fe_getround", "fp_mode.c")]); + } + + // Remove the assembly implementations that won't compile for the target + if llvm_target[0] == "thumbv6m" + || llvm_target[0] == "thumbv8m.base" + || cfg.target_os == "uefi" + { + let mut to_remove = Vec::new(); + for (k, v) in sources.map.iter() { + if v.ends_with(".S") { + to_remove.push(*k); + } + } + sources.remove(&to_remove); + } + + if llvm_target[0] == "thumbv7m" || llvm_target[0] == "thumbv7em" { + sources.remove(&["__aeabi_cdcmp", "__aeabi_cfcmp"]); + } + + // Android and Cygwin uses emulated TLS so we need a runtime support function. + if cfg.target_os == "android" || cfg.target_os == "cygwin" { + sources.extend(&[("__emutls_get_address", "emutls.c")]); + } + + // Work around a bug in the NDK headers (fixed in + // https://r.android.com/2038949 which will be released in a future + // NDK version) by providing a definition of LONG_BIT. + if cfg.target_os == "android" { + build.define("LONG_BIT", "(8 * sizeof(long))"); + } + + // OpenHarmony also uses emulated TLS. + if cfg.target_env == "ohos" { + sources.extend(&[("__emutls_get_address", "emutls.c")]); + } + + // Optionally, link against a prebuilt llvm compiler-rt containing the builtins + // library. Only the builtins library is required. On many platforms, this is + // available as a library named libclang_rt.builtins.a. + let link_against_prebuilt_rt = env::var_os("LLVM_COMPILER_RT_LIB").is_some(); + + // When compiling the C code we require the user to tell us where the + // source code is, and this is largely done so when we're compiling as + // part of rust-lang/rust we can use the same llvm-project repository as + // rust-lang/rust. + let root = match env::var_os("RUST_COMPILER_RT_ROOT") { + Some(s) => PathBuf::from(s), + // If a prebuild libcompiler-rt is provided, set a valid + // path to simplify later logic. Nothing should be compiled. + None if link_against_prebuilt_rt => PathBuf::new(), + None => { + panic!( + "RUST_COMPILER_RT_ROOT is not set. You may need to run \ + `ci/download-compiler-rt.sh`." + ); + } + }; + if !link_against_prebuilt_rt && !root.exists() { + panic!("RUST_COMPILER_RT_ROOT={} does not exist", root.display()); + } + + // Support deterministic builds by remapping the __FILE__ prefix if the + // compiler supports it. This fixes the nondeterminism caused by the + // use of that macro in lib/builtins/int_util.h in compiler-rt. + build.flag_if_supported(&format!("-ffile-prefix-map={}=.", root.display())); + + // Include out-of-line atomics for aarch64, which are all generated by supplying different + // sets of flags to the same source file. + // Note: Out-of-line aarch64 atomics are not supported by the msvc toolchain (#430) and + // on uefi. + let src_dir = root.join("lib/builtins"); + if cfg.target_arch == "aarch64" && cfg.target_env != "msvc" && cfg.target_os != "uefi" { + // See below for why we're building these as separate libraries. + build_aarch64_out_of_line_atomics_libraries(&src_dir, build, link_against_prebuilt_rt); + + // Some run-time CPU feature detection is necessary, as well. + let cpu_model_src = if src_dir.join("cpu_model.c").exists() { + "cpu_model.c" + } else { + "cpu_model/aarch64.c" + }; + sources.extend(&[("__aarch64_have_lse_atomics", cpu_model_src)]); + } + + let mut added_sources = HashSet::new(); + for (sym, src) in sources.map.iter() { + let src = src_dir.join(src); + if !link_against_prebuilt_rt && added_sources.insert(src.clone()) { + build.file(&src); + println!("cargo:rerun-if-changed={}", src.display()); + } + println!("cargo:rustc-cfg={}=\"optimized-c\"", sym); + } + + if link_against_prebuilt_rt { + let rt_builtins_ext = PathBuf::from(env::var_os("LLVM_COMPILER_RT_LIB").unwrap()); + if !rt_builtins_ext.exists() { + panic!( + "LLVM_COMPILER_RT_LIB={} does not exist", + rt_builtins_ext.display() + ); + } + if let Some(dir) = rt_builtins_ext.parent() { + println!("cargo::rustc-link-search=native={}", dir.display()); + } + if let Some(lib) = rt_builtins_ext.file_name() { + println!( + "cargo::rustc-link-lib=static:+verbatim={}", + lib.to_str().unwrap() + ); + } + } else { + build.compile("libcompiler-rt.a"); + } + } + + fn build_aarch64_out_of_line_atomics_libraries( + builtins_dir: &Path, + cfg: &mut cc::Build, + link_against_prebuilt_rt: bool, + ) { + let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); + let outlined_atomics_file = builtins_dir.join("aarch64").join("lse.S"); + if !link_against_prebuilt_rt { + println!("cargo:rerun-if-changed={}", outlined_atomics_file.display()); + } + + cfg.include(&builtins_dir); + + for instruction_type in &["cas", "swp", "ldadd", "ldclr", "ldeor", "ldset"] { + for size in &[1, 2, 4, 8, 16] { + if *size == 16 && *instruction_type != "cas" { + continue; + } + + for (model_number, model_name) in + &[(1, "relax"), (2, "acq"), (3, "rel"), (4, "acq_rel")] + { + let sym = format!("__aarch64_{}{}_{}", instruction_type, size, model_name); + println!("cargo:rustc-cfg={}=\"optimized-c\"", sym); + + if link_against_prebuilt_rt { + continue; + } + + // The original compiler-rt build system compiles the same + // source file multiple times with different compiler + // options. Here we do something slightly different: we + // create multiple .S files with the proper #defines and + // then include the original file. + // + // This is needed because the cc crate doesn't allow us to + // override the name of object files and libtool requires + // all objects in an archive to have unique names. + let path = + out_dir.join(format!("lse_{}{}_{}.S", instruction_type, size, model_name)); + let mut file = File::create(&path).unwrap(); + writeln!(file, "#define L_{}", instruction_type).unwrap(); + writeln!(file, "#define SIZE {}", size).unwrap(); + writeln!(file, "#define MODEL {}", model_number).unwrap(); + writeln!( + file, + "#include \"{}\"", + outlined_atomics_file.canonicalize().unwrap().display() + ) + .unwrap(); + drop(file); + cfg.file(path); + } + } + } + } +} diff --git a/ci/licenses/rust-compiler-originals/compiler-builtins-float-conv.rs.txt b/ci/licenses/rust-compiler-originals/compiler-builtins-float-conv.rs.txt new file mode 100644 index 0000000..6193aa4 --- /dev/null +++ b/ci/licenses/rust-compiler-originals/compiler-builtins-float-conv.rs.txt @@ -0,0 +1,508 @@ +use core::ops::Neg; + +use crate::support::{CastFrom, CastInto, Float, Int, MinInt}; + +/// Conversions from integers to floats. +/// +/// The algorithm is explained here: . It roughly does the following: +/// - Calculate a base mantissa by shifting the integer into mantissa position. This gives us a +/// mantissa _with the implicit bit set_! +/// - Figure out if rounding needs to occur by classifying the bits that are to be truncated. Some +/// patterns are used to simplify this. Adjust the mantissa with the result if needed. +/// - Calculate the exponent based on the base-2 logarithm of `i` (leading zeros). Subtract one. +/// - Shift the exponent and add the mantissa to create the final representation. Subtracting one +/// from the exponent (above) accounts for the explicit bit being set in the mantissa. +/// +/// # Terminology +/// +/// - `i`: the original integer +/// - `i_m`: the integer, shifted fully left (no leading zeros) +/// - `n`: number of leading zeroes +/// - `e`: the resulting exponent. Usually 1 is subtracted to offset the mantissa implicit bit. +/// - `m_base`: the mantissa before adjusting for truncated bits. Implicit bit is usually set. +/// - `adj`: the bits that will be truncated, possibly compressed in some way. +/// - `m`: the resulting mantissa. Implicit bit is usually set. +mod int_to_float { + use super::*; + + /// Calculate the exponent from the number of leading zeros. + /// + /// Usually 1 is subtracted from this function's result, so that a mantissa with the implicit + /// bit set can be added back later. + fn exp>>(n: u32) -> F::Int { + F::Int::cast_from(F::EXP_BIAS - 1 + I::BITS - n) + } + + /// Adjust a mantissa with dropped bits to perform correct rounding. + /// + /// The dropped bits should be exactly the bits that get truncated (left-aligned), but they + /// can be combined or compressed in some way that simplifies operations. + fn m_adj(m_base: F::Int, dropped_bits: F::Int) -> F::Int { + // Branchlessly extract a `1` if rounding up should happen, 0 otherwise + // This accounts for rounding to even. + let adj = (dropped_bits - ((dropped_bits >> (F::BITS - 1)) & !m_base)) >> (F::BITS - 1); + + // Add one when we need to round up. Break ties to even. + m_base + adj + } + + /// Shift the exponent to its position and add the mantissa. + /// + /// If the mantissa has the implicit bit set, the exponent should be one less than its actual + /// value to cancel it out. + fn repr(e: F::Int, m: F::Int) -> F::Int { + // + rather than | so the mantissa can overflow into the exponent + (e << F::SIG_BITS) + m + } + + /// Shift distance from a left-aligned integer to a smaller float. + fn shift_f_lt_i() -> u32 { + (I::BITS - F::BITS) + F::EXP_BITS + } + + /// Shift distance from an integer with `n` leading zeros to a smaller float. + fn shift_f_gt_i(n: u32) -> u32 { + F::SIG_BITS - I::BITS + 1 + n + } + + /// Perform a signed operation as unsigned, then add the sign back. + pub fn signed(i: I, conv: Conv) -> F + where + F: Float, + I: Int, + F::Int: CastFrom, + Conv: Fn(I::Unsigned) -> F::Int, + { + let sign_bit = F::Int::cast_from_lossy(i >> (I::BITS - 1)) << (F::BITS - 1); + F::from_bits(conv(i.unsigned_abs()) | sign_bit) + } + + pub fn u32_to_f32_bits(i: u32) -> u32 { + if i == 0 { + return 0; + } + let n = i.leading_zeros(); + // Mantissa with implicit bit set (significant bits) + let m_base = (i << n) >> f32::EXP_BITS; + // Bits that will be dropped (insignificant bits) + let adj = (i << n) << (f32::SIG_BITS + 1); + let m = m_adj::(m_base, adj); + let e = exp::(n) - 1; + repr::(e, m) + } + + pub fn u32_to_f64_bits(i: u32) -> u64 { + if i == 0 { + return 0; + } + let n = i.leading_zeros(); + // Mantissa with implicit bit set + let m = (i as u64) << shift_f_gt_i::(n); + let e = exp::(n) - 1; + repr::(e, m) + } + + #[cfg(f128_enabled)] + pub fn u32_to_f128_bits(i: u32) -> u128 { + if i == 0 { + return 0; + } + let n = i.leading_zeros(); + + // Shift into mantissa position that is correct for the type, but shifted into the lower + // 64 bits over so can can avoid 128-bit math. + let m = (i as u64) << (shift_f_gt_i::(n) - 64); + let e = exp::(n) as u64 - 1; + // High 64 bits of f128 representation. + let h = (e << (f128::SIG_BITS - 64)) + m; + + // Shift back to the high bits, the rest of the mantissa will always be 0. + (h as u128) << 64 + } + + pub fn u64_to_f32_bits(i: u64) -> u32 { + let n = i.leading_zeros(); + let i_m = i.wrapping_shl(n); + // Mantissa with implicit bit set + let m_base: u32 = (i_m >> shift_f_lt_i::()) as u32; + // The entire lower half of `i` will be truncated (masked portion), plus the + // next `EXP_BITS` bits. + let adj = ((i_m >> f32::EXP_BITS) | i_m & 0xFFFF) as u32; + let m = m_adj::(m_base, adj); + let e = if i == 0 { 0 } else { exp::(n) - 1 }; + repr::(e, m) + } + + pub fn u64_to_f64_bits(i: u64) -> u64 { + if i == 0 { + return 0; + } + let n = i.leading_zeros(); + // Mantissa with implicit bit set + let m_base = (i << n) >> f64::EXP_BITS; + let adj = (i << n) << (f64::SIG_BITS + 1); + let m = m_adj::(m_base, adj); + let e = exp::(n) - 1; + repr::(e, m) + } + + #[cfg(f128_enabled)] + pub fn u64_to_f128_bits(i: u64) -> u128 { + if i == 0 { + return 0; + } + let n = i.leading_zeros(); + // Mantissa with implicit bit set + let m = (i as u128) << shift_f_gt_i::(n); + let e = exp::(n) - 1; + repr::(e, m) + } + + pub fn u128_to_f32_bits(i: u128) -> u32 { + let n = i.leading_zeros(); + let i_m = i.wrapping_shl(n); // Mantissa, shifted so the first bit is nonzero + let m_base: u32 = (i_m >> shift_f_lt_i::()) as u32; + + // Within the upper `F::BITS`, everything except for the signifcand + // gets truncated + let d1: u32 = (i_m >> (u128::BITS - f32::BITS - f32::SIG_BITS - 1)).cast_lossy(); + + // The entire rest of `i_m` gets truncated. Zero the upper `F::BITS` then just + // check if it is nonzero. + let d2: u32 = (i_m << f32::BITS >> f32::BITS != 0).into(); + let adj = d1 | d2; + + // Mantissa with implicit bit set + let m = m_adj::(m_base, adj); + let e = if i == 0 { 0 } else { exp::(n) - 1 }; + repr::(e, m) + } + + pub fn u128_to_f64_bits(i: u128) -> u64 { + let n = i.leading_zeros(); + let i_m = i.wrapping_shl(n); + // Mantissa with implicit bit set + let m_base: u64 = (i_m >> shift_f_lt_i::()) as u64; + // The entire lower half of `i` will be truncated (masked portion), plus the + // next `EXP_BITS` bits. + let adj = ((i_m >> f64::EXP_BITS) | i_m & 0xFFFF_FFFF) as u64; + let m = m_adj::(m_base, adj); + let e = if i == 0 { 0 } else { exp::(n) - 1 }; + repr::(e, m) + } + + #[cfg(f128_enabled)] + pub fn u128_to_f128_bits(i: u128) -> u128 { + if i == 0 { + return 0; + } + let n = i.leading_zeros(); + // Mantissa with implicit bit set + let m_base = (i << n) >> f128::EXP_BITS; + let adj = (i << n) << (f128::SIG_BITS + 1); + let m = m_adj::(m_base, adj); + let e = exp::(n) - 1; + repr::(e, m) + } +} + +// Conversions from unsigned integers to floats. +intrinsics! { + #[arm_aeabi_alias = __aeabi_ui2f] + pub extern "C" fn __floatunsisf(i: u32) -> f32 { + f32::from_bits(int_to_float::u32_to_f32_bits(i)) + } + + #[arm_aeabi_alias = __aeabi_ui2d] + pub extern "C" fn __floatunsidf(i: u32) -> f64 { + f64::from_bits(int_to_float::u32_to_f64_bits(i)) + } + + #[arm_aeabi_alias = __aeabi_ul2f] + pub extern "C" fn __floatundisf(i: u64) -> f32 { + f32::from_bits(int_to_float::u64_to_f32_bits(i)) + } + + #[arm_aeabi_alias = __aeabi_ul2d] + pub extern "C" fn __floatundidf(i: u64) -> f64 { + f64::from_bits(int_to_float::u64_to_f64_bits(i)) + } + + #[cfg(not(all(target_os = "uefi", target_arch = "x86_64")))] + pub extern "C" fn __floatuntisf(i: u128) -> f32 { + f32::from_bits(int_to_float::u128_to_f32_bits(i)) + } + + #[cfg(all(target_os = "uefi", target_arch = "x86_64"))] + pub extern "C" fn __floatuntisf(lo: u64, hi: u64) -> f32 { + f32::from_bits(int_to_float::u128_to_f32_bits((u128::from(hi) << 64) | u128::from(lo))) + } + + #[cfg(not(all(target_os = "uefi", target_arch = "x86_64")))] + pub extern "C" fn __floatuntidf(i: u128) -> f64 { + f64::from_bits(int_to_float::u128_to_f64_bits(i)) + } + + #[cfg(all(target_os = "uefi", target_arch = "x86_64"))] + pub extern "C" fn __floatuntidf(lo: u64, hi: u64) -> f64 { + f64::from_bits(int_to_float::u128_to_f64_bits((u128::from(hi) << 64) | u128::from(lo))) + } + + #[ppc_alias = __floatunsikf] + #[cfg(f128_enabled)] + pub extern "C" fn __floatunsitf(i: u32) -> f128 { + f128::from_bits(int_to_float::u32_to_f128_bits(i)) + } + + #[ppc_alias = __floatundikf] + #[cfg(f128_enabled)] + pub extern "C" fn __floatunditf(i: u64) -> f128 { + f128::from_bits(int_to_float::u64_to_f128_bits(i)) + } + + #[ppc_alias = __floatuntikf] + #[cfg(f128_enabled)] + pub extern "C" fn __floatuntitf(i: u128) -> f128 { + f128::from_bits(int_to_float::u128_to_f128_bits(i)) + } +} + +// Conversions from signed integers to floats. +intrinsics! { + #[arm_aeabi_alias = __aeabi_i2f] + pub extern "C" fn __floatsisf(i: i32) -> f32 { + int_to_float::signed(i, int_to_float::u32_to_f32_bits) + } + + #[arm_aeabi_alias = __aeabi_i2d] + pub extern "C" fn __floatsidf(i: i32) -> f64 { + int_to_float::signed(i, int_to_float::u32_to_f64_bits) + } + + #[arm_aeabi_alias = __aeabi_l2f] + pub extern "C" fn __floatdisf(i: i64) -> f32 { + int_to_float::signed(i, int_to_float::u64_to_f32_bits) + } + + #[arm_aeabi_alias = __aeabi_l2d] + pub extern "C" fn __floatdidf(i: i64) -> f64 { + int_to_float::signed(i, int_to_float::u64_to_f64_bits) + } + + #[cfg(not(all(target_os = "uefi", target_arch = "x86_64")))] + pub extern "C" fn __floattisf(i: i128) -> f32 { + int_to_float::signed(i, int_to_float::u128_to_f32_bits) + } + + #[cfg(all(target_os = "uefi", target_arch = "x86_64"))] + pub extern "C" fn __floattisf(lo: u64, hi: u64) -> f32 { + int_to_float::signed((i128::from(hi) << 64) | i128::from(lo), int_to_float::u128_to_f32_bits) + } + + #[cfg(not(all(target_os = "uefi", target_arch = "x86_64")))] + pub extern "C" fn __floattidf(i: i128) -> f64 { + int_to_float::signed(i, int_to_float::u128_to_f64_bits) + } + + #[cfg(all(target_os = "uefi", target_arch = "x86_64"))] + pub extern "C" fn __floattidf(lo: u64, hi: u64) -> f64 { + int_to_float::signed((i128::from(hi) << 64) | i128::from(lo), int_to_float::u128_to_f64_bits) + } + + #[ppc_alias = __floatsikf] + #[cfg(f128_enabled)] + pub extern "C" fn __floatsitf(i: i32) -> f128 { + int_to_float::signed(i, int_to_float::u32_to_f128_bits) + } + + #[ppc_alias = __floatdikf] + #[cfg(f128_enabled)] + pub extern "C" fn __floatditf(i: i64) -> f128 { + int_to_float::signed(i, int_to_float::u64_to_f128_bits) + } + + #[ppc_alias = __floattikf] + #[cfg(f128_enabled)] + pub extern "C" fn __floattitf(i: i128) -> f128 { + int_to_float::signed(i, int_to_float::u128_to_f128_bits) + } +} + +/// Generic float to unsigned int conversions. +fn float_to_unsigned_int(f: F) -> U +where + F: Float, + U: Int, + F::Int: CastInto, + F::Int: CastFrom, + F::Int: CastInto, + u32: CastFrom, +{ + float_to_int_inner::(f.to_bits(), |i: U| i, || U::MAX) +} + +/// Generic float to signed int conversions. +fn float_to_signed_int(f: F) -> I +where + F: Float, + I: Int + Neg, + I::Unsigned: Int, + F::Int: CastInto, + F::Int: CastFrom, + u32: CastFrom, +{ + float_to_int_inner::( + f.to_bits() & !F::SIGN_MASK, + |i: I| if f.is_sign_negative() { -i } else { i }, + || if f.is_sign_negative() { I::MIN } else { I::MAX }, + ) +} + +/// Float to int conversions, generic for both signed and unsigned. +/// +/// Parameters: +/// - `fbits`: `abg(f)` bitcasted to an integer. +/// - `map_inbounds`: apply this transformation to integers that are within range (add the sign back). +/// - `out_of_bounds`: return value when out of range for `I`. +fn float_to_int_inner( + fbits: F::Int, + map_inbounds: FnFoo, + out_of_bounds: FnOob, +) -> I +where + F: Float, + I: Int, + FnFoo: FnOnce(I) -> I, + FnOob: FnOnce() -> I, + I::Unsigned: Int, + F::Int: CastInto, + F::Int: CastFrom, + u32: CastFrom, +{ + let int_max_exp = F::EXP_BIAS + I::MAX.ilog2() + 1; + let foobar = F::EXP_BIAS + I::Unsigned::BITS - 1; + + if fbits < F::ONE.to_bits() { + // < 0 gets rounded to 0 + I::ZERO + } else if fbits < F::Int::cast_from(int_max_exp) << F::SIG_BITS { + // >= 1, < integer max + let m_base = if I::Unsigned::BITS >= F::Int::BITS { + I::Unsigned::cast_from(fbits) << (I::BITS - F::SIG_BITS - 1) + } else { + I::Unsigned::cast_from_lossy(fbits >> (F::SIG_BITS - I::BITS + 1)) + }; + + // Set the implicit 1-bit. + let m: I::Unsigned = (I::Unsigned::ONE << (I::BITS - 1)) | m_base; + + // Shift based on the exponent and bias. + let s: u32 = (foobar) - u32::cast_from(fbits >> F::SIG_BITS); + + let unsigned = m >> s; + map_inbounds(I::from_unsigned(unsigned)) + } else if fbits <= F::EXP_MASK { + // >= max (incl. inf) + out_of_bounds() + } else { + I::ZERO + } +} + +// Conversions from floats to unsigned integers. +intrinsics! { + #[arm_aeabi_alias = __aeabi_f2uiz] + pub extern "C" fn __fixunssfsi(f: f32) -> u32 { + float_to_unsigned_int(f) + } + + #[arm_aeabi_alias = __aeabi_f2ulz] + pub extern "C" fn __fixunssfdi(f: f32) -> u64 { + float_to_unsigned_int(f) + } + + pub extern "C" fn __fixunssfti(f: f32) -> u128 { + float_to_unsigned_int(f) + } + + #[arm_aeabi_alias = __aeabi_d2uiz] + pub extern "C" fn __fixunsdfsi(f: f64) -> u32 { + float_to_unsigned_int(f) + } + + #[arm_aeabi_alias = __aeabi_d2ulz] + pub extern "C" fn __fixunsdfdi(f: f64) -> u64 { + float_to_unsigned_int(f) + } + + pub extern "C" fn __fixunsdfti(f: f64) -> u128 { + float_to_unsigned_int(f) + } + + #[ppc_alias = __fixunskfsi] + #[cfg(f128_enabled)] + pub extern "C" fn __fixunstfsi(f: f128) -> u32 { + float_to_unsigned_int(f) + } + + #[ppc_alias = __fixunskfdi] + #[cfg(f128_enabled)] + pub extern "C" fn __fixunstfdi(f: f128) -> u64 { + float_to_unsigned_int(f) + } + + #[ppc_alias = __fixunskfti] + #[cfg(f128_enabled)] + pub extern "C" fn __fixunstfti(f: f128) -> u128 { + float_to_unsigned_int(f) + } +} + +// Conversions from floats to signed integers. +intrinsics! { + #[arm_aeabi_alias = __aeabi_f2iz] + pub extern "C" fn __fixsfsi(f: f32) -> i32 { + float_to_signed_int(f) + } + + #[arm_aeabi_alias = __aeabi_f2lz] + pub extern "C" fn __fixsfdi(f: f32) -> i64 { + float_to_signed_int(f) + } + + pub extern "C" fn __fixsfti(f: f32) -> i128 { + float_to_signed_int(f) + } + + #[arm_aeabi_alias = __aeabi_d2iz] + pub extern "C" fn __fixdfsi(f: f64) -> i32 { + float_to_signed_int(f) + } + + #[arm_aeabi_alias = __aeabi_d2lz] + pub extern "C" fn __fixdfdi(f: f64) -> i64 { + float_to_signed_int(f) + } + + pub extern "C" fn __fixdfti(f: f64) -> i128 { + float_to_signed_int(f) + } + + #[ppc_alias = __fixkfsi] + #[cfg(f128_enabled)] + pub extern "C" fn __fixtfsi(f: f128) -> i32 { + float_to_signed_int(f) + } + + #[ppc_alias = __fixkfdi] + #[cfg(f128_enabled)] + pub extern "C" fn __fixtfdi(f: f128) -> i64 { + float_to_signed_int(f) + } + + #[ppc_alias = __fixkfti] + #[cfg(f128_enabled)] + pub extern "C" fn __fixtfti(f: f128) -> i128 { + float_to_signed_int(f) + } +} diff --git a/ci/licenses/rust-compiler-originals/compiler-builtins-math-mod.rs.txt b/ci/licenses/rust-compiler-originals/compiler-builtins-math-mod.rs.txt new file mode 100644 index 0000000..3dfa386 --- /dev/null +++ b/ci/licenses/rust-compiler-originals/compiler-builtins-math-mod.rs.txt @@ -0,0 +1,211 @@ +#[rustfmt::skip] +#[allow(dead_code)] +#[allow(unused_imports)] +#[allow(clippy::all)] +#[path = "../../../libm/src/math/mod.rs"] +pub(crate) mod libm_math; + +macro_rules! libm_intrinsics { + ($(fn $fun:ident($($iid:ident : $ity:ty),+) -> $oty:ty;)+) => { + intrinsics! { + $( + pub extern "C" fn $fun($($iid: $ity),+) -> $oty { + $crate::math::libm_math::$fun($($iid),+) + } + )+ + } + } +} + +/// This set of functions is well tested in `libm` and known to provide similar performance to +/// system `libm`, as well as the same or better accuracy. +pub mod full_availability { + #[cfg(f16_enabled)] + libm_intrinsics! { + fn ceilf16(x: f16) -> f16; + fn copysignf16(x: f16, y: f16) -> f16; + fn fabsf16(x: f16) -> f16; + fn fdimf16(x: f16, y: f16) -> f16; + fn floorf16(x: f16) -> f16; + fn fmaxf16(x: f16, y: f16) -> f16; + fn fmaximum_numf16(x: f16, y: f16) -> f16; + fn fmaximumf16(x: f16, y: f16) -> f16; + fn fminf16(x: f16, y: f16) -> f16; + fn fminimum_numf16(x: f16, y: f16) -> f16; + fn fminimumf16(x: f16, y: f16) -> f16; + fn fmodf16(x: f16, y: f16) -> f16; + fn rintf16(x: f16) -> f16; + fn roundevenf16(x: f16) -> f16; + fn roundf16(x: f16) -> f16; + fn sqrtf16(x: f16) -> f16; + fn truncf16(x: f16) -> f16; + } + + /* Weak linkage is unreliable on Windows and Apple, so we don't expose symbols that we know + * the system libc provides in order to avoid conflicts. */ + + #[cfg(all(not(windows), not(target_vendor = "apple")))] + libm_intrinsics! { + /* f32 */ + fn cbrtf(n: f32) -> f32; + fn ceilf(x: f32) -> f32; + fn copysignf(x: f32, y: f32) -> f32; + fn fabsf(x: f32) -> f32; + fn fdimf(a: f32, b: f32) -> f32; + fn floorf(x: f32) -> f32; + fn fmaf(x: f32, y: f32, z: f32) -> f32; + fn fmaxf(x: f32, y: f32) -> f32; + fn fminf(x: f32, y: f32) -> f32; + fn fmodf(x: f32, y: f32) -> f32; + fn rintf(x: f32) -> f32; + fn roundf(x: f32) -> f32; + fn sqrtf(x: f32) -> f32; + fn truncf(x: f32) -> f32; + + /* f64 */ + fn cbrt(x: f64) -> f64; + fn ceil(x: f64) -> f64; + fn copysign(x: f64, y: f64) -> f64; + fn fabs(x: f64) -> f64; + fn fdim(a: f64, b: f64) -> f64; + fn floor(x: f64) -> f64; + fn fma(x: f64, y: f64, z: f64) -> f64; + fn fmax(x: f64, y: f64) -> f64; + fn fmin(x: f64, y: f64) -> f64; + fn fmod(x: f64, y: f64) -> f64; + fn rint(x: f64) -> f64; + fn round(x: f64) -> f64; + fn sqrt(x: f64) -> f64; + fn trunc(x: f64) -> f64; + } + + // Windows and MacOS do not yet expose roundeven and IEEE 754-2019 `maximum` / `minimum`, + // however, so we still provide a fallback. + libm_intrinsics! { + fn fmaximum(x: f64, y: f64) -> f64; + fn fmaximum_num(x: f64, y: f64) -> f64; + fn fmaximum_numf(x: f32, y: f32) -> f32; + fn fmaximumf(x: f32, y: f32) -> f32; + fn fminimum(x: f64, y: f64) -> f64; + fn fminimum_num(x: f64, y: f64) -> f64; + fn fminimum_numf(x: f32, y: f32) -> f32; + fn fminimumf(x: f32, y: f32) -> f32; + fn roundeven(x: f64) -> f64; + fn roundevenf(x: f32) -> f32; + } + + #[cfg(f128_enabled)] + libm_intrinsics! { + fn ceilf128(x: f128) -> f128; + fn copysignf128(x: f128, y: f128) -> f128; + fn fabsf128(x: f128) -> f128; + fn fdimf128(x: f128, y: f128) -> f128; + fn floorf128(x: f128) -> f128; + fn fmaf128(x: f128, y: f128, z: f128) -> f128; + fn fmaxf128(x: f128, y: f128) -> f128; + fn fmaximum_numf128(x: f128, y: f128) -> f128; + fn fmaximumf128(x: f128, y: f128) -> f128; + fn fminf128(x: f128, y: f128) -> f128; + fn fminimum_numf128(x: f128, y: f128) -> f128; + fn fminimumf128(x: f128, y: f128) -> f128; + fn fmodf128(x: f128, y: f128) -> f128; + fn rintf128(x: f128) -> f128; + fn roundevenf128(x: f128) -> f128; + fn roundf128(x: f128) -> f128; + fn sqrtf128(x: f128) -> f128; + fn truncf128(x: f128) -> f128; + } +} + +/// This group of functions has more performance or precision issues than system versions, or +/// are otherwise less well tested. Provide them only on platforms that have problems with the +/// system `libm`. +/// +/// As `libm` improves, more functions will be moved from this group to the first group. +/// +/// Do not supply for any of the following: +/// - x86 without sse2 due to ABI issues +/// - +/// - but exclude UEFI since it is a soft-float target +/// - +/// - All unix targets (linux, macos, freebsd, android, etc) +/// - wasm with known target_os +#[cfg(not(any( + all( + target_arch = "x86", + not(target_feature = "sse2"), + not(target_os = "uefi"), + ), + unix, + all(target_family = "wasm", not(target_os = "unknown")) +)))] +pub mod partial_availability { + #[cfg(not(windows))] + libm_intrinsics! { + fn acos(x: f64) -> f64; + fn acosf(n: f32) -> f32; + fn acosh(x: f64) -> f64; + fn acoshf(x: f32) -> f32; + fn asin(x: f64) -> f64; + fn asinf(n: f32) -> f32; + fn asinh(x: f64) -> f64; + fn asinhf(x: f32) -> f32; + fn atan(x: f64) -> f64; + fn atan2(x: f64, y: f64) -> f64; + fn atan2f(a: f32, b: f32) -> f32; + fn atanf(n: f32) -> f32; + fn cos(x: f64) -> f64; + fn cosf(x: f32) -> f32; + fn cosh(x: f64) -> f64; + fn coshf(n: f32) -> f32; + fn erf(x: f64) -> f64; + fn erfc(x: f64) -> f64; + fn erfcf(x: f32) -> f32; + fn erff(x: f32) -> f32; + fn exp(x: f64) -> f64; + fn exp2(x: f64) -> f64; + fn exp2f(x: f32) -> f32; + fn expf(x: f32) -> f32; + fn expm1(x: f64) -> f64; + fn expm1f(n: f32) -> f32; + fn hypot(x: f64, y: f64) -> f64; + fn hypotf(x: f32, y: f32) -> f32; + fn ldexp(f: f64, n: i32) -> f64; + fn ldexpf(f: f32, n: i32) -> f32; + fn log(x: f64) -> f64; + fn log10(x: f64) -> f64; + fn log10f(x: f32) -> f32; + fn log1p(x: f64) -> f64; + fn log1pf(n: f32) -> f32; + fn log2(x: f64) -> f64; + fn log2f(x: f32) -> f32; + fn logf(x: f32) -> f32; + fn pow(x: f64, y: f64) -> f64; + fn powf(x: f32, y: f32) -> f32; + fn sin(x: f64) -> f64; + fn sinf(x: f32) -> f32; + fn sinh(x: f64) -> f64; + fn sinhf(n: f32) -> f32; + fn tan(x: f64) -> f64; + fn tanf(n: f32) -> f32; + fn tanh(x: f64) -> f64; + fn tanhf(n: f32) -> f32; + fn tgamma(x: f64) -> f64; + fn tgammaf(x: f32) -> f32; + } + + // allow for windows (and other targets) + intrinsics! { + pub extern "C" fn lgamma_r(x: f64, s: &mut i32) -> f64 { + let r = super::libm_math::lgamma_r(x); + *s = r.1; + r.0 + } + + pub extern "C" fn lgammaf_r(x: f32, s: &mut i32) -> f32 { + let r = super::libm_math::lgammaf_r(x); + *s = r.1; + r.0 + } + } +} diff --git a/ci/licenses/rust-compiler-originals/compiler-rt-CREDITS.TXT b/ci/licenses/rust-compiler-originals/compiler-rt-CREDITS.TXT new file mode 100644 index 0000000..6964eba --- /dev/null +++ b/ci/licenses/rust-compiler-originals/compiler-rt-CREDITS.TXT @@ -0,0 +1,36 @@ +This file is a partial list of people who have contributed to the LLVM/CompilerRT +project. If you have contributed a patch or made some other contribution to +LLVM/CompilerRT, please submit a patch to this file to add yourself, and it will be +done! + +The list is sorted by surname and formatted to allow easy grepping and +beautification by scripts. The fields are: name (N), email (E), web-address +(W), PGP key ID and fingerprint (P), description (D), and snail-mail address +(S). + +N: Craig van Vliet +E: cvanvliet@auroraux.org +W: http://www.auroraux.org +D: Code style and Readability fixes. + +N: Edward O'Callaghan +E: eocallaghan@auroraux.org +W: http://www.auroraux.org +D: CMake'ify Compiler-RT build system +D: Maintain Solaris & AuroraUX ports of Compiler-RT + +N: Howard Hinnant +E: hhinnant@apple.com +D: Architect and primary author of compiler-rt + +N: Guan-Hong Liu +E: koviankevin@hotmail.com +D: IEEE Quad-precision functions + +N: Joerg Sonnenberger +E: joerg@NetBSD.org +D: Maintains NetBSD port. + +N: Matt Thomas +E: matt@NetBSD.org +D: ARM improvements. diff --git a/ci/licenses/rust-compiler-originals/compiler-rt-LICENSE.TXT b/ci/licenses/rust-compiler-originals/compiler-rt-LICENSE.TXT new file mode 100644 index 0000000..5a79a1b --- /dev/null +++ b/ci/licenses/rust-compiler-originals/compiler-rt-LICENSE.TXT @@ -0,0 +1,311 @@ +============================================================================== +The LLVM Project is under the Apache License v2.0 with LLVM Exceptions: +============================================================================== + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +---- LLVM Exceptions to the Apache 2.0 License ---- + +As an exception, if, as a result of your compiling your source code, portions +of this Software are embedded into an Object form of such source code, you +may redistribute such embedded portions in such Object form without complying +with the conditions of Sections 4(a), 4(b) and 4(d) of the License. + +In addition, if you combine or link compiled forms of this Software with +software that is licensed under the GPLv2 ("Combined Software") and if a +court of competent jurisdiction determines that the patent provision (Section +3), the indemnity provision (Section 9) or other Section of the License +conflicts with the conditions of the GPLv2, you may retroactively and +prospectively choose to deem waived or otherwise exclude such Section(s) of +the License, but only in their entirety and only with respect to the Combined +Software. + +============================================================================== +Software from third parties included in the LLVM Project: +============================================================================== +The LLVM Project contains third party software which is under different license +terms. All such code will be identified clearly using at least one of two +mechanisms: +1) It will be in a separate directory tree with its own `LICENSE.txt` or + `LICENSE` file at the top containing the specific license and restrictions + which apply to that software, or +2) It will contain specific license and restriction terms at the top of every + file. + +============================================================================== +Legacy LLVM License (https://llvm.org/docs/DeveloperPolicy.html#legacy): +============================================================================== + +The compiler_rt library is dual licensed under both the University of Illinois +"BSD-Like" license and the MIT license. As a user of this code you may choose +to use it under either license. As a contributor, you agree to allow your code +to be used under both. + +Full text of the relevant licenses is included below. + +============================================================================== + +University of Illinois/NCSA +Open Source License + +Copyright (c) 2009-2019 by the contributors listed in CREDITS.TXT + +All rights reserved. + +Developed by: + + LLVM Team + + University of Illinois at Urbana-Champaign + + http://llvm.org + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal with +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimers. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimers in the + documentation and/or other materials provided with the distribution. + + * Neither the names of the LLVM Team, University of Illinois at + Urbana-Champaign, nor the names of its contributors may be used to + endorse or promote products derived from this Software without specific + prior written permission. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE +SOFTWARE. + +============================================================================== + +Copyright (c) 2009-2015 by the contributors listed in CREDITS.TXT + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/ci/licenses/rust-compiler-originals/compiler-rt-aarch64-cpu-model.c.txt b/ci/licenses/rust-compiler-originals/compiler-rt-aarch64-cpu-model.c.txt new file mode 100644 index 0000000..347bb4e --- /dev/null +++ b/ci/licenses/rust-compiler-originals/compiler-rt-aarch64-cpu-model.c.txt @@ -0,0 +1,94 @@ +//===-- cpu_model/aarch64.c - Support for __cpu_model builtin ----*- C -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is based on LLVM's lib/Support/Host.cpp. +// It implements __aarch64_have_lse_atomics, __aarch64_cpu_features for +// AArch64. +// +//===----------------------------------------------------------------------===// + +#include "aarch64.h" + +#if !defined(__aarch64__) && !defined(__arm64__) && !defined(_M_ARM64) && \ + !defined(__arm64ec__) && !defined(_M_ARM64EC) +#error This file is intended only for aarch64-based targets +#endif + +#if __has_include() +#include +#else +typedef struct __ifunc_arg_t { + unsigned long _size; + unsigned long _hwcap; + unsigned long _hwcap2; +} __ifunc_arg_t; +#endif // __has_include() + +// LSE support detection for out-of-line atomics +// using HWCAP and Auxiliary vector +_Bool __aarch64_have_lse_atomics + __attribute__((visibility("hidden"), nocommon)) = false; + +// The formatter wants to re-order these includes, but doing so is incorrect: +// clang-format off +#if defined(__FreeBSD__) || defined(__OpenBSD__) +#include +#include "aarch64/hwcap.inc" +#include "aarch64/lse_atomics/elf_aux_info.inc" +#elif defined(__Fuchsia__) +#include "aarch64/hwcap.inc" +#include "aarch64/lse_atomics/fuchsia.inc" +#elif defined(__ANDROID__) +#include +#include "aarch64/hwcap.inc" +#include "aarch64/lse_atomics/android.inc" +#elif defined(__linux__) +#include +#include "aarch64/hwcap.inc" +#include "aarch64/lse_atomics/getauxval.inc" +#elif defined(_WIN32) +#include "aarch64/lse_atomics/windows.inc" +#else +// When unimplemented, we leave __aarch64_have_lse_atomics initialized to false. +#endif +// clang-format on + +#if !defined(DISABLE_AARCH64_FMV) + +// Architecture features used +// in Function Multi Versioning +struct { + unsigned long long features; + // As features grows new fields could be added +} __aarch64_cpu_features __attribute__((visibility("hidden"), nocommon)); + +// The formatter wants to re-order these includes, but doing so is incorrect: +// clang-format off +#if defined(TARGET_BAREMETAL_AARCH64) +#include "aarch64/fmv/baremetal.inc" +#elif defined(__APPLE__) +#include "aarch64/fmv/apple.inc" +#elif defined(__FreeBSD__) || defined(__OpenBSD__) +#include "aarch64/fmv/hwcap.inc" +#include "aarch64/fmv/elf_aux_info.inc" +#elif defined(__Fuchsia__) +#include "aarch64/fmv/fuchsia.inc" +#elif defined(__ANDROID__) +#include "aarch64/fmv/hwcap.inc" +#include "aarch64/fmv/android.inc" +#elif defined(__linux__) +#include "aarch64/fmv/hwcap.inc" +#include "aarch64/fmv/getauxval.inc" +#elif defined(_WIN32) +#include "aarch64/fmv/windows.inc" +#else +#include "aarch64/fmv/unimplemented.inc" +#endif +// clang-format on + +#endif // !defined(DISABLE_AARCH64_FMV) diff --git a/ci/licenses/rust-compiler-originals/compiler-rt-aarch64-lse.S.txt b/ci/licenses/rust-compiler-originals/compiler-rt-aarch64-lse.S.txt new file mode 100644 index 0000000..a444d82 --- /dev/null +++ b/ci/licenses/rust-compiler-originals/compiler-rt-aarch64-lse.S.txt @@ -0,0 +1,270 @@ +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include "assembly.h" + +// Out-of-line LSE atomics helpers. Ported from libgcc library. +// N = {1, 2, 4, 8} +// M = {1, 2, 4, 8, 16} +// ORDER = {'relax', 'acq', 'rel', 'acq_rel', 'sync'} +// Routines implemented: +// +// iM __aarch64_casM_ORDER(iM expected, iM desired, iM *ptr) +// iN __aarch64_swpN_ORDER(iN val, iN *ptr) +// iN __aarch64_ldaddN_ORDER(iN val, iN *ptr) +// iN __aarch64_ldclrN_ORDER(iN val, iN *ptr) +// iN __aarch64_ldeorN_ORDER(iN val, iN *ptr) +// iN __aarch64_ldsetN_ORDER(iN val, iN *ptr) +// +// Routines may modify temporary registers tmp0, tmp1, tmp2, +// return value x0 and the flags only. + +#if defined(__aarch64__) || defined(__arm64ec__) + +#ifdef HAS_ASM_LSE +.arch armv8-a+lse +#else +.arch armv8-a +#endif + +#if !defined(__APPLE__) +HIDDEN(__aarch64_have_lse_atomics) +#else +HIDDEN(___aarch64_have_lse_atomics) +#endif + +// Generate mnemonics for +// L_cas: SIZE: 1,2,4,8,16 MODEL: 1,2,3,4,5 +// L_swp L_ldadd L_ldclr L_ldeor L_ldset: SIZE: 1,2,4,8 MODEL: 1,2,3,4,5 + +#if SIZE == 1 +#define S b +#define UXT uxtb +#define B 0x00000000 +#elif SIZE == 2 +#define S h +#define UXT uxth +#define B 0x40000000 +#elif SIZE == 4 || SIZE == 8 || SIZE == 16 +#define S +#define UXT mov +#if SIZE == 4 +#define B 0x80000000 +#elif SIZE == 8 +#define B 0xc0000000 +#endif +#else +#error +#endif // SIZE + +#if MODEL == 1 +#define SUFF _relax +#define A +#define L +#define M 0x000000 +#define N 0x000000 +#define BARRIER +#elif MODEL == 2 +#define SUFF _acq +#define A a +#define L +#define M 0x400000 +#define N 0x800000 +#define BARRIER +#elif MODEL == 3 +#define SUFF _rel +#define A +#define L l +#define M 0x008000 +#define N 0x400000 +#define BARRIER +#elif MODEL == 4 +#define SUFF _acq_rel +#define A a +#define L l +#define M 0x408000 +#define N 0xc00000 +#define BARRIER +#elif MODEL == 5 +#define SUFF _sync +#ifdef L_swp +// swp has _acq semantics. +#define A a +#define L +#define M 0x400000 +#define N 0x800000 +#else +// All other _sync functions have _seq semantics. +#define A a +#define L l +#define M 0x408000 +#define N 0xc00000 +#endif +#define BARRIER dmb ish +#else +#error +#endif // MODEL + +// Define register size. +#define x(N) GLUE2(x, N) +#define w(N) GLUE2(w, N) +#if SIZE < 8 +#define s(N) w(N) +#else +#define s(N) x(N) +#endif + +#define NAME(BASE) GLUE4(__aarch64_, BASE, SIZE, SUFF) +#if MODEL == 5 +// Drop A for _sync functions. +#define LDXR GLUE3(ld, xr, S) +#else +#define LDXR GLUE4(ld, A, xr, S) +#endif +#define STXR GLUE4(st, L, xr, S) + +// Define temporary registers. +#define tmp0 16 +#define tmp1 17 +#define tmp2 15 + +// Macro for branch to label if no LSE available +.macro JUMP_IF_NOT_LSE label +#if !defined(__APPLE__) + adrp x(tmp0), __aarch64_have_lse_atomics + ldrb w(tmp0), [x(tmp0), :lo12:__aarch64_have_lse_atomics] +#else + adrp x(tmp0), ___aarch64_have_lse_atomics@page + ldrb w(tmp0), [x(tmp0), ___aarch64_have_lse_atomics@pageoff] +#endif + cbz w(tmp0), \label +.endm + +#ifdef L_cas +DEFINE_COMPILERRT_OUTLINE_FUNCTION_UNMANGLED(NAME(cas)) + JUMP_IF_NOT_LSE 8f +#if SIZE < 16 +#ifdef HAS_ASM_LSE +#define CAS GLUE4(cas, A, L, S) s(0), s(1), [x2] +#else +#define CAS .inst 0x08a07c41 + B + M +#endif + CAS // s(0), s(1), [x2] + ret +8: + UXT s(tmp0), s(0) +0: + LDXR s(0), [x2] + cmp s(0), s(tmp0) + bne 1f + STXR w(tmp1), s(1), [x2] + cbnz w(tmp1), 0b +1: + BARRIER + ret +#else +#if MODEL == 5 +// Drop A for _sync functions. +#define LDXP GLUE2(ld, xp) +#else +#define LDXP GLUE3(ld, A, xp) +#endif +#define STXP GLUE3(st, L, xp) +#ifdef HAS_ASM_LSE +#define CASP GLUE3(casp, A, L) x0, x1, x2, x3, [x4] +#else +#define CASP .inst 0x48207c82 + M +#endif + + CASP // x0, x1, x2, x3, [x4] + ret +8: + mov x(tmp0), x0 + mov x(tmp1), x1 +0: + LDXP x0, x1, [x4] + cmp x0, x(tmp0) + ccmp x1, x(tmp1), #0, eq + bne 1f + STXP w(tmp2), x2, x3, [x4] + cbnz w(tmp2), 0b +1: + BARRIER + ret +#endif +END_COMPILERRT_OUTLINE_FUNCTION(NAME(cas)) +#endif // L_cas + +#ifdef L_swp +#ifdef HAS_ASM_LSE +#define SWP GLUE4(swp, A, L, S) s(0), s(0), [x1] +#else +#define SWP .inst 0x38208020 + B + N +#endif +DEFINE_COMPILERRT_OUTLINE_FUNCTION_UNMANGLED(NAME(swp)) + JUMP_IF_NOT_LSE 8f + SWP // s(0), s(0), [x1] + ret +8: + mov s(tmp0), s(0) +0: + LDXR s(0), [x1] + STXR w(tmp1), s(tmp0), [x1] + cbnz w(tmp1), 0b + BARRIER + ret +END_COMPILERRT_OUTLINE_FUNCTION(NAME(swp)) +#endif // L_swp + +#if defined(L_ldadd) || defined(L_ldclr) || \ + defined(L_ldeor) || defined(L_ldset) + +#ifdef L_ldadd +#define LDNM ldadd +#define OP add +#define OPN 0x0000 +#elif defined(L_ldclr) +#define LDNM ldclr +#define OP bic +#define OPN 0x1000 +#elif defined(L_ldeor) +#define LDNM ldeor +#define OP eor +#define OPN 0x2000 +#elif defined(L_ldset) +#define LDNM ldset +#define OP orr +#define OPN 0x3000 +#else +#error +#endif + +#ifdef HAS_ASM_LSE +#define LDOP GLUE4(LDNM, A, L, S) s(0), s(0), [x1] +#else +#define LDOP .inst 0x38200020 + OPN + B + N +#endif + +DEFINE_COMPILERRT_OUTLINE_FUNCTION_UNMANGLED(NAME(LDNM)) + JUMP_IF_NOT_LSE 8f + LDOP // s(0), s(0), [x1] + ret +8: + mov s(tmp0), s(0) +0: + LDXR s(0), [x1] + OP s(tmp1), s(0), s(tmp0) + STXR w(tmp2), s(tmp1), [x1] + cbnz w(tmp2), 0b + BARRIER + ret +END_COMPILERRT_OUTLINE_FUNCTION(NAME(LDNM)) +#endif // L_ldadd L_ldclr L_ldeor L_ldset + +NO_EXEC_STACK_DIRECTIVE + +// GNU property note for BTI, PAC, and GCS +GNU_PROPERTY_BTI_PAC_GCS + +#endif // defined(__aarch64__) || defined(__arm64ec__) diff --git a/ci/licenses/rust-compiler-originals/libm-LICENSE.txt b/ci/licenses/rust-compiler-originals/libm-LICENSE.txt new file mode 100644 index 0000000..2f8e41f --- /dev/null +++ b/ci/licenses/rust-compiler-originals/libm-LICENSE.txt @@ -0,0 +1,258 @@ +rust-lang/libm as a whole is available for use under the MIT license: + +------------------------------------------------------------------------------ +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ + +As a contributor, you agree that your code can be used under either the MIT +license or the Apache-2.0 license: + +------------------------------------------------------------------------------ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +------------------------------------------------------------------------------ + +This Rust library contains the following copyrights: + + Copyright (c) 2018 Jorge Aparicio + +Portions of this software are derived from third-party works licensed under +terms compatible with the above MIT license: + +* musl libc https://www.musl-libc.org/. This library contains the following + copyright: + + Copyright © 2005-2020 Rich Felker, et al. + +* The CORE-MATH project https://core-math.gitlabpages.inria.fr/. CORE-MATH + routines are available under the MIT license on a per-file basis. + +The musl libc COPYRIGHT file also includes the following notice relevant to +math portions of the library: + +------------------------------------------------------------------------------ +Much of the math library code (src/math/* and src/complex/*) is +Copyright © 1993,2004 Sun Microsystems or +Copyright © 2003-2011 David Schultz or +Copyright © 2003-2009 Steven G. Kargl or +Copyright © 2003-2009 Bruce D. Evans or +Copyright © 2008 Stephen L. Moshier or +Copyright © 2017-2018 Arm Limited +and labelled as such in comments in the individual source files. All +have been licensed under extremely permissive terms. +------------------------------------------------------------------------------ + +Copyright notices are retained in src/* files where relevant. diff --git a/ci/licenses/rust-compiler-originals/libm-generic-trunc.rs.txt b/ci/licenses/rust-compiler-originals/libm-generic-trunc.rs.txt new file mode 100644 index 0000000..0b252fa --- /dev/null +++ b/ci/licenses/rust-compiler-originals/libm-generic-trunc.rs.txt @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: MIT + * origin: musl src/math/trunc.c */ + +use crate::support::{Float, FpResult, Int, IntTy, MinInt, Status}; + +#[inline] +pub fn trunc_status(x: F) -> FpResult { + let xi: F::Int = x.to_bits(); + let e: i32 = x.exp_unbiased(); + + // The represented value has no fractional part, so no truncation is needed + if e >= F::SIG_BITS as i32 { + return FpResult::ok(x); + } + + let clear_mask = if e < 0 { + // If the exponent is negative, the result will be zero so we clear everything + // except the sign. + !F::SIGN_MASK + } else { + // Otherwise, we keep `e` fractional bits and clear the rest. + F::SIG_MASK >> e.unsigned() + }; + + let cleared = xi & clear_mask; + let status = if cleared == IntTy::::ZERO { + // If the to-be-zeroed portion is already zero, we have an exact result. + Status::OK + } else { + // Otherwise the result is inexact and we will truncate, so indicate `FE_INEXACT`. + Status::INEXACT + }; + + // Now zero the bits we need to truncate and return. + FpResult::new(F::from_bits(xi ^ cleared), status) +} diff --git a/ci/licenses/rust-compiler-originals/libm-trunc.rs.txt b/ci/licenses/rust-compiler-originals/libm-trunc.rs.txt new file mode 100644 index 0000000..9eb902e --- /dev/null +++ b/ci/licenses/rust-compiler-originals/libm-trunc.rs.txt @@ -0,0 +1,147 @@ +use super::generic; + +/// Rounds the number toward 0 to the closest integral value (f16). +/// +/// This effectively removes the decimal part of the number, leaving the integral part. +#[cfg(f16_enabled)] +#[cfg_attr(assert_no_panic, no_panic::no_panic)] +pub fn truncf16(x: f16) -> f16 { + generic::trunc_status(x).val +} + +/// Rounds the number toward 0 to the closest integral value (f32). +/// +/// This effectively removes the decimal part of the number, leaving the integral part. +#[cfg_attr(assert_no_panic, no_panic::no_panic)] +pub fn truncf(x: f32) -> f32 { + select_implementation! { + name: truncf, + use_arch: all(target_arch = "wasm32", intrinsics_enabled), + args: x, + } + + generic::trunc_status(x).val +} + +/// Rounds the number toward 0 to the closest integral value (f64). +/// +/// This effectively removes the decimal part of the number, leaving the integral part. +#[cfg_attr(assert_no_panic, no_panic::no_panic)] +pub fn trunc(x: f64) -> f64 { + select_implementation! { + name: trunc, + use_arch: all(target_arch = "wasm32", intrinsics_enabled), + args: x, + } + + generic::trunc_status(x).val +} + +/// Rounds the number toward 0 to the closest integral value (f128). +/// +/// This effectively removes the decimal part of the number, leaving the integral part. +#[cfg(f128_enabled)] +#[cfg_attr(assert_no_panic, no_panic::no_panic)] +pub fn truncf128(x: f128) -> f128 { + generic::trunc_status(x).val +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::support::{Float, FpResult, Hex, Status}; + + macro_rules! cases { + ($f:ty) => { + [ + // roundtrip + (0.0, 0.0, Status::OK), + (-0.0, -0.0, Status::OK), + (1.0, 1.0, Status::OK), + (-1.0, -1.0, Status::OK), + (<$f>::INFINITY, <$f>::INFINITY, Status::OK), + (<$f>::NEG_INFINITY, <$f>::NEG_INFINITY, Status::OK), + // with rounding + (0.1, 0.0, Status::INEXACT), + (-0.1, -0.0, Status::INEXACT), + (0.5, 0.0, Status::INEXACT), + (-0.5, -0.0, Status::INEXACT), + (0.9, 0.0, Status::INEXACT), + (-0.9, -0.0, Status::INEXACT), + (1.1, 1.0, Status::INEXACT), + (-1.1, -1.0, Status::INEXACT), + (1.5, 1.0, Status::INEXACT), + (-1.5, -1.0, Status::INEXACT), + (1.9, 1.0, Status::INEXACT), + (-1.9, -1.0, Status::INEXACT), + ] + }; + } + + #[track_caller] + fn check(f: fn(F) -> F, cases: &[(F, F, Status)]) { + for &(x, exp_res, exp_stat) in cases { + let FpResult { val, status } = generic::trunc_status(x); + assert_biteq!(val, exp_res, "generic::trunc_status({x:?}) ({})", Hex(x)); + assert_eq!( + status, + exp_stat, + "{x:?} {} -> {exp_res:?} {}", + Hex(x), + Hex(exp_res) + ); + let val = f(x); + assert_biteq!(val, exp_res, "trunc({x:?}) ({})", Hex(x)); + } + } + + #[test] + #[cfg(f16_enabled)] + fn check_f16() { + check::(truncf16, &cases!(f16)); + check::( + truncf16, + &[ + (hf16!("0x1p10"), hf16!("0x1p10"), Status::OK), + (hf16!("-0x1p10"), hf16!("-0x1p10"), Status::OK), + ], + ); + } + + #[test] + fn check_f32() { + check::(truncf, &cases!(f32)); + check::( + truncf, + &[ + (hf32!("0x1p23"), hf32!("0x1p23"), Status::OK), + (hf32!("-0x1p23"), hf32!("-0x1p23"), Status::OK), + ], + ); + } + + #[test] + fn check_f64() { + check::(trunc, &cases!(f64)); + check::( + trunc, + &[ + (hf64!("0x1p52"), hf64!("0x1p52"), Status::OK), + (hf64!("-0x1p52"), hf64!("-0x1p52"), Status::OK), + ], + ); + } + + #[test] + #[cfg(f128_enabled)] + fn check_f128() { + check::(truncf128, &cases!(f128)); + check::( + truncf128, + &[ + (hf128!("0x1p112"), hf128!("0x1p112"), Status::OK), + (hf128!("-0x1p112"), hf128!("-0x1p112"), Status::OK), + ], + ); + } +} diff --git a/ci/rust-compiler-notices.json b/ci/rust-compiler-notices.json new file mode 100644 index 0000000..7cdc2d0 --- /dev/null +++ b/ci/rust-compiler-notices.json @@ -0,0 +1,163 @@ +{ + "coverage": "Complete original Rust compiler-builtins, LLVM compiler-rt and libm grants and a conservative source witness collection for the pinned compiler. Not a retained-code inventory, all-target input proof or change to the first-party MIT OR Apache-2.0 license.", + "llvm_revision": "1b9c0d5ff9bbe7634aead059efe6b11a7eeba145", + "materials": [ + { + "bytes": 15078, + "git_blob": "00ae6140bd54e1b538d833f60438e11ed8be12cf", + "legal_document": true, + "path": "ci/licenses/rust-compiler-originals/compiler-builtins-LICENSE.txt", + "repository": "rust-lang/rust", + "revision": "58cf8f9c35b355199727627d07ae34a4e2846d67", + "role": "compiler-builtins-LICENSE.txt", + "sha256": "ab6eec6caf0fa5775e411c7a8bc6a45c4ef2956b0980b157ab74fc5cd62a928b", + "upstream_path": "library/compiler-builtins/LICENSE.txt" + }, + { + "bytes": 2005, + "git_blob": "b2f1443f2eb09d297b62c7b44fce9e31d71b10a2", + "legal_document": false, + "path": "ci/licenses/rust-compiler-originals/compiler-builtins-Cargo.toml.txt", + "repository": "rust-lang/rust", + "revision": "58cf8f9c35b355199727627d07ae34a4e2846d67", + "role": "compiler-builtins-Cargo.toml.txt", + "sha256": "d89e31fed70f2cad9c6e91b787bf4c0fb680c631196b2a7215c81745aaf7fb7e", + "upstream_path": "library/compiler-builtins/compiler-builtins/Cargo.toml" + }, + { + "bytes": 27313, + "git_blob": "e86c784b5c6317a53e9ff89104b15e60ba7ac4bf", + "legal_document": false, + "path": "ci/licenses/rust-compiler-originals/compiler-builtins-build.rs.txt", + "repository": "rust-lang/rust", + "revision": "58cf8f9c35b355199727627d07ae34a4e2846d67", + "role": "compiler-builtins-build.rs.txt", + "sha256": "dd1d07d0852fd6a654c69d6dafd8ae66744853cb5678610008dbf6ae6baec11e", + "upstream_path": "library/compiler-builtins/compiler-builtins/build.rs" + }, + { + "bytes": 463, + "git_blob": "2c12cb7f3095b2ace3938a666ebd1b607b8a269c", + "legal_document": false, + "path": "ci/licenses/rust-compiler-originals/compiler-builtins-aarch64.rs.txt", + "repository": "rust-lang/rust", + "revision": "58cf8f9c35b355199727627d07ae34a4e2846d67", + "role": "compiler-builtins-aarch64.rs.txt", + "sha256": "5acb367779a8e6e6e06e188e715d6d2ff76b9c3208acd899e139957bd1d3c877", + "upstream_path": "library/compiler-builtins/compiler-builtins/src/aarch64.rs" + }, + { + "bytes": 16463, + "git_blob": "367768ecb60d65069679b3c84a9ef932c5ab6962", + "legal_document": false, + "path": "ci/licenses/rust-compiler-originals/compiler-builtins-aarch64-outline-atomics.rs.txt", + "repository": "rust-lang/rust", + "revision": "58cf8f9c35b355199727627d07ae34a4e2846d67", + "role": "compiler-builtins-aarch64-outline-atomics.rs.txt", + "sha256": "d2e6ed0066ac36db55dfaa245bdc814353456dda92437aba539cc828d2011ae9", + "upstream_path": "library/compiler-builtins/compiler-builtins/src/aarch64_outline_atomics.rs" + }, + { + "bytes": 5851, + "git_blob": "a444d82892c36f8c1e3ba5f1977638d56fb929f8", + "legal_document": false, + "path": "ci/licenses/rust-compiler-originals/compiler-rt-aarch64-lse.S.txt", + "repository": "rust-lang/llvm-project", + "revision": "1b9c0d5ff9bbe7634aead059efe6b11a7eeba145", + "role": "compiler-rt-aarch64-lse.S.txt", + "sha256": "dcd05cb984ef8d777642eb960e3ef61b1140f268c3e4744f8f738aa46fd8ed66", + "upstream_path": "compiler-rt/lib/builtins/aarch64/lse.S" + }, + { + "bytes": 3026, + "git_blob": "347bb4e121c97c98aa6e7440b1c952978bfedc9b", + "legal_document": false, + "path": "ci/licenses/rust-compiler-originals/compiler-rt-aarch64-cpu-model.c.txt", + "repository": "rust-lang/llvm-project", + "revision": "1b9c0d5ff9bbe7634aead059efe6b11a7eeba145", + "role": "compiler-rt-aarch64-cpu-model.c.txt", + "sha256": "405b2ad2520db341af1338be2e58cce9d28a9f63006486d1c9572f4fe10ae032", + "upstream_path": "compiler-rt/lib/builtins/cpu_model/aarch64.c" + }, + { + "bytes": 16708, + "git_blob": "5a79a1b9d5cb0af9a779751f07033e44ca3b14d8", + "legal_document": true, + "path": "ci/licenses/rust-compiler-originals/compiler-rt-LICENSE.TXT", + "repository": "rust-lang/llvm-project", + "revision": "1b9c0d5ff9bbe7634aead059efe6b11a7eeba145", + "role": "compiler-rt-LICENSE.TXT", + "sha256": "1a8f1058753f1ba890de984e48f0242a3a5c29a6a8f2ed9fd813f36985387e8d", + "upstream_path": "compiler-rt/LICENSE.TXT" + }, + { + "bytes": 1049, + "git_blob": "6964eba020b8aa7a08629b249e3b9aea3ce3e448", + "legal_document": true, + "path": "ci/licenses/rust-compiler-originals/compiler-rt-CREDITS.TXT", + "repository": "rust-lang/llvm-project", + "revision": "1b9c0d5ff9bbe7634aead059efe6b11a7eeba145", + "role": "compiler-rt-CREDITS.TXT", + "sha256": "a9901f47a089da41e4690682d00ce4cedaa2baf41fedbe79beee366d43ac2461", + "upstream_path": "compiler-rt/CREDITS.TXT" + }, + { + "bytes": 7270, + "git_blob": "3dfa3863bb77007fbec01b7c931bf1c349953d11", + "legal_document": false, + "path": "ci/licenses/rust-compiler-originals/compiler-builtins-math-mod.rs.txt", + "repository": "rust-lang/rust", + "revision": "58cf8f9c35b355199727627d07ae34a4e2846d67", + "role": "compiler-builtins-math-mod.rs.txt", + "sha256": "a3ff615e04d9ae9a0bb36cfbfd9a0aaf183ab6e32ddfe836cdf4f243f97a401e", + "upstream_path": "library/compiler-builtins/compiler-builtins/src/math/mod.rs" + }, + { + "bytes": 4407, + "git_blob": "9eb902ea578f87588d8236619a67ab7db72c8ba8", + "legal_document": false, + "path": "ci/licenses/rust-compiler-originals/libm-trunc.rs.txt", + "repository": "rust-lang/rust", + "revision": "58cf8f9c35b355199727627d07ae34a4e2846d67", + "role": "libm-trunc.rs.txt", + "sha256": "ef59952a1a80ec93be27eb78c977189084ff7fcae0e8f782d60b5947e1de141c", + "upstream_path": "library/compiler-builtins/libm/src/math/trunc.rs" + }, + { + "bytes": 14088, + "git_blob": "2f8e41f147472510b05e0adeadf350df6f5a6b96", + "legal_document": true, + "path": "ci/licenses/rust-compiler-originals/libm-LICENSE.txt", + "repository": "rust-lang/rust", + "revision": "58cf8f9c35b355199727627d07ae34a4e2846d67", + "role": "libm-LICENSE.txt", + "sha256": "3823dda7cf046602f4b4e77ec8e227863dc4736037cc85bb33d9f19febe16bb7", + "upstream_path": "library/compiler-builtins/libm/LICENSE.txt" + }, + { + "bytes": 1152, + "git_blob": "0b252fab94ae1d54aac4df8d5ab6f7a2e919bd32", + "legal_document": false, + "path": "ci/licenses/rust-compiler-originals/libm-generic-trunc.rs.txt", + "repository": "rust-lang/rust", + "revision": "58cf8f9c35b355199727627d07ae34a4e2846d67", + "role": "libm-generic-trunc.rs.txt", + "sha256": "3a3ca091cd1d750276601dc2dfeb2a8ff058636ea2b66a748c55a1804bc8f00a", + "upstream_path": "library/compiler-builtins/libm/src/math/generic/trunc.rs" + }, + { + "bytes": 16862, + "git_blob": "6193aa416e22249ed25e856aa03dda2f5a585b62", + "legal_document": false, + "path": "ci/licenses/rust-compiler-originals/compiler-builtins-float-conv.rs.txt", + "repository": "rust-lang/rust", + "revision": "58cf8f9c35b355199727627d07ae34a4e2846d67", + "role": "compiler-builtins-float-conv.rs.txt", + "sha256": "740286ec154ddd78a9a8e5e6b8fad2c564585d1a76051cd6ee429c313d660070", + "upstream_path": "library/compiler-builtins/compiler-builtins/src/float/conv.rs" + } + ], + "rust_revision": "58cf8f9c35b355199727627d07ae34a4e2846d67", + "rust_version": "1.99.1", + "schema_version": 1 +} diff --git a/scripts/prepare_native_notices.py b/scripts/prepare_native_notices.py index 80f9cfb..be3465a 100644 --- a/scripts/prepare_native_notices.py +++ b/scripts/prepare_native_notices.py @@ -8,8 +8,9 @@ import sys from pathlib import Path -from cargo_notice_bundle import _build +from cargo_notice_bundle import MAX_BUNDLE_BYTES, _build from collect_cargo_notices import _collect +from rust_compiler_notices import collect as compiler_notices def _prepare(evidence: Path) -> None: @@ -63,7 +64,11 @@ def _prepare(evidence: Path) -> None: (evidence / "notices.json").write_text( json.dumps(notices, ensure_ascii=False, indent=2) + "\n", encoding="utf-8" ) - data = _build(evidence) + bundle = json.loads(_build(evidence)) + bundle["rust_compiler_builtins"] = compiler_notices(root, bundle["rustc"]) + data = (json.dumps(bundle, ensure_ascii=False, sort_keys=True, indent=2) + "\n").encode() + if len(data) > MAX_BUNDLE_BYTES: + raise ValueError("native notice bundle size limit exceeded") if lock.read_bytes() != original_lock: raise ValueError("Cargo.lock changed while generating notices") (evidence / "NOTICE.json").write_bytes(data) diff --git a/scripts/rust_compiler_notices.py b/scripts/rust_compiler_notices.py new file mode 100644 index 0000000..36bdc1b --- /dev/null +++ b/scripts/rust_compiler_notices.py @@ -0,0 +1,65 @@ +"""Сохранить полные compiler grants для exact Rust source revision.""" + +from __future__ import annotations + +import hashlib +import json +from pathlib import Path, PurePosixPath + +PROFILE_SHA256 = "39cbf18420d5e0c0a35fb2d5e5fbed4db755488ea0ca11414944ead9fbabab92" +RUST_REVISION = "58cf8f9c35b355199727627d07ae34a4e2846d67" + + +def _read(path: Path, limit: int) -> bytes: + if path.is_symlink() or not path.is_file(): + raise ValueError("missing or symlinked compiler notice input") + with path.open("rb") as source: + raw = source.read(limit + 1) + if len(raw) > limit: + raise ValueError("compiler notice input exceeds bound") + return raw + + +def collect(root: Path, rustc: str) -> dict: + """Сверить compiler identity и сохранить original legal/source bytes.""" + if ( + not rustc.startswith("rustc 1.99.1 ") + or "\nrelease: 1.99.1\n" not in rustc + or "\ncommit-hash: " + RUST_REVISION + "\n" not in rustc + ): + raise ValueError("unreviewed Rust compiler source identity") + root = root.resolve() + raw = _read(root / "ci/rust-compiler-notices.json", 1024 * 1024) + if hashlib.sha256(raw).hexdigest() != PROFILE_SHA256: + raise ValueError("compiler notice profile differs from review") + profile = json.loads(raw) + materials = [] + for item in profile["materials"]: + relative = PurePosixPath(item["path"]) + path = root / item["path"] + if ( + relative.is_absolute() + or ".." in relative.parts + or "\\" in item["path"] + or not path.resolve().is_relative_to(root) + ): + raise ValueError("unsafe compiler notice source path") + original = _read(path, 4 * 1024 * 1024) + blob = hashlib.sha1(b"blob " + str(len(original)).encode() + b"\0" + original).hexdigest() + if ( + len(original) != item["bytes"] + or hashlib.sha256(original).hexdigest() != item["sha256"] + or blob != item["git_blob"] + ): + raise ValueError("compiler notice original identity differs") + materials.append({**item, "text": original.decode("utf-8")}) + return { + "schema_version": 1, + "profile_sha256": PROFILE_SHA256, + "rust_revision": profile["rust_revision"], + "llvm_revision": profile["llvm_revision"], + "compiler_builtins_license": "MIT AND Apache-2.0 WITH LLVM-exception", + "libm_license": "MIT", + "coverage": profile["coverage"], + "materials": materials, + } diff --git a/tests/ci/test_rust_compiler_notices.py b/tests/ci/test_rust_compiler_notices.py new file mode 100644 index 0000000..8c0a702 --- /dev/null +++ b/tests/ci/test_rust_compiler_notices.py @@ -0,0 +1,121 @@ +"""Проверить exact-source compiler grants без запуска toolchain.""" + +from __future__ import annotations + +import importlib.util +import json +from pathlib import Path + +import pytest + +ROOT = Path(__file__).resolve().parents[2] +spec = importlib.util.spec_from_file_location( + "rust_compiler_notices", ROOT / "scripts/rust_compiler_notices.py" +) +assert spec is not None +assert spec.loader is not None +module = importlib.util.module_from_spec(spec) +spec.loader.exec_module(module) +RUSTC = ( + "rustc 1.99.1 (fixture)\nhost: x86_64-unknown-linux-gnu\nrelease: 1.99.1\n" + f"commit-hash: {module.RUST_REVISION}\nLLVM version: 23.1.1\n" +) + + +def test_complete_original_source_and_legal_materials() -> None: + record = module.collect(ROOT, RUSTC) + profile = json.loads((ROOT / "ci/rust-compiler-notices.json").read_text(encoding="utf-8")) + assert record["compiler_builtins_license"] == "MIT AND Apache-2.0 WITH LLVM-exception" + assert record["libm_license"] == "MIT" + assert record["rust_revision"] == module.RUST_REVISION + assert len(record["materials"]) == 14 + assert sum(item["legal_document"] for item in record["materials"]) == 4 + assert len(profile["materials"]) == 14 + for expected, actual in zip(profile["materials"], record["materials"]): + original = (ROOT / expected["path"]).read_bytes().decode("utf-8") + assert actual == {**expected, "text": original} + + +def test_libm_and_legacy_attribution_is_preserved() -> None: + materials = module.collect(ROOT, RUSTC)["materials"] + text = "\n".join(item["text"] for item in materials if item["legal_document"]) + assert "Copyright (c) 2018 Jorge Aparicio" in text + assert "Copyright © 2005-2020 Rich Felker, et al." in text + assert "Copyright (c) 2009-2016" in text + assert "LLVM Exceptions to the Apache 2.0 License" in text + + +@pytest.mark.parametrize( + "rustc", + [ + "", + "rustc 1.99.1", + RUSTC.replace("1.99.1", "1.88.0"), + RUSTC.replace(module.RUST_REVISION, "0" * 40), + ], +) +def test_unknown_compiler_is_rejected(rustc: str) -> None: + with pytest.raises(ValueError, match="compiler source identity"): + module.collect(ROOT, rustc) + + +def test_changed_profile_is_rejected(tmp_path: Path) -> None: + (tmp_path / "ci").mkdir() + (tmp_path / "ci/rust-compiler-notices.json").write_text("{}", encoding="utf-8") + with pytest.raises(ValueError, match="profile differs from review"): + module.collect(tmp_path, RUSTC) + + +def test_missing_profile_is_rejected(tmp_path: Path) -> None: + with pytest.raises(ValueError, match="missing or symlinked"): + module.collect(tmp_path, RUSTC) + + +def test_changed_and_missing_original_are_rejected(tmp_path: Path) -> None: + (tmp_path / "ci").mkdir() + profile = (ROOT / "ci/rust-compiler-notices.json").read_bytes() + (tmp_path / "ci/rust-compiler-notices.json").write_bytes(profile) + item = json.loads(profile)["materials"][0] + original = tmp_path / item["path"] + original.parent.mkdir(parents=True) + original.write_bytes(b"changed") + with pytest.raises(ValueError, match="original identity differs"): + module.collect(tmp_path, RUSTC) + original.unlink() + with pytest.raises(ValueError, match="missing or symlinked"): + module.collect(tmp_path, RUSTC) + + +def test_collection_is_read_only_and_deterministic() -> None: + before = (ROOT / "ci/rust-compiler-notices.json").read_bytes() + assert module.collect(ROOT, RUSTC) == module.collect(ROOT, RUSTC) + assert (ROOT / "ci/rust-compiler-notices.json").read_bytes() == before + + +def test_oversized_input_is_rejected(tmp_path: Path) -> None: + source = tmp_path / "large.txt" + source.write_bytes(b"12345") + with pytest.raises(ValueError, match="exceeds bound"): + module._read(source, 4) + + +def test_actual_container_binding_preserves_compiler_grants() -> None: + binding_spec = importlib.util.spec_from_file_location( + "bind_native_compiler", ROOT / "scripts/bind_native_compiler.py" + ) + assert binding_spec is not None + assert binding_spec.loader is not None + binding = importlib.util.module_from_spec(binding_spec) + binding_spec.loader.exec_module(binding) + collection = module.collect(ROOT, RUSTC) + bundle = { + "schema_version": 1, + "rustc": RUSTC, + "rust_standard_library": {"text": "copyright", "sha256": "fixture"}, + "rust_compiler_builtins": collection, + } + actual = RUSTC.replace("x86_64", "aarch64") + result = json.loads(binding._bind(bundle, actual)) + assert result["rustc"] == actual + assert result["rust_compiler_builtins"] == collection + assert result["rust_standard_library"]["report_compiler"] == RUSTC From b96aac53f2a5bff29ad5b02046df03df674c1e07 Mon Sep 17 00:00:00 2001 From: Arch-AI-tech Date: Sun, 1 Nov 2026 08:00:20 +0100 Subject: [PATCH 45/54] fix(ci): use supported Ubuntu for Python 3.9 The ubuntu-latest rollout now selects Ubuntu 26.04, which has no setup-python 3.9 artifact. Use Ubuntu 24.04 for the two Python 3.9 benchmarks and only the affected Python 3.9 Linux matrix entries. Keep all interpreter versions, matrix cardinalities, benchmarks, assertions and other runner choices unchanged. Do not spoof the platform, skip jobs or upgrade project dependencies. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/CI.yml | 12 ++++++++++++ .github/workflows/bench-compare-edit.yml | 2 +- .github/workflows/bench-edit.yml | 2 +- .github/workflows/licenses.yml | 6 ++++++ 4 files changed, 20 insertions(+), 2 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index a365066..ea0f06b 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -130,6 +130,12 @@ jobs: matrix: os: [ubuntu-latest, macos-latest, windows-latest] python: ["3.9", "3.11", "3.13"] + exclude: + - os: ubuntu-latest + python: "3.9" + include: + - os: ubuntu-24.04 + python: "3.9" steps: - uses: actions/checkout@v6 with: @@ -225,6 +231,12 @@ jobs: matrix: os: [ubuntu-latest, windows-latest] python: ["3.9", "3.13"] + exclude: + - os: ubuntu-latest + python: "3.9" + include: + - os: ubuntu-24.04 + python: "3.9" steps: - uses: actions/checkout@v6 with: diff --git a/.github/workflows/bench-compare-edit.yml b/.github/workflows/bench-compare-edit.yml index c30e8fc..c9f6a05 100644 --- a/.github/workflows/bench-compare-edit.yml +++ b/.github/workflows/bench-compare-edit.yml @@ -10,7 +10,7 @@ permissions: jobs: compare: name: Python 3.9 / Ubuntu - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Checkout baseline uses: actions/checkout@v6 diff --git a/.github/workflows/bench-edit.yml b/.github/workflows/bench-edit.yml index 0d7fd07..c821add 100644 --- a/.github/workflows/bench-edit.yml +++ b/.github/workflows/bench-edit.yml @@ -11,7 +11,7 @@ permissions: jobs: benchmark: name: Python 3.9 / Ubuntu - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 env: CARGO_TERM_COLOR: always steps: diff --git a/.github/workflows/licenses.yml b/.github/workflows/licenses.yml index aab0fe8..b5a74e5 100644 --- a/.github/workflows/licenses.yml +++ b/.github/workflows/licenses.yml @@ -125,6 +125,12 @@ jobs: matrix: os: [ubuntu-latest, macos-latest, windows-latest] python: ["3.9", "3.11", "3.13"] + exclude: + - os: ubuntu-latest + python: "3.9" + include: + - os: ubuntu-24.04 + python: "3.9" steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 From 555a6e63ec3da57e7a57a86049c28021f0e85700 Mon Sep 17 00:00:00 2001 From: Arch-AI-tech Date: Sun, 1 Nov 2026 08:00:23 +0100 Subject: [PATCH 46/54] fix(licenses): verify exact nodeenv 1.12.0 grant Verify the immutable PyPI wheel and sdist as data. Both carry the complete original BSD grant matching the installed license hash 606faf42d48b54d539dae99db6fecd48544535587e27275b319a3f36113cb6fa. Retain the full original text, archive identities and provenance. Add only the exact nodeenv 1.12.0 case. Keep unknown versions, modified grants and stronger metadata expressions rejected, with positive and negative regression checks. Do not alter dependency versions or approve BSD metadata generically. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/licenses.yml | 5 +++++ ci/licenses/nodeenv-1.12.0-review.json | 30 ++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) create mode 100644 ci/licenses/nodeenv-1.12.0-review.json diff --git a/.github/workflows/licenses.yml b/.github/workflows/licenses.yml index b5a74e5..e11e640 100644 --- a/.github/workflows/licenses.yml +++ b/.github/workflows/licenses.yml @@ -187,6 +187,7 @@ jobs: reviewed = { ('nodeenv', '1.11.0'): ('bsd', '606faf42d48b54d539dae99db6fecd48544535587e27275b319a3f36113cb6fa'), ('nodeenv', '1.11.1'): ('bsd', '606faf42d48b54d539dae99db6fecd48544535587e27275b319a3f36113cb6fa'), + ('nodeenv', '1.12.0'): ('bsd', '606faf42d48b54d539dae99db6fecd48544535587e27275b319a3f36113cb6fa'), ('colorama', '0.4.6'): ('', 'cac35c02686e5d04a5a7140bfb3b36e73aed496656e891102e428886d7930318'), ('exceptiongroup', '1.3.1'): ('', '6e5070d765031e0ad403a1cbf90ae6d19a0228f802e320b7acff460aa72ed47c'), ('pathspec', '1.1.1'): ('', 'fab3dd6bdab226f1c08630b1dd917e11fcb4ec5e1e020e2c16f83a0a13863e85'), @@ -205,6 +206,10 @@ jobs: expected = reviewed[('nodeenv', '1.11.0')][1] assert check('nodeenv', '1.11.0', None, 'BSD', [expected]) assert check('nodeenv', '1.11.1', None, 'BSD', [expected]) + assert check('nodeenv', '1.12.0', None, 'BSD', [expected]) + assert not check('nodeenv', '1.12.0', None, 'BSD', ['wrong']) + assert not check('nodeenv', '1.12.0', 'MIT AND GPL-3.0-only', 'BSD', [expected]) + assert not check('nodeenv', '1.12.1', None, 'BSD', [expected]) assert not check('nodeenv', '1.11.2', None, 'BSD', [expected]) assert not check('nodeenv', '1.11.1', None, 'BSD', ['wrong']) assert not check('nodeenv', '1.11.1', 'MIT AND GPL-3.0-only', 'BSD', [expected]) diff --git a/ci/licenses/nodeenv-1.12.0-review.json b/ci/licenses/nodeenv-1.12.0-review.json new file mode 100644 index 0000000..228a777 --- /dev/null +++ b/ci/licenses/nodeenv-1.12.0-review.json @@ -0,0 +1,30 @@ +{ + "artifacts": [ + { + "bytes": 39442, + "filename": "nodeenv-1.12.0-py3-none-any.whl", + "license_path": "nodeenv-1.12.0.dist-info/licenses/LICENSE", + "packagetype": "bdist_wheel", + "requires_python": ">=3.10", + "sha256": "80644d65c10cc2be12e3e4b54b3367ff288c7ad24ae75c2dcc65454fc7449d4d", + "url": "https://files.pythonhosted.org/packages/2d/6a/8a9ac7d5e2f7cf53c459bfef90ebc17a2f8cd54b665af96799d4c534840e/nodeenv-1.12.0-py3-none-any.whl" + }, + { + "bytes": 131292, + "filename": "nodeenv-1.12.0.tar.gz", + "license_path": "nodeenv-1.12.0/LICENSE", + "packagetype": "sdist", + "requires_python": ">=3.10", + "sha256": "4ea7f88339310afebc3253f2c7a91b94bacf97d73a36a5c108962abdfaca501e", + "url": "https://files.pythonhosted.org/packages/e6/99/d27cfc9518f49e4cbc25c683ec7cf8704a9d31311341c291c0e78ddf791b/nodeenv-1.12.0.tar.gz" + } + ], + "license_bytes": 1532, + "license_expression_metadata": null, + "license_metadata": "BSD", + "license_sha256": "606faf42d48b54d539dae99db6fecd48544535587e27275b319a3f36113cb6fa", + "license_text": "Copyright (c) 2011, Eugene Kalinin.\n\nSome rights reserved.\n\nRedistribution and use in source and binary forms of the software as well\nas documentation, with or without modification, are permitted provided\nthat the following conditions are met:\n\n* Redistributions of source code must retain the above copyright\n notice, this list of conditions and the following disclaimer.\n\n* Redistributions in binary form must reproduce the above\n copyright notice, this list of conditions and the following\n disclaimer in the documentation and/or other materials provided\n with the distribution.\n\n* The names of the contributors may not be used to endorse or\n promote products derived from this software without specific\n prior written permission.\n\nTHIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY THE COPYRIGHT HOLDERS AND\nCONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT\nNOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER\nOR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\nEXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\nPROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\nPROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\nLIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\nNEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\nSOFTWARE AND DOCUMENTATION, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH\nDAMAGE.\n", + "name": "nodeenv", + "scope": "Exact PyPI wheel/sdist metadata and complete original LICENSE match the installed CI evidence. This review does not install or execute nodeenv, alter dependency versions or approve unknown versions or modified grants.", + "version": "1.12.0" +} \ No newline at end of file From 0465596ffb8e2445b30253d8f8ed1357d2fa83de Mon Sep 17 00:00:00 2001 From: Arch-AI-tech Date: Sun, 1 Nov 2026 20:06:08 +0100 Subject: [PATCH 47/54] ci: probe genuine Windows GNU PyO3 compatibility Build original Python 0.8.1 source and its reviewed registry-core lock for actual x64 and ARM64 gnullvm targets on matching native runners. Use the exact reviewed LLVM MinGW SDK and real installed CPython stable-ABI import libraries with recorded bytes and actual COFF CPU. Do not spoof targets, patch dependencies, fabricate DLL/import libraries or change any project or dependency version. Inspect actual built wheel tags, PyInit export, Python/system imports, linker input fingerprints and the installed extension identity. Execute unchanged original Python tests and native conformance against the installed local wheel without Rust/SDK paths. Keep original-source and locked-dependency assertions strict. This is a compatibility investigation only: upload inert JSON/text/map/ lock/XML diagnostics, never the wheel, extension or linker reproduction. Do not replace the production native producer or claim full runtime notice/grant acceptance, publication or historical asset repair. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/windows-gnu-pyo3-probe.yml | 264 +++++++++++++++ ci/windows-gnu-probe.lock | 324 +++++++++++++++++++ 2 files changed, 588 insertions(+) create mode 100644 .github/workflows/windows-gnu-pyo3-probe.yml create mode 100644 ci/windows-gnu-probe.lock diff --git a/.github/workflows/windows-gnu-pyo3-probe.yml b/.github/workflows/windows-gnu-pyo3-probe.yml new file mode 100644 index 0000000..657f778 --- /dev/null +++ b/.github/workflows/windows-gnu-pyo3-probe.yml @@ -0,0 +1,264 @@ +name: Windows GNU PyO3 compatibility probe + +on: + pull_request: + workflow_dispatch: + +permissions: + contents: read + +env: + SOURCE_REF: 566bbfce6c6b48387d7a0a089939a26744013dea + +jobs: + compatibility: + name: Genuine PyO3 GNU / ${{ matrix.target }} + runs-on: ${{ matrix.os }} + timeout-minutes: 45 + strategy: + fail-fast: false + matrix: + include: + - os: windows-latest + target: x86_64-pc-windows-gnullvm + python-arch: x64 + - os: windows-11-arm + target: aarch64-pc-windows-gnullvm + python-arch: arm64 + env: + GNU_TARGET: ${{ matrix.target }} + ACTUAL_RUNNER_ARCH: ${{ runner.arch }} + steps: + - uses: actions/checkout@v4 + - name: Preserve the reviewed original Python lock + shell: python + run: | + import hashlib + import os + from pathlib import Path + raw = Path('ci/windows-gnu-probe.lock').read_bytes() + if hashlib.sha256(raw).hexdigest() != '657c314ced9fea78d2868a6ba42d410bab8db587a80c130ff7866e00bb9a57b7': + raise SystemExit('reviewed original Python lock differs') + (Path(os.environ['RUNNER_TEMP']) / 'python-gnu-probe.lock').write_bytes(raw) + - uses: actions/checkout@v4 + with: + ref: ${{ env.SOURCE_REF }} + submodules: recursive + persist-credentials: false + - uses: actions/setup-python@v5 + with: + python-version: '3.13' + architecture: ${{ matrix.python-arch }} + - name: Bind original Python source and dependency identities + shell: python + run: | + import hashlib + import os + import subprocess + import tomllib + from pathlib import Path + if subprocess.check_output(['git', 'rev-parse', 'HEAD'], text=True).strip() != os.environ['SOURCE_REF']: + raise SystemExit('unexpected original Python source') + raw = (Path(os.environ['RUNNER_TEMP']) / 'python-gnu-probe.lock').read_bytes() + Path('Cargo.lock').write_bytes(raw) + packages = tomllib.loads(raw.decode('utf-8'))['package'] + expected = {'ktav': ('0.8.1', '7140dfe2fa6c320e74768cd6a637828c2d26f81746254f4fcd805f3993bd3fd5'), 'pyo3': ('0.28.3', '91fd8e38a3b50ed1167fb981cd6fd60147e091784c427b8f7183a7ee32c31c12'), 'python3-dll-a': ('0.2.15', 'd80ba7540edb18890d444c5aa8e1f1f99b1bdf26fb26ae383135325f4a36042b')} + for name, (version, checksum) in expected.items(): + found = [item for item in packages if item['name'] == name] + if len(found) != 1 or found[0]['version'] != version or found[0]['checksum'] != checksum or found[0]['source'] != 'registry+https://github.com/rust-lang/crates.io-index': + raise SystemExit('original reviewed dependency differs: ' + name) + subprocess.run(['git', 'diff', '--exit-code', '--', 'Cargo.toml', 'pyproject.toml', 'src', 'python', 'tests', 'spec'], check=True) + - uses: dtolnay/rust-toolchain@1.99.1 + with: + targets: ${{ matrix.target }} + - name: Install exact SDK and bind real matching-CPU CPython import libraries + shell: python + run: | + """Настроить reviewed SDK и genuine CPython import libraries для CI probe.""" + + import hashlib + import json + import os + from pathlib import Path, PurePosixPath + import platform + import stat + import subprocess + import sys + import urllib.request + import zipfile + + TARGETS = {'x86_64-pc-windows-gnullvm': ('x64', 'x86_64-w64-mingw32-clang.exe', 'IMAGE_FILE_MACHINE_AMD64'), 'aarch64-pc-windows-gnullvm': ('arm64', 'aarch64-w64-mingw32-clang.exe', 'IMAGE_FILE_MACHINE_ARM64')} + target = os.environ['GNU_TARGET'] + arch, compiler_name, machine = TARGETS[target] + actual = {'amd64': 'x64', 'x86_64': 'x64', 'arm64': 'arm64', 'aarch64': 'arm64'}.get(platform.machine().lower()) + if actual != arch or os.environ['ACTUAL_RUNNER_ARCH'].lower() != arch: + raise ValueError('genuine matching runner and CPython architecture required') + root = Path(os.environ['RUNNER_TEMP']) + evidence = root / 'python-gnu-probe-evidence' + evidence.mkdir() + folder = root / 'python-gnu-probe-sdk' + folder.mkdir() + archive_path = folder / 'sdk.zip' + url = 'https://github.com/mstorsjo/llvm-mingw/releases/download/20261020/llvm-mingw-20261020-ucrt-x86_64.zip' + checksum = hashlib.sha256() + total = 0 + with urllib.request.urlopen(url, timeout=120) as response, archive_path.open('xb') as output: + while chunk := response.read(1024 * 1024): + total += len(chunk) + if total > 256 * 1024 * 1024: + raise ValueError('SDK archive bound') + checksum.update(chunk) + output.write(chunk) + if checksum.hexdigest() != 'fd5462b7f5d93b2ff981fcd3f9c7e427e47f3203c64ea91c27bcc444eebd74cd': + raise ValueError('reviewed LLVM MinGW SDK archive differs') + with zipfile.ZipFile(archive_path) as archive: + seen = set() + total = 0 + for item in archive.infolist(): + path = PurePosixPath(item.filename) + total += item.file_size + if path.is_absolute() or '..' in path.parts or chr(92) in item.filename or ':' in item.filename or item.filename.casefold() in seen or stat.S_IFMT(item.external_attr >> 16) == stat.S_IFLNK or item.file_size > 512 * 1024 * 1024 or total > 2 * 1024 * 1024 * 1024 or len(seen) > 16384: + raise ValueError('unsafe or oversized reviewed SDK member') + seen.add(item.filename.casefold()) + archive.extractall(folder) + sdk = folder / 'llvm-mingw-20261020-ucrt-x86_64' + compiler = sdk / 'bin' / compiler_name + lib_dir = Path(sys.base_prefix) / 'libs' + import_library = lib_dir / 'python3.lib' + if not compiler.is_file() or not import_library.is_file() or import_library.is_symlink() or import_library.stat().st_size > 16 * 1024 * 1024: + raise ValueError('real SDK compiler and installed CPython stable-ABI import library required') + headers = subprocess.check_output([str(sdk / 'bin/llvm-readobj.exe'), '--file-headers', str(import_library)]) + if machine.encode() not in headers or any(value.encode() in headers for value in ['IMAGE_FILE_MACHINE_AMD64', 'IMAGE_FILE_MACHINE_ARM64', 'IMAGE_FILE_MACHINE_I386'] if value != machine): + raise ValueError('actual CPython import library CPU differs') + (evidence / 'cpython-import-library-headers.txt').write_bytes(headers) + (evidence / 'compiler-version.txt').write_bytes(subprocess.check_output([str(compiler), '--version'])) + record = {'target': target, 'runner_arch': arch, 'python_version': platform.python_version(), 'python_machine': platform.machine(), 'python_import_library': import_library.name, 'python_import_library_sha256': hashlib.sha256(import_library.read_bytes()).hexdigest(), 'sdk_archive_sha256': checksum.hexdigest(), 'source_ref': os.environ['SOURCE_REF'], 'tooling_ref': os.environ['GITHUB_SHA'], 'scope': 'Compatibility probe using real installed CPython import libraries; no target spoof, generated fake DLL/library, native distribution or licence clearance.'} + (evidence / 'source-provenance.json').write_text(json.dumps(record, indent=2) + '\n', encoding='utf-8') + key = 'CARGO_TARGET_' + target.upper().replace('-', '_') + '_LINKER' + flags = ['-C', 'target-feature=+crt-static', '-C', 'link-arg=-Wl,-Map,' + (evidence / 'native-link.map').as_posix(), '-C', 'link-arg=-Wl,--reproduce=' + (root / 'python-gnu-probe-link.tar').as_posix()] + with Path(os.environ['GITHUB_ENV']).open('a', encoding='utf-8') as output: + for name, value in [(key, str(compiler)), ('CARGO_ENCODED_RUSTFLAGS', chr(31).join(flags)), ('LLVM_MINGW_CANDIDATE', str(sdk)), ('PYO3_CROSS_LIB_DIR', str(lib_dir)), ('PYO3_CROSS_PYTHON_VERSION', platform.python_version().rsplit('.', 1)[0])]: + output.write(name + '=' + value + '\n') + with Path(os.environ['GITHUB_PATH']).open('a', encoding='utf-8') as output: + output.write(str(sdk / 'bin') + '\n') + - name: Build original Python stable-ABI wheel for the genuine GNU target + uses: PyO3/maturin-action@v1 + with: + target: ${{ matrix.target }} + args: --release --locked --out dist -i python + rust-toolchain: '1.99.1' + - name: Inspect actual PE and run unchanged original native Python tests + shell: python + run: | + """Проверить actual GNU wheel и установленный native Python backend в CI.""" + + import hashlib + import json + import os + from pathlib import Path + import re + import shutil + import struct + import subprocess + import sys + import tarfile + import xml.etree.ElementTree as ET + import zipfile + + target = os.environ['GNU_TARGET'] + arm = target.startswith('aarch64-') + root = Path(os.environ['RUNNER_TEMP']) + evidence = root / 'python-gnu-probe-evidence' + sdk = Path(os.environ['LLVM_MINGW_CANDIDATE']) + wheels = list(Path('dist').glob('*.whl')) + if len(wheels) != 1 or ('-cp39-abi3-win_arm64.whl' if arm else '-cp39-abi3-win_amd64.whl') not in wheels[0].name: + raise ValueError('one genuine 0.8.1 Windows stable-ABI wheel required') + wheel = wheels[0] + if not wheel.name.startswith('ktav-0.8.1-') or wheel.stat().st_size > 128 * 1024 * 1024: + raise ValueError('bounded original-version compatibility wheel required') + with zipfile.ZipFile(wheel) as archive: + native = [name for name in archive.namelist() if name.startswith('ktav/_core') and name.endswith('.pyd')] + if len(native) != 1: + raise ValueError('one real PyO3 extension required') + raw = archive.read(native[0]) + pe = int.from_bytes(raw[60:64], 'little') + if len(raw) > 128 * 1024 * 1024 or raw[:2] != b'MZ' or raw[pe:pe + 4] != b'PE\0\0' or struct.unpack_from(' 512 * 1024 * 1024: + raise ValueError('actual linker reproduction required') + inputs = [] + seen = set() + total = 0 + with tarfile.open(reproduction, mode='r:') as archive: + for item in archive: + from pathlib import PurePosixPath + path = PurePosixPath(item.name) + if path.is_absolute() or '..' in path.parts or chr(92) in item.name or item.name in seen or item.issym() or item.islnk(): + raise ValueError('unsafe linker input inventory') + seen.add(item.name) + if item.isdir(): + continue + total += item.size + if not item.isfile() or item.size > 128 * 1024 * 1024 or total > 512 * 1024 * 1024 or len(seen) > 8192: + raise ValueError('linker input bounds') + source = archive.extractfile(item) + checksum = hashlib.sha256() + size = 0 + while chunk := source.read(1024 * 1024): + size += len(chunk) + checksum.update(chunk) + if size != item.size: + raise ValueError('truncated actual linker input') + inputs.append({'path': item.name, 'bytes': size, 'sha256': checksum.hexdigest()}) + (evidence / 'link-input-fingerprints.json').write_text(json.dumps(inputs, indent=2) + '\n', encoding='utf-8') + (evidence / 'rustc.txt').write_bytes(subprocess.check_output(['rustc', '--version', '--verbose'])) + shutil.copyfile('Cargo.lock', evidence / 'Cargo.lock') + if hashlib.sha256(Path('Cargo.lock').read_bytes()).hexdigest() != '657c314ced9fea78d2868a6ba42d410bab8db587a80c130ff7866e00bb9a57b7': + raise ValueError('compatibility build changed original reviewed lock') + subprocess.run([sys.executable, '-m', 'pip', 'install', '--no-index', '--no-deps', str(wheel)], check=True) + subprocess.run([sys.executable, '-m', 'pip', 'install', 'pytest>=7.0', 'pytest-cov>=4.0'], check=True) + environment = dict(os.environ) + for name in ['PYTHONPATH', 'PYO3_CROSS_LIB_DIR', 'PYO3_CROSS_PYTHON_VERSION', 'CARGO_TARGET_DIR']: + environment.pop(name, None) + path_key = next(name for name in environment if name.upper() == 'PATH') + environment[path_key] = os.pathsep.join(part for part in environment[path_key].split(os.pathsep) if part and not any(value in part.lower() for value in ['.cargo', '.rustup', 'llvm-mingw'])) + environment['KTAV_REQUIRE_NATIVE'] = '1' + environment['EXPECTED_NATIVE_SHA256'] = hashlib.sha256(raw).hexdigest() + preflight = 'import hashlib,importlib.metadata,json,os,pathlib,ktav,ktav._core; p=pathlib.Path(ktav._core.__file__).resolve(); d=importlib.metadata.distribution("ktav"); assert d.version=="0.8.1"; assert any(pathlib.Path(d.locate_file(f)).resolve()==p for f in d.files); assert hashlib.sha256(p.read_bytes()).hexdigest()==os.environ["EXPECTED_NATIVE_SHA256"]; assert ktav.loads("answer: 42")=={"answer":42}; print(json.dumps({"installed_native":str(p),"sha256":hashlib.sha256(p.read_bytes()).hexdigest()}))' + result = subprocess.run([sys.executable, '-I', '-c', preflight], env=environment, stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True) + (evidence / 'installed-native-preflight.txt').write_bytes(result.stdout + result.stderr) + result = subprocess.run([sys.executable, '-I', '-m', 'pytest', 'tests', '-q', '--junitxml=' + str(evidence / 'pytest.xml')], env=environment, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + (evidence / 'consumer-stdout.txt').write_bytes(result.stdout) + (evidence / 'consumer-stderr.txt').write_bytes(result.stderr) + sys.stdout.buffer.write(result.stdout) + sys.stderr.buffer.write(result.stderr) + if result.returncode: + raise ValueError('unchanged original Python tests failed against actual installed GNU extension') + suites = ET.parse(evidence / 'pytest.xml').getroot() + cases = suites.findall('.//testcase') + conformance = [case for case in cases if 'test_conformance' in case.attrib.get('classname', '')] + if not conformance or any(case.find(tag) is not None for case in conformance for tag in ['failure', 'error', 'skipped']): + raise ValueError('complete original native conformance tests must execute without skips') + subprocess.run(['git', 'diff', '--exit-code', '--', 'Cargo.toml', 'pyproject.toml', 'src', 'python', 'tests', 'spec'], check=True) + report = {'target': target, 'source_ref': os.environ['SOURCE_REF'], 'binary_sha256': hashlib.sha256(raw).hexdigest(), 'wheel_sha256': hashlib.sha256(wheel.read_bytes()).hexdigest(), 'imports': imports, 'test_cases': len(cases), 'conformance_cases': len(conformance), 'rust_and_sdk_paths_removed': True, 'wheel_and_native_uploaded': False, 'scope': 'Genuine same-CPU original 0.8.1 PyO3 GNU compatibility probe only. No complete runtime-grant/resource acceptance, production integration, publication or historical repair.'} + (evidence / 'compatibility-verification.json').write_text(json.dumps(report, indent=2) + '\n', encoding='utf-8') + - name: Upload inert compatibility diagnostics only + if: always() + uses: actions/upload-artifact@v4 + with: + name: python-gnu-compatibility-${{ matrix.target }} + path: | + ${{ runner.temp }}/python-gnu-probe-evidence/*.json + ${{ runner.temp }}/python-gnu-probe-evidence/*.txt + ${{ runner.temp }}/python-gnu-probe-evidence/*.map + ${{ runner.temp }}/python-gnu-probe-evidence/*.lock + ${{ runner.temp }}/python-gnu-probe-evidence/*.xml + if-no-files-found: warn diff --git a/ci/windows-gnu-probe.lock b/ci/windows-gnu-probe.lock new file mode 100644 index 0000000..254be59 --- /dev/null +++ b/ci/windows-gnu-probe.lock @@ -0,0 +1,324 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "bumpalo" +version = "3.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" + +[[package]] +name = "castaway" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dec551ab6e7578819132c713a93c022a05d60159dc86e7a7050223577484c55a" +dependencies = [ + "rustversion", +] + +[[package]] +name = "cc" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86e055fa6e3e40ad6187ba25b6855eeb635d5271f8e0f0e1e9a5826b8d75bea0" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7648175b45a9a48536d676f68d918270699102aa8dab5496df06904c914600" + +[[package]] +name = "compact_str" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dfdd1c2274d9aa354115b09dc9a901d6c5576818cdf70d14cae2bdb47df00ab" +dependencies = [ + "castaway", + "cfg-if", + "itoa", + "rustversion", + "ryu", + "serde", + "static_assertions", +] + +[[package]] +name = "equivalent" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00d174d5400e5e8fd687ad1049e2f578285fa914201b1af7e8b112a4546bd826" + +[[package]] +name = "find-msvc-tools" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c66915cc5391097de7a2b0b972e8505aa33c0df401fd3a6811dbdb32881ba48" + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "indexmap" +version = "2.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc4e190f5d26ca7051642629da2c52fc03bde85a03197c99408dcd291734c855" +dependencies = [ + "equivalent", + "hashbrown", + "serde", + "serde_core", +] + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "ktav" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7140dfe2fa6c320e74768cd6a637828c2d26f81746254f4fcd805f3993bd3fd5" +dependencies = [ + "bumpalo", + "compact_str", + "indexmap", + "itoa", + "memchr", + "rustc-hash", + "ryu", + "serde", +] + +[[package]] +name = "ktav-python" +version = "0.8.1" +dependencies = [ + "indexmap", + "itoa", + "ktav", + "pyo3", + "rustc-hash", + "ryu", +] + +[[package]] +name = "libc" +version = "0.2.191" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65cd87c9a6d60536580b316ad985726104730a5151c99845f2490428ef91240a" + +[[package]] +name = "memchr" +version = "2.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "portable-atomic" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05c8b63e8d9609db387f0324918f81d68fe27748f084ef092fb35954d0539a85" + +[[package]] +name = "proc-macro2" +version = "1.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "pyo3" +version = "0.28.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91fd8e38a3b50ed1167fb981cd6fd60147e091784c427b8f7183a7ee32c31c12" +dependencies = [ + "indexmap", + "libc", + "once_cell", + "portable-atomic", + "pyo3-build-config", + "pyo3-ffi", + "pyo3-macros", +] + +[[package]] +name = "pyo3-build-config" +version = "0.28.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e368e7ddfdeb98c9bca7f8383be1648fd84ab466bf2bc015e94008db6d35611e" +dependencies = [ + "python3-dll-a", + "target-lexicon", +] + +[[package]] +name = "pyo3-ffi" +version = "0.28.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f29e10af80b1f7ccaf7f69eace800a03ecd13e883acfacc1e5d0988605f651e" +dependencies = [ + "libc", + "pyo3-build-config", +] + +[[package]] +name = "pyo3-macros" +version = "0.28.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df6e520eff47c45997d2fc7dd8214b25dd1310918bbb2642156ef66a67f29813" +dependencies = [ + "proc-macro2", + "pyo3-macros-backend", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "pyo3-macros-backend" +version = "0.28.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4cdc218d835738f81c2338f822078af45b4afdf8b2e33cbb5916f108b813acb" +dependencies = [ + "heck", + "proc-macro2", + "pyo3-build-config", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "python3-dll-a" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d80ba7540edb18890d444c5aa8e1f1f99b1bdf26fb26ae383135325f4a36042b" +dependencies = [ + "cc", +] + +[[package]] +name = "quote" +version = "1.0.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rustc-hash" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b1e7f9a428571be2dc5bc0505c13fb6bf936822b894ec87abf8a08a4e51742d" + +[[package]] +name = "rustversion" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" + +[[package]] +name = "ryu" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" + +[[package]] +name = "serde" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.9", +] + +[[package]] +name = "shlex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "syn" +version = "2.0.119" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "3.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d78c8dee4c7bf0e14673097256fed6142ce9d3b85a408189d07482442145823b" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "target-lexicon" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca" + +[[package]] +name = "unicode-ident" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2c754d6c33795a1c324727428e5a7dedb5b06195f9890bdbcba760d3e246563" From 8bd3c8f5f7890908d639474b310836af860616ce Mon Sep 17 00:00:00 2001 From: Arch-AI-tech Date: Sun, 1 Nov 2026 20:10:22 +0100 Subject: [PATCH 48/54] fix(ci): scope Python probe runner context to its step GitHub evaluates job-level env before runner context is available. Move the actual runner architecture binding into the SDK execution step, where runner.arch is supported. Preserve all original source, lock, real CPython import-library, target and native consumer assertions. No version, library, parser, production or publication change. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/windows-gnu-pyo3-probe.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/windows-gnu-pyo3-probe.yml b/.github/workflows/windows-gnu-pyo3-probe.yml index 657f778..9b7f769 100644 --- a/.github/workflows/windows-gnu-pyo3-probe.yml +++ b/.github/workflows/windows-gnu-pyo3-probe.yml @@ -27,7 +27,6 @@ jobs: python-arch: arm64 env: GNU_TARGET: ${{ matrix.target }} - ACTUAL_RUNNER_ARCH: ${{ runner.arch }} steps: - uses: actions/checkout@v4 - name: Preserve the reviewed original Python lock @@ -73,6 +72,8 @@ jobs: targets: ${{ matrix.target }} - name: Install exact SDK and bind real matching-CPU CPython import libraries shell: python + env: + ACTUAL_RUNNER_ARCH: ${{ runner.arch }} run: | """Настроить reviewed SDK и genuine CPython import libraries для CI probe.""" From 95515d1fdc1c09a935c226f9609624a4263e091e Mon Sep 17 00:00:00 2001 From: Arch-AI-tech Date: Sun, 1 Nov 2026 20:29:02 +0100 Subject: [PATCH 49/54] ci(licenses): capture actual CPython import library structure The genuine GNU compatibility probe retained the reviewed SDK inputs, one additional MinGW atexit object and real CPython python3.lib. Its headers contain 902 compact import records and three ordinary COFF members. Record their actual sections, symbols and relocations to establish the native input/code boundary before assigning grants. Capture inert text only. Preserve source, lock, real import-library, CPU, wheel, native consumer and diagnostic-only upload gates. Do not infer absence of static runtime code from a library filename or from VCRUNTIME import absence. No dependency/version or production change. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/windows-gnu-pyo3-probe.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/windows-gnu-pyo3-probe.yml b/.github/workflows/windows-gnu-pyo3-probe.yml index 9b7f769..b7d2b42 100644 --- a/.github/workflows/windows-gnu-pyo3-probe.yml +++ b/.github/workflows/windows-gnu-pyo3-probe.yml @@ -128,7 +128,7 @@ jobs: import_library = lib_dir / 'python3.lib' if not compiler.is_file() or not import_library.is_file() or import_library.is_symlink() or import_library.stat().st_size > 16 * 1024 * 1024: raise ValueError('real SDK compiler and installed CPython stable-ABI import library required') - headers = subprocess.check_output([str(sdk / 'bin/llvm-readobj.exe'), '--file-headers', str(import_library)]) + headers = subprocess.check_output([str(sdk / 'bin/llvm-readobj.exe'), '--file-headers', '--sections', '--symbols', '--relocations', str(import_library)]) if machine.encode() not in headers or any(value.encode() in headers for value in ['IMAGE_FILE_MACHINE_AMD64', 'IMAGE_FILE_MACHINE_ARM64', 'IMAGE_FILE_MACHINE_I386'] if value != machine): raise ValueError('actual CPython import library CPU differs') (evidence / 'cpython-import-library-headers.txt').write_bytes(headers) From c50ad7373ffde680b60f40f28b1315d8426e1345 Mon Sep 17 00:00:00 2001 From: Arch-AI-tech Date: Sun, 1 Nov 2026 21:04:04 +0100 Subject: [PATCH 50/54] ci(licenses): bind GNU Python candidates to original grants Extend the genuine matching-CPU Windows GNU path with complete existing Cargo/compiler notices and source-bound MinGW/libunwind/CPython original materials. Pin the actual SDK, native import libraries, stable-ABI source commits, exact retained runtime objects and unchanged 0.8.1 Cargo lock. Preserve all existing Cargo/std/compiler notice fields and validators. Include 59 runtime source originals, six full runtime documents and the selected original CPython licence/stable-ABI definition source in the wheel-owned NOTICE.json resource. Verify actual wheel bytes and RECORD, PE target/imports, complete native consumers and exact native-input boundary before any success-bound wheel/evidence candidate upload. Keep the interpreter and its own runtime DLLs external, not bundled. The original package/binding sources, APIs and dependency versions stay unchanged. Production release integration is not performed in this step. No local build/test/generator, contract acceptance, publication, tag, release, merge, deployment or historical asset repair. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/windows-gnu-pyo3-probe.yml | 154 ++- ci/licenses/python-gnullvm-runtime.json | 948 +++++++++++++++++++ 2 files changed, 1099 insertions(+), 3 deletions(-) create mode 100644 ci/licenses/python-gnullvm-runtime.json diff --git a/.github/workflows/windows-gnu-pyo3-probe.yml b/.github/workflows/windows-gnu-pyo3-probe.yml index b7d2b42..32a1168 100644 --- a/.github/workflows/windows-gnu-pyo3-probe.yml +++ b/.github/workflows/windows-gnu-pyo3-probe.yml @@ -1,4 +1,4 @@ -name: Windows GNU PyO3 compatibility probe +name: Windows GNU PyO3 source-bound notice candidates on: pull_request: @@ -22,13 +22,29 @@ jobs: - os: windows-latest target: x86_64-pc-windows-gnullvm python-arch: x64 + python-version: '3.13.16' - os: windows-11-arm target: aarch64-pc-windows-gnullvm python-arch: arm64 + python-version: '3.13.17' env: GNU_TARGET: ${{ matrix.target }} steps: - uses: actions/checkout@v4 + - name: Preserve reviewed runtime policy and existing Cargo/compiler notice tooling + shell: python + run: | + import os + import shutil + from pathlib import Path + output = Path(os.environ['RUNNER_TEMP']) / 'python-gnu-notice-tools' + output.mkdir() + files = ['scripts/prepare_native_notices.py', 'scripts/cargo_notice_bundle.py', 'scripts/collect_cargo_notices.py', 'scripts/rust_compiler_notices.py', 'scripts/check_native_wheel.py', 'scripts/check_musllinux_wheel.py', 'scripts/macos_native_imports.py', 'ci/about.toml', 'ci/rust-compiler-notices.json', 'ci/licenses/python-gnullvm-runtime.json'] + files.extend(str(path) for path in Path('ci/licenses/rust-compiler-originals').glob('*')) + for name in files: + destination = output / name + destination.parent.mkdir(parents=True, exist_ok=True) + shutil.copyfile(name, destination) - name: Preserve the reviewed original Python lock shell: python run: | @@ -46,8 +62,20 @@ jobs: persist-credentials: false - uses: actions/setup-python@v5 with: - python-version: '3.13' + python-version: ${{ matrix.python-version }} architecture: ${{ matrix.python-arch }} + - name: Restore reviewed notice tooling without changing bindings + shell: python + run: | + import os + import shutil + from pathlib import Path + stage = Path(os.environ['RUNNER_TEMP']) / 'python-gnu-notice-tools' + for path in stage.rglob('*'): + if path.is_file(): + destination = Path(path.relative_to(stage)) + destination.parent.mkdir(parents=True, exist_ok=True) + shutil.copyfile(path, destination) - name: Bind original Python source and dependency identities shell: python run: | @@ -70,6 +98,7 @@ jobs: - uses: dtolnay/rust-toolchain@1.99.1 with: targets: ${{ matrix.target }} + components: rust-docs, llvm-tools-preview - name: Install exact SDK and bind real matching-CPU CPython import libraries shell: python env: @@ -142,6 +171,65 @@ jobs: output.write(name + '=' + value + '\n') with Path(os.environ['GITHUB_PATH']).open('a', encoding='utf-8') as output: output.write(str(sdk / 'bin') + '\n') + - uses: taiki-e/install-action@v2 + with: + tool: cargo-about@0.9.3 + - name: Prepare complete source-bound Python GNU and CPython original grants + shell: python + run: | + import hashlib + import json + import os + import subprocess + import sys + import urllib.request + from pathlib import Path + root = Path(os.environ['RUNNER_TEMP']) + evidence = root / 'python-gnu-notice-evidence' + subprocess.run([sys.executable, 'scripts/prepare_native_notices.py', str(evidence)], check=True) + raw_policy = Path('ci/licenses/python-gnullvm-runtime.json').read_bytes() + if hashlib.sha256(raw_policy).hexdigest() != '3d219f3500c46b346f0031407c276805f8a2c9720963187f466eded0c18609a3': + raise SystemExit('reviewed Python runtime profile differs') + policy = json.loads(raw_policy) + target = os.environ['GNU_TARGET'] + selected = policy['targets'][target] + identity = json.loads((root / 'python-gnu-probe-evidence/source-provenance.json').read_bytes()) + cpython = selected['cpython_import_library'] + if identity['python_version'] != cpython['version'] or identity['python_import_library_sha256'] != cpython['sha256']: + raise SystemExit('actual CPython import library differs from reviewed profile') + materials = [*policy['source_files'], *policy['original_documents'], *cpython['original_materials']] + originals = [] + total = 0 + for number, item in enumerate(materials): + url = item['source'] + if not url.startswith(('https://raw.githubusercontent.com/mingw-w64/mingw-w64/0d6dfbc348dfad714761c0a7151615767f96bcb5/', 'https://raw.githubusercontent.com/llvm/llvm-project/7a941dfb2877a0b8cf30323fefa85ad019e0b0f5/', 'https://raw.githubusercontent.com/python/cpython/' + cpython['source_commit'] + '/')): + raise SystemExit('unreviewed runtime original source URL') + with urllib.request.urlopen(url, timeout=90) as response: + raw = response.read(4 * 1024 * 1024 + 1) + total += len(raw) + blob = hashlib.sha1(b'blob ' + str(len(raw)).encode() + bytes([0]) + raw).hexdigest() + if len(raw) > 4 * 1024 * 1024 or total > 32 * 1024 * 1024 or hashlib.sha256(raw).hexdigest() != item['sha256'] or item.get('git_blob', blob) != blob or 'text' in item and item['text'].encode('utf-8') != raw: + raise SystemExit('full original runtime source/grant bytes differ') + originals.append({**item, 'text': raw.decode('utf-8')}) + destination = evidence / 'runtime-originals' / (str(number) + '.txt') + destination.parent.mkdir(parents=True, exist_ok=True) + destination.write_bytes(raw) + before = (evidence / 'NOTICE.json').read_bytes() + bundle = json.loads(before) + if 'non_cargo_runtimes' in bundle or bundle['cargo_lock_sha256'] != '657c314ced9fea78d2868a6ba42d410bab8db587a80c130ff7866e00bb9a57b7' or bundle['rust_compiler_builtins']['profile_sha256'] != policy['compiler_profile_sha256']: + raise SystemExit('original Cargo/compiler notice collection differs') + bundle['non_cargo_runtimes'] = [{'name': 'Source-bound Windows GNU runtime and CPython stable-ABI import metadata', 'target': target, 'profile_sha256': hashlib.sha256(raw_policy).hexdigest(), 'sdk_archive_sha256': policy['sdk_archive_sha256'], 'selected_inputs': selected, 'original_materials': originals, 'coverage': policy['coverage']}] + bundle['coverage'] += ' Full selected GNU/CPython source and grant originals retained; actual retained-input gate is mandatory.' + data = (json.dumps(bundle, ensure_ascii=False, sort_keys=True, indent=2) + '\n').encode('utf-8') + if len(data) > 64 * 1024 * 1024: + raise SystemExit('combined notice resource exceeds bound') + package = Path('python/ktav/_third_party/NOTICE.json') + if package.read_bytes() != before: + raise SystemExit('prepared package notice differs before augmentation') + (evidence / 'NOTICE.cargo-std.json').write_bytes(before) + (evidence / 'NOTICE.json').write_bytes(data) + package.write_bytes(data) + (evidence / 'python-gnullvm-runtime.json').write_bytes(raw_policy) - name: Build original Python stable-ABI wheel for the genuine GNU target uses: PyO3/maturin-action@v1 with: @@ -249,8 +337,68 @@ jobs: if not conformance or any(case.find(tag) is not None for case in conformance for tag in ['failure', 'error', 'skipped']): raise ValueError('complete original native conformance tests must execute without skips') subprocess.run(['git', 'diff', '--exit-code', '--', 'Cargo.toml', 'pyproject.toml', 'src', 'python', 'tests', 'spec'], check=True) - report = {'target': target, 'source_ref': os.environ['SOURCE_REF'], 'binary_sha256': hashlib.sha256(raw).hexdigest(), 'wheel_sha256': hashlib.sha256(wheel.read_bytes()).hexdigest(), 'imports': imports, 'test_cases': len(cases), 'conformance_cases': len(conformance), 'rust_and_sdk_paths_removed': True, 'wheel_and_native_uploaded': False, 'scope': 'Genuine same-CPU original 0.8.1 PyO3 GNU compatibility probe only. No complete runtime-grant/resource acceptance, production integration, publication or historical repair.'} + report = {'target': target, 'source_ref': os.environ['SOURCE_REF'], 'binary_sha256': hashlib.sha256(raw).hexdigest(), 'wheel_sha256': hashlib.sha256(wheel.read_bytes()).hexdigest(), 'imports': imports, 'test_cases': len(cases), 'conformance_cases': len(conformance), 'rust_and_sdk_paths_removed': True, 'native_upload_requires_full_notice_gate': True, 'scope': 'Genuine same-CPU original 0.8.1 PyO3 GNU installed-wheel execution. Complete runtime/source-grant acceptance requires the separate retained-input and exact wheel notice gates before any candidate upload. No production integration, publication or historical repair.'} (evidence / 'compatibility-verification.json').write_text(json.dumps(report, indent=2) + '\n', encoding='utf-8') + - name: Verify complete actual wheel and exact retained-input/source-grant binding + shell: python + run: | + import hashlib + import json + import os + import re + import shutil + import subprocess + import sys + from pathlib import Path + root = Path(os.environ['RUNNER_TEMP']) + evidence = root / 'python-gnu-notice-evidence' + probe = root / 'python-gnu-probe-evidence' + target = os.environ['GNU_TARGET'] + policy = json.loads((evidence / 'python-gnullvm-runtime.json').read_bytes()) + expected = policy['targets'][target] + wheel, = Path('dist').glob('*.whl') + subprocess.run([sys.executable, 'scripts/check_native_wheel.py', str(wheel), str(evidence / 'NOTICE.json'), str(evidence / 'native-report.txt')], check=True) + native_inputs = json.loads((probe / 'link-input-fingerprints.json').read_bytes()) + selected = [] + python_libraries = [] + marker = 'llvm-mingw-20261020-ucrt-x86_64/' + for item in native_inputs: + path = item['path'].replace(chr(92), '/') + if not path.endswith(('.a', '.o', '.obj', '.rlib', '.lib')): + continue + if marker in path: + selected.append({'path': path.split(marker, 1)[1], 'size': item['bytes'], 'sha256': item['sha256']}) + elif path.endswith('/python3.lib'): + python_libraries.append(item) + else: + cargo = r'/target/' + re.escape(target) + r'/release/deps/' + host = 'aarch64' if target.startswith('aarch64-') else 'x86_64' + rust = r'/[.]rustup/toolchains/1[.]99[.]1-' + host + r'-pc-windows-msvc/lib/rustlib/' + re.escape(target) + r'/lib/' + if not (re.search(cargo + r'_core[.][^/]+[.]rcgu[.]o$', path) or re.search(cargo + r'rustc[^/]+/libstd-[0-9a-f]+[.]rlib$', path) or re.search(rust + r'libcompiler_builtins-[0-9a-f]+[.]rlib$', path)): + raise SystemExit('native linker input outside reviewed Rust/SDK/CPython boundary') + if sorted(selected, key=lambda item: item['path']) != expected['sdk_inputs'] or len(python_libraries) != 1 or python_libraries[0]['sha256'] != expected['cpython_import_library']['sha256'] or python_libraries[0]['bytes'] != expected['cpython_import_library']['bytes']: + raise SystemExit('actual SDK/CPython import-library bytes differ') + retained = set() + for line in (probe / 'native-link.map').read_bytes().splitlines(): + match = re.match(rb'^[0-9a-fA-F]+\s+[0-9a-fA-F]+\s+\d+\s{2,}(.+):\([^\r\n]*\)\s*$', line) + if match and b'.rcgu.o' not in match[1]: + retained.add(match[1].decode('utf-8').replace(chr(92), '/').rsplit('/', 1)[-1]) + if retained != set(expected['retained_objects']): + raise SystemExit('actual retained GNU runtime objects differ from reviewed source grants') + for path in probe.iterdir(): + if path.is_file() and path.suffix in {'.json', '.txt', '.map', '.lock', '.xml'}: + shutil.copyfile(path, evidence / path.name) + report = {'target': target, 'source_ref': os.environ['SOURCE_REF'], 'tooling_ref': os.environ['GITHUB_SHA'], 'wheel_sha256': hashlib.sha256(wheel.read_bytes()).hexdigest(), 'notice_sha256': hashlib.sha256((evidence / 'NOTICE.json').read_bytes()).hexdigest(), 'sdk_inputs': len(selected), 'retained_runtime_objects': len(retained), 'original_materials': len(json.loads((evidence / 'NOTICE.json').read_bytes())['non_cargo_runtimes'][0]['original_materials']), 'scope': 'Actual wheel RECORD/resource/native target, original Cargo/compiler/runtime/CPython grant/source and retained-input gates. Not production integration or historical repair.'} + (evidence / 'native-runtime-verification.json').write_text(json.dumps(report, indent=2) + '\n', encoding='utf-8') + delivery = root / 'python-gnu-notice-candidate' + delivery.mkdir() + shutil.copyfile(wheel, delivery / wheel.name) + shutil.copytree(evidence, delivery / 'evidence') + - uses: actions/upload-artifact@v4 + with: + name: python-gnu-notice-candidate-${{ matrix.target }} + path: ${{ runner.temp }}/python-gnu-notice-candidate/ + if-no-files-found: error - name: Upload inert compatibility diagnostics only if: always() uses: actions/upload-artifact@v4 diff --git a/ci/licenses/python-gnullvm-runtime.json b/ci/licenses/python-gnullvm-runtime.json new file mode 100644 index 0000000..bc32eb2 --- /dev/null +++ b/ci/licenses/python-gnullvm-runtime.json @@ -0,0 +1,948 @@ +{ + "cargo_lock_sha256": "657c314ced9fea78d2868a6ba42d410bab8db587a80c130ff7866e00bb9a57b7", + "compiler_profile_sha256": "39cbf18420d5e0c0a35fb2d5e5fbed4db755488ea0ca11414944ead9fbabab92", + "coverage": "Source-bound Python GNU DLL startup/MinGW/libunwind grants, exact selected SDK archive inputs and retained runtime objects. The actual separately supplied CPython python3.lib is pinned by CPU/hash and its 902 compact import records match the original stable-ABI declarations. Its three ordinary COFF members are non-executable import/debug metadata, not Microsoft CRT startup. CPython source grants are retained conservatively; no interpreter implementation or interpreter runtime is bundled. No SDK source-build reproducibility, all-compiler-code inventory or historical package repair claim.", + "llvm_revision": "7a941dfb2877a0b8cf30323fefa85ad019e0b0f5", + "mingw_revision": "0d6dfbc348dfad714761c0a7151615767f96bcb5", + "original_documents": [ + { + "git_blob": "fa6ac540007032cbd0ec772a1c72e6cb5527a4fe", + "path": "LICENSE.TXT", + "sha256": "8d85c1057d742e597985c7d4e6320b015a9139385cff4cbae06ffc0ebe89afee", + "source": "https://raw.githubusercontent.com/llvm/llvm-project/7a941dfb2877a0b8cf30323fefa85ad019e0b0f5/LICENSE.TXT", + "text": "==============================================================================\nThe LLVM Project is under the Apache License v2.0 with LLVM Exceptions:\n==============================================================================\n\n Apache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\n TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n 1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n 2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n 3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under this License for that Work shall terminate\n as of the date such litigation is filed.\n\n 4. Redistribution. You may reproduce and distribute copies of the\n Work or Derivative Works thereof in any medium, with or without\n modifications, and in Source or Object form, provided that You\n meet the following conditions:\n\n (a) You must give any other recipients of the Work or\n Derivative Works a copy of this License; and\n\n (b) You must cause any modified files to carry prominent notices\n stating that You changed the files; and\n\n (c) You must retain, in the Source form of any Derivative Works\n that You distribute, all copyright, patent, trademark, and\n attribution notices from the Source form of the Work,\n excluding those notices that do not pertain to any part of\n the Derivative Works; and\n\n (d) If the Work includes a \"NOTICE\" text file as part of its\n distribution, then any Derivative Works that You distribute must\n include a readable copy of the attribution notices contained\n within such NOTICE file, excluding those notices that do not\n pertain to any part of the Derivative Works, in at least one\n of the following places: within a NOTICE text file distributed\n as part of the Derivative Works; within the Source form or\n documentation, if provided along with the Derivative Works; or,\n within a display generated by the Derivative Works, if and\n wherever such third-party notices normally appear. The contents\n of the NOTICE file are for informational purposes only and\n do not modify the License. You may add Your own attribution\n notices within Derivative Works that You distribute, alongside\n or as an addendum to the NOTICE text from the Work, provided\n that such additional attribution notices cannot be construed\n as modifying the License.\n\n You may add Your own copyright statement to Your modifications and\n may provide additional or different license terms and conditions\n for use, reproduction, or distribution of Your modifications, or\n for any such Derivative Works as a whole, provided Your use,\n reproduction, and distribution of the Work otherwise complies with\n the conditions stated in this License.\n\n 5. Submission of Contributions. Unless You explicitly state otherwise,\n any Contribution intentionally submitted for inclusion in the Work\n by You to the Licensor shall be under the terms and conditions of\n this License, without any additional terms or conditions.\n Notwithstanding the above, nothing herein shall supersede or modify\n the terms of any separate license agreement you may have executed\n with Licensor regarding such Contributions.\n\n 6. Trademarks. This License does not grant permission to use the trade\n names, trademarks, service marks, or product names of the Licensor,\n except as required for reasonable and customary use in describing the\n origin of the Work and reproducing the content of the NOTICE file.\n\n 7. Disclaimer of Warranty. Unless required by applicable law or\n agreed to in writing, Licensor provides the Work (and each\n Contributor provides its Contributions) on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n implied, including, without limitation, any warranties or conditions\n of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n PARTICULAR PURPOSE. You are solely responsible for determining the\n appropriateness of using or redistributing the Work and assume any\n risks associated with Your exercise of permissions under this License.\n\n 8. Limitation of Liability. In no event and under no legal theory,\n whether in tort (including negligence), contract, or otherwise,\n unless required by applicable law (such as deliberate and grossly\n negligent acts) or agreed to in writing, shall any Contributor be\n liable to You for damages, including any direct, indirect, special,\n incidental, or consequential damages of any character arising as a\n result of this License or out of the use or inability to use the\n Work (including but not limited to damages for loss of goodwill,\n work stoppage, computer failure or malfunction, or any and all\n other commercial damages or losses), even if such Contributor\n has been advised of the possibility of such damages.\n\n 9. Accepting Warranty or Additional Liability. While redistributing\n the Work or Derivative Works thereof, You may choose to offer,\n and charge a fee for, acceptance of support, warranty, indemnity,\n or other liability obligations and/or rights consistent with this\n License. However, in accepting such obligations, You may act only\n on Your own behalf and on Your sole responsibility, not on behalf\n of any other Contributor, and only if You agree to indemnify,\n defend, and hold each Contributor harmless for any liability\n incurred by, or claims asserted against, such Contributor by reason\n of your accepting any such warranty or additional liability.\n\n END OF TERMS AND CONDITIONS\n\n APPENDIX: How to apply the Apache License to your work.\n\n To apply the Apache License to your work, attach the following\n boilerplate notice, with the fields enclosed by brackets \"[]\"\n replaced with your own identifying information. (Don't include\n the brackets!) The text should be enclosed in the appropriate\n comment syntax for the file format. We also recommend that a\n file or class name and description of purpose be included on the\n same \"printed page\" as the copyright notice for easier\n identification within third-party archives.\n\n Copyright [yyyy] [name of copyright owner]\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n\n---- LLVM Exceptions to the Apache 2.0 License ----\n\nAs an exception, if, as a result of your compiling your source code, portions\nof this Software are embedded into an Object form of such source code, you\nmay redistribute such embedded portions in such Object form without complying\nwith the conditions of Sections 4(a), 4(b) and 4(d) of the License.\n\nIn addition, if you combine or link compiled forms of this Software with\nsoftware that is licensed under the GPLv2 (\"Combined Software\") and if a\ncourt of competent jurisdiction determines that the patent provision (Section\n3), the indemnity provision (Section 9) or other Section of the License\nconflicts with the conditions of the GPLv2, you may retroactively and\nprospectively choose to deem waived or otherwise exclude such Section(s) of\nthe License, but only in their entirety and only with respect to the Combined\nSoftware.\n\n==============================================================================\nSoftware from third parties included in the LLVM Project:\n==============================================================================\nThe LLVM Project contains third party software which is under different license\nterms. All such code will be identified clearly using at least one of two\nmechanisms:\n1) It will be in a separate directory tree with its own `LICENSE.txt` or\n `LICENSE` file at the top containing the specific license and restrictions\n which apply to that software, or\n2) It will contain specific license and restriction terms at the top of every\n file.\n\n==============================================================================\nLegacy LLVM License (https://llvm.org/docs/DeveloperPolicy.html#legacy):\n==============================================================================\nUniversity of Illinois/NCSA\nOpen Source License\n\nCopyright (c) 2003-2019 University of Illinois at Urbana-Champaign.\nAll rights reserved.\n\nDeveloped by:\n\n LLVM Team\n\n University of Illinois at Urbana-Champaign\n\n http://llvm.org\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal with\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies\nof the Software, and to permit persons to whom the Software is furnished to do\nso, subject to the following conditions:\n\n * Redistributions of source code must retain the above copyright notice,\n this list of conditions and the following disclaimers.\n\n * Redistributions in binary form must reproduce the above copyright notice,\n this list of conditions and the following disclaimers in the\n documentation and/or other materials provided with the distribution.\n\n * Neither the names of the LLVM Team, University of Illinois at\n Urbana-Champaign, nor the names of its contributors may be used to\n endorse or promote products derived from this Software without specific\n prior written permission.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\nFOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nCONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE\nSOFTWARE.\n\n" + }, + { + "git_blob": "1e3120621caeb62648745a42f7a9a25d1e2731ab", + "path": "libunwind/LICENSE.TXT", + "sha256": "b5efebcaca80879234098e52d1725e6d9eb8fb96a19fce625d39184b705f7b6d", + "source": "https://raw.githubusercontent.com/llvm/llvm-project/7a941dfb2877a0b8cf30323fefa85ad019e0b0f5/libunwind/LICENSE.TXT", + "text": "==============================================================================\nThe LLVM Project is under the Apache License v2.0 with LLVM Exceptions:\n==============================================================================\n\n Apache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\n TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n 1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n 2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n 3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under this License for that Work shall terminate\n as of the date such litigation is filed.\n\n 4. Redistribution. You may reproduce and distribute copies of the\n Work or Derivative Works thereof in any medium, with or without\n modifications, and in Source or Object form, provided that You\n meet the following conditions:\n\n (a) You must give any other recipients of the Work or\n Derivative Works a copy of this License; and\n\n (b) You must cause any modified files to carry prominent notices\n stating that You changed the files; and\n\n (c) You must retain, in the Source form of any Derivative Works\n that You distribute, all copyright, patent, trademark, and\n attribution notices from the Source form of the Work,\n excluding those notices that do not pertain to any part of\n the Derivative Works; and\n\n (d) If the Work includes a \"NOTICE\" text file as part of its\n distribution, then any Derivative Works that You distribute must\n include a readable copy of the attribution notices contained\n within such NOTICE file, excluding those notices that do not\n pertain to any part of the Derivative Works, in at least one\n of the following places: within a NOTICE text file distributed\n as part of the Derivative Works; within the Source form or\n documentation, if provided along with the Derivative Works; or,\n within a display generated by the Derivative Works, if and\n wherever such third-party notices normally appear. The contents\n of the NOTICE file are for informational purposes only and\n do not modify the License. You may add Your own attribution\n notices within Derivative Works that You distribute, alongside\n or as an addendum to the NOTICE text from the Work, provided\n that such additional attribution notices cannot be construed\n as modifying the License.\n\n You may add Your own copyright statement to Your modifications and\n may provide additional or different license terms and conditions\n for use, reproduction, or distribution of Your modifications, or\n for any such Derivative Works as a whole, provided Your use,\n reproduction, and distribution of the Work otherwise complies with\n the conditions stated in this License.\n\n 5. Submission of Contributions. Unless You explicitly state otherwise,\n any Contribution intentionally submitted for inclusion in the Work\n by You to the Licensor shall be under the terms and conditions of\n this License, without any additional terms or conditions.\n Notwithstanding the above, nothing herein shall supersede or modify\n the terms of any separate license agreement you may have executed\n with Licensor regarding such Contributions.\n\n 6. Trademarks. This License does not grant permission to use the trade\n names, trademarks, service marks, or product names of the Licensor,\n except as required for reasonable and customary use in describing the\n origin of the Work and reproducing the content of the NOTICE file.\n\n 7. Disclaimer of Warranty. Unless required by applicable law or\n agreed to in writing, Licensor provides the Work (and each\n Contributor provides its Contributions) on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n implied, including, without limitation, any warranties or conditions\n of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n PARTICULAR PURPOSE. You are solely responsible for determining the\n appropriateness of using or redistributing the Work and assume any\n risks associated with Your exercise of permissions under this License.\n\n 8. Limitation of Liability. In no event and under no legal theory,\n whether in tort (including negligence), contract, or otherwise,\n unless required by applicable law (such as deliberate and grossly\n negligent acts) or agreed to in writing, shall any Contributor be\n liable to You for damages, including any direct, indirect, special,\n incidental, or consequential damages of any character arising as a\n result of this License or out of the use or inability to use the\n Work (including but not limited to damages for loss of goodwill,\n work stoppage, computer failure or malfunction, or any and all\n other commercial damages or losses), even if such Contributor\n has been advised of the possibility of such damages.\n\n 9. Accepting Warranty or Additional Liability. While redistributing\n the Work or Derivative Works thereof, You may choose to offer,\n and charge a fee for, acceptance of support, warranty, indemnity,\n or other liability obligations and/or rights consistent with this\n License. However, in accepting such obligations, You may act only\n on Your own behalf and on Your sole responsibility, not on behalf\n of any other Contributor, and only if You agree to indemnify,\n defend, and hold each Contributor harmless for any liability\n incurred by, or claims asserted against, such Contributor by reason\n of your accepting any such warranty or additional liability.\n\n END OF TERMS AND CONDITIONS\n\n APPENDIX: How to apply the Apache License to your work.\n\n To apply the Apache License to your work, attach the following\n boilerplate notice, with the fields enclosed by brackets \"[]\"\n replaced with your own identifying information. (Don't include\n the brackets!) The text should be enclosed in the appropriate\n comment syntax for the file format. We also recommend that a\n file or class name and description of purpose be included on the\n same \"printed page\" as the copyright notice for easier\n identification within third-party archives.\n\n Copyright [yyyy] [name of copyright owner]\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n\n---- LLVM Exceptions to the Apache 2.0 License ----\n\nAs an exception, if, as a result of your compiling your source code, portions\nof this Software are embedded into an Object form of such source code, you\nmay redistribute such embedded portions in such Object form without complying\nwith the conditions of Sections 4(a), 4(b) and 4(d) of the License.\n\nIn addition, if you combine or link compiled forms of this Software with\nsoftware that is licensed under the GPLv2 (\"Combined Software\") and if a\ncourt of competent jurisdiction determines that the patent provision (Section\n3), the indemnity provision (Section 9) or other Section of the License\nconflicts with the conditions of the GPLv2, you may retroactively and\nprospectively choose to deem waived or otherwise exclude such Section(s) of\nthe License, but only in their entirety and only with respect to the Combined\nSoftware.\n\n==============================================================================\nSoftware from third parties included in the LLVM Project:\n==============================================================================\nThe LLVM Project contains third party software which is under different license\nterms. All such code will be identified clearly using at least one of two\nmechanisms:\n1) It will be in a separate directory tree with its own `LICENSE.txt` or\n `LICENSE` file at the top containing the specific license and restrictions\n which apply to that software, or\n2) It will contain specific license and restriction terms at the top of every\n file.\n\n==============================================================================\nLegacy LLVM License (https://llvm.org/docs/DeveloperPolicy.html#legacy):\n==============================================================================\n\nThe libunwind library is dual licensed under both the University of Illinois\n\"BSD-Like\" license and the MIT license. As a user of this code you may choose\nto use it under either license. As a contributor, you agree to allow your code\nto be used under both.\n\nFull text of the relevant licenses is included below.\n\n==============================================================================\n\nUniversity of Illinois/NCSA\nOpen Source License\n\nCopyright (c) 2009-2019 by the contributors listed in CREDITS.TXT\n\nAll rights reserved.\n\nDeveloped by:\n\n LLVM Team\n\n University of Illinois at Urbana-Champaign\n\n http://llvm.org\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal with\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies\nof the Software, and to permit persons to whom the Software is furnished to do\nso, subject to the following conditions:\n\n * Redistributions of source code must retain the above copyright notice,\n this list of conditions and the following disclaimers.\n\n * Redistributions in binary form must reproduce the above copyright notice,\n this list of conditions and the following disclaimers in the\n documentation and/or other materials provided with the distribution.\n\n * Neither the names of the LLVM Team, University of Illinois at\n Urbana-Champaign, nor the names of its contributors may be used to\n endorse or promote products derived from this Software without specific\n prior written permission.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\nFOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nCONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE\nSOFTWARE.\n\n==============================================================================\n\nCopyright (c) 2009-2014 by the contributors listed in CREDITS.TXT\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n" + }, + { + "git_blob": "776d5f1034240f6dae0ce59d592f9c6f4ad29e7d", + "path": "COPYING.MinGW-w64-runtime/COPYING.MinGW-w64-runtime.txt", + "sha256": "1db8da07b436c68833c0673ffee3d9fcb2526047f3820b81661865dfedc79a1f", + "source": "https://raw.githubusercontent.com/mingw-w64/mingw-w64/0d6dfbc348dfad714761c0a7151615767f96bcb5/COPYING.MinGW-w64-runtime/COPYING.MinGW-w64-runtime.txt", + "text": "MinGW-w64 runtime licensing\n***************************\n\nThis program or library was built using MinGW-w64 and statically\nlinked against the MinGW-w64 runtime. Some parts of the runtime\nare under licenses which require that the copyright and license\nnotices are included when distributing the code in binary form.\nThese notices are listed below.\n\n\n========================\nOverall copyright notice\n========================\n\nCopyright (c) 2009, 2010, 2011, 2012, 2013 by the mingw-w64 project\n\nThis license has been certified as open source. It has also been designated\nas GPL compatible by the Free Software Foundation (FSF).\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n 1. Redistributions in source code must retain the accompanying copyright\n notice, this list of conditions, and the following disclaimer.\n 2. Redistributions in binary form must reproduce the accompanying\n copyright notice, this list of conditions, and the following disclaimer\n in the documentation and/or other materials provided with the\n distribution.\n 3. Names of the copyright holders must not be used to endorse or promote\n products derived from this software without prior written permission\n from the copyright holders.\n 4. The right to distribute this software or to use it for any purpose does\n not give you the right to use Servicemarks (sm) or Trademarks (tm) of\n the copyright holders. Use of them is covered by separate agreement\n with the copyright holders.\n 5. If any files are modified, you must cause the modified files to carry\n prominent notices stating that you changed the files and the date of\n any change.\n\nDisclaimer\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY EXPRESSED\nOR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\nOF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO\nEVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT,\nINCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,\nOR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\nLIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\nNEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,\nEVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n======================================== \ngetopt, getopt_long, and getop_long_only\n======================================== \n\nCopyright (c) 2002 Todd C. Miller \n \nPermission to use, copy, modify, and distribute this software for any \npurpose with or without fee is hereby granted, provided that the above \ncopyright notice and this permission notice appear in all copies. \n \t \nTHE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\nWITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\nMERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\nANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\nWHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\nACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\nOR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n\nSponsored in part by the Defense Advanced Research Projects\nAgency (DARPA) and Air Force Research Laboratory, Air Force\nMateriel Command, USAF, under agreement number F39502-99-1-0512.\n\n * * * * * * * \n\nCopyright (c) 2000 The NetBSD Foundation, Inc.\nAll rights reserved.\n\nThis code is derived from software contributed to The NetBSD Foundation\nby Dieter Baron and Thomas Klausner.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions\nare met:\n 1. Redistributions of source code must retain the above copyright\n notice, this list of conditions and the following disclaimer.\n 2. Redistributions in binary form must reproduce the above copyright\n notice, this list of conditions and the following disclaimer in the\n documentation and/or other materials provided with the distribution.\n\nTHIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS\n``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED\nTO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\nPURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS\nBE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\nCONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\nSUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\nINTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\nCONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\nARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\nPOSSIBILITY OF SUCH DAMAGE.\n\n\n===============================================================\ngdtoa: Converting between IEEE floating point numbers and ASCII\n===============================================================\n\nThe author of this software is David M. Gay.\n\nCopyright (C) 1997, 1998, 1999, 2000, 2001 by Lucent Technologies\nAll Rights Reserved\n\nPermission to use, copy, modify, and distribute this software and\nits documentation for any purpose and without fee is hereby\ngranted, provided that the above copyright notice appear in all\ncopies and that both that the copyright notice and this\npermission notice and warranty disclaimer appear in supporting\ndocumentation, and that the name of Lucent or any of its entities\nnot be used in advertising or publicity pertaining to\ndistribution of the software without specific, written prior\npermission.\n\nLUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,\nINCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.\nIN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY\nSPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\nWHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER\nIN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\nARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF\nTHIS SOFTWARE.\n\n * * * * * * *\n\nThe author of this software is David M. Gay.\n\nCopyright (C) 2005 by David M. Gay\nAll Rights Reserved\n\nPermission to use, copy, modify, and distribute this software and its\ndocumentation for any purpose and without fee is hereby granted,\nprovided that the above copyright notice appear in all copies and that\nboth that the copyright notice and this permission notice and warranty\ndisclaimer appear in supporting documentation, and that the name of\nthe author or any of his current or former employers not be used in\nadvertising or publicity pertaining to distribution of the software\nwithout specific, written prior permission.\n\nTHE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,\nINCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN\nNO EVENT SHALL THE AUTHOR OR ANY OF HIS CURRENT OR FORMER EMPLOYERS BE\nLIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY\nDAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\nWHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\nARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS\nSOFTWARE.\n\n * * * * * * *\n\nThe author of this software is David M. Gay.\n\nCopyright (C) 2004 by David M. Gay.\nAll Rights Reserved\nBased on material in the rest of /netlib/fp/gdota.tar.gz,\nwhich is copyright (C) 1998, 2000 by Lucent Technologies.\n\nPermission to use, copy, modify, and distribute this software and\nits documentation for any purpose and without fee is hereby\ngranted, provided that the above copyright notice appear in all\ncopies and that both that the copyright notice and this\npermission notice and warranty disclaimer appear in supporting\ndocumentation, and that the name of Lucent or any of its entities\nnot be used in advertising or publicity pertaining to\ndistribution of the software without specific, written prior\npermission.\n\nLUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,\nINCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.\nIN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY\nSPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\nWHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER\nIN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\nARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF\nTHIS SOFTWARE.\n\n\n=========================\nParts of the math library\n=========================\n\nCopyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.\n\nDeveloped at SunSoft, a Sun Microsystems, Inc. business.\nPermission to use, copy, modify, and distribute this\nsoftware is freely granted, provided that this notice\nis preserved.\n\n * * * * * * *\n\nCopyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.\n\nDeveloped at SunPro, a Sun Microsystems, Inc. business.\nPermission to use, copy, modify, and distribute this\nsoftware is freely granted, provided that this notice\nis preserved.\n\n * * * * * * *\n\nFIXME: Cephes math lib\nCopyright (C) 1984-1998 Stephen L. Moshier\n\nIt sounds vague, but as to be found at\n, it gives an\nimpression that the author could be willing to give an explicit\npermission to distribute those files e.g. under a BSD style license. So\nprobably there is no problem here, although it could be good to get a\npermission from the author and then add a license into the Cephes files\nin MinGW runtime. At least on follow-up it is marked that debian sees the\nversion a-like BSD one. As MinGW.org (where those cephes parts are coming\nfrom) distributes them now over 6 years, it should be fine.\n\n=================================================\nSome string, memory and time conversion functions\n=================================================\n\nCopyright © 2005-2020 Rich Felker, et al.\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n\"Software\"), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n===================================\nHeaders and IDLs imported from Wine\n===================================\n\nSome header and IDL files were imported from the Wine project. These files\nare prominent maked in source. Their copyright belongs to contributors and\nthey are distributed under LGPL license.\n\nDisclaimer\n\nThis library is free software; you can redistribute it and/or\nmodify it under the terms of the GNU Lesser General Public\nLicense as published by the Free Software Foundation; either\nversion 2.1 of the License, or (at your option) any later version.\n\nThis library is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\nLesser General Public License for more details.\n" + }, + { + "git_blob": "51261a8ec4b5c5b6b074317fab1d7f506eb30425", + "path": "DISCLAIMER", + "sha256": "039ed6b4f31bb7fb97e733170b58cc435335352e4745734fcfd113877fdaf340", + "source": "https://raw.githubusercontent.com/mingw-w64/mingw-w64/0d6dfbc348dfad714761c0a7151615767f96bcb5/DISCLAIMER", + "source_revision": "0d6dfbc348dfad714761c0a7151615767f96bcb5", + "text": "/*\n This Software is provided under the Zope Public License (ZPL) Version 2.1.\n\n Copyright (c) 2009, 2010 by the mingw-w64 project\n\n See the AUTHORS file for the list of contributors to the mingw-w64 project.\n\n This license has been certified as open source. It has also been designated\n as GPL compatible by the Free Software Foundation (FSF).\n\n Redistribution and use in source and binary forms, with or without\n modification, are permitted provided that the following conditions are met:\n\n 1. Redistributions in source code must retain the accompanying copyright\n notice, this list of conditions, and the following disclaimer.\n 2. Redistributions in binary form must reproduce the accompanying\n copyright notice, this list of conditions, and the following disclaimer\n in the documentation and/or other materials provided with the\n distribution.\n 3. Names of the copyright holders must not be used to endorse or promote\n products derived from this software without prior written permission\n from the copyright holders.\n 4. The right to distribute this software or to use it for any purpose does\n not give you the right to use Servicemarks (sm) or Trademarks (tm) of\n the copyright holders. Use of them is covered by separate agreement\n with the copyright holders.\n 5. If any files are modified, you must cause the modified files to carry\n prominent notices stating that you changed the files and the date of\n any change.\n\n Disclaimer\n\n THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY EXPRESSED\n OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO\n EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT,\n INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, \n OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\n LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,\n EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n*/\n" + }, + { + "git_blob": "91fb0d6dc9d1f65394cbec575a857e635868ed74", + "path": "DISCLAIMER.PD", + "sha256": "87f33608570312a0f2a9745edcb2527275df813b13982e8c3b3f893d88a4141a", + "source": "https://raw.githubusercontent.com/mingw-w64/mingw-w64/0d6dfbc348dfad714761c0a7151615767f96bcb5/DISCLAIMER.PD", + "source_revision": "0d6dfbc348dfad714761c0a7151615767f96bcb5", + "text": "/**\n * DISCLAIMER\n * This file has no copyright assigned and is placed in the Public Domain.\n * This file is part of the mingw-w64 runtime package.\n *\n * The mingw-w64 runtime package and its code is distributed in the hope that it \n * will be useful but WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESSED OR \n * IMPLIED ARE HEREBY DISCLAIMED. This includes but is not limited to \n * warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n */\n" + }, + { + "git_blob": "4b40f2beb3cac255a09ff12616c22aa89aa9d80f", + "path": "AUTHORS", + "sha256": "8f308316e7a94b8410f871868bd3948f126526dc1775aeee97a0e365f9a0fd9f", + "source": "https://raw.githubusercontent.com/mingw-w64/mingw-w64/0d6dfbc348dfad714761c0a7151615767f96bcb5/AUTHORS", + "source_revision": "0d6dfbc348dfad714761c0a7151615767f96bcb5", + "text": "Note\n====\n\nThis file contains information about people who are permitted to make\nchanges to various parts of the runtimes, headers, associated tools,\nand associated libraries. Also the list of contributors to mingw-w64\nare listed here, too.\n\nPlease do not contact the people in this file directly to report\nproblems in mingw-w64.\n\nFor general information about mingw-w64, please visit:\n\n http://sourceforge.net/projects/mingw-w64/\n\nTo report problems in mingw-w64, please visit bug-tracker on:\n\n http://sourceforge.net/projects/mingw-w64/\n\nMaintainers\n===========\n\nMook \nNightStrike \nKai Tietz \n\nDevelopers with write after approval\n====================================\n\nkjk_hyperion \nTor Lillqvist \nAlexey Pushkin \nRoland Schwingel \nOzkan Sezer \nFridrich Strba \nChris Sutcliffe \nJonathan Yong \nJarrod Chesney \nDoug Semler \nJacek Caban \nCorinna Vinschen \nAndré Hentschel \nLiu Hao \nRuslan Garipov \n\nProject members without SVN/Git access\n==================================\n\nxenofears \n\nContributors\n============\n\nWe want to thank the following list of people for supporting us\nby contributions:\n\n20-40 \nProfessor Brian Ripley \nsduplichan \nWesley W. Terpstra \nzhou drangon \nPiotr Caban \n\nand additional thanks to the following organizations:\n\nOneVision Software AG\nReactos project\nWine project\n\nand many others, we possibly missed to mention here and are sorry for this.\n\nIf you think your name is missing and should be mentioned here, please\ncontact us for adding you.\n" + } + ], + "python_source_ref": "566bbfce6c6b48387d7a0a089939a26744013dea", + "reviewed_cabi_evidence": { + "repository": "ktav-lang/js", + "run": 41245571952, + "source_ref": "562deb3fde092d63a78f359be095e5079929d0d1", + "tooling_head": "7d8148acc7681196283bd7e63593e5aa7863a12d" + }, + "schema_version": 1, + "sdk_archive_sha256": "fd5462b7f5d93b2ff981fcd3f9c7e427e47f3203c64ea91c27bcc444eebd74cd", + "sdk_recipe_revision": "e08a31fbfe5cfdad07b698299af22d66e5608383", + "sdk_version": "20261020", + "source_files": [ + { + "git_blob": "a1179c73c5906b2df9bb3973cd7918ad1f42b28a", + "path": "libunwind/include/__libunwind_config.h", + "sha256": "0bb004911d967d5cc2d87d07f81e03668d1cf42c6b9347a95285d9e18f82954c", + "source": "https://raw.githubusercontent.com/llvm/llvm-project/7a941dfb2877a0b8cf30323fefa85ad019e0b0f5/libunwind/include/__libunwind_config.h", + "source_notices": [ + "//===----------------------------------------------------------------------===//\n//\n// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n//\n//===----------------------------------------------------------------------===//\n" + ] + }, + { + "git_blob": "6c2dfe58e53bf1f97bca6c78dacb47cc3676b8dc", + "path": "libunwind/include/libunwind.h", + "sha256": "3b411eb68de64e829937faaeec942bbc8e3e3a1c013b55107be2c679df64173c", + "source": "https://raw.githubusercontent.com/llvm/llvm-project/7a941dfb2877a0b8cf30323fefa85ad019e0b0f5/libunwind/include/libunwind.h", + "source_notices": [ + "//===----------------------------------------------------------------------===//\n//\n// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n//\n//\n// Compatible with libunwind API documented at:\n// http://www.nongnu.org/libunwind/man/libunwind(3).html\n//\n//===----------------------------------------------------------------------===//\n" + ] + }, + { + "git_blob": "c495b83c40b90ae517e390fe659061a7bbbe2603", + "path": "libunwind/include/mach-o/compact_unwind_encoding.h", + "sha256": "a458a96b1257048618e22688e3ad6fc4c36f99e1229b1492b7b800911e651a68", + "source": "https://raw.githubusercontent.com/llvm/llvm-project/7a941dfb2877a0b8cf30323fefa85ad019e0b0f5/libunwind/include/mach-o/compact_unwind_encoding.h", + "source_notices": [ + "//===----------------------------------------------------------------------===//\n//\n// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n//\n//\n// Darwin's alternative to DWARF based unwind encodings.\n//\n//===----------------------------------------------------------------------===//\n" + ] + }, + { + "git_blob": "b1775d3a3decc76e1a0916fead1aafbc6fa0fb51", + "path": "libunwind/include/unwind.h", + "sha256": "e4a58637fc8ca1d29cd600c3c4416aafed50085edf276fa076214c8d81c263f0", + "source": "https://raw.githubusercontent.com/llvm/llvm-project/7a941dfb2877a0b8cf30323fefa85ad019e0b0f5/libunwind/include/unwind.h", + "source_notices": [ + "//===----------------------------------------------------------------------===//\n//\n// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n//\n//\n// C++ ABI Level 1 ABI documented at:\n// https://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html\n//\n//===----------------------------------------------------------------------===//\n" + ] + }, + { + "git_blob": "68e02e47602d60de9f9d33267e270cb5f73fc5aa", + "path": "libunwind/include/unwind_arm_ehabi.h", + "sha256": "04cf22a069866ae35bf075c36f8d9112700bb9772653d80d9c6c4c856f4e7dbb", + "source": "https://raw.githubusercontent.com/llvm/llvm-project/7a941dfb2877a0b8cf30323fefa85ad019e0b0f5/libunwind/include/unwind_arm_ehabi.h", + "source_notices": [ + "//===----------------------------------------------------------------------===//\n//\n// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n//\n//\n// C++ ABI Level 1 ABI documented at:\n// https://github.com/ARM-software/abi-aa/blob/main/ehabi32/ehabi32.rst\n//\n//===----------------------------------------------------------------------===//\n" + ] + }, + { + "git_blob": "860ad36cdf14e2e06cd6fe2f77b9070434240434", + "path": "libunwind/include/unwind_itanium.h", + "sha256": "43ad2c8ca672c8fb9a4eb80d7e05a687ae9134f4899641357c86cecde4efbd1f", + "source": "https://raw.githubusercontent.com/llvm/llvm-project/7a941dfb2877a0b8cf30323fefa85ad019e0b0f5/libunwind/include/unwind_itanium.h", + "source_notices": [ + "//===----------------------------------------------------------------------===//\n//\n// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n//\n//\n// C++ ABI Level 1 ABI documented at:\n// https://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html\n//\n//===----------------------------------------------------------------------===//\n" + ] + }, + { + "git_blob": "ff3b5fc6fea77fba0f4ebc4d74e72bac984935fa", + "path": "libunwind/src/Unwind-EHABI.h", + "sha256": "c04fdebcd31e26de7615b82751373cc64ef83d02b24dbaaf0e112d0a559acc6b", + "source": "https://raw.githubusercontent.com/llvm/llvm-project/7a941dfb2877a0b8cf30323fefa85ad019e0b0f5/libunwind/src/Unwind-EHABI.h", + "source_notices": [ + "//===----------------------------------------------------------------------===//\n//\n// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n//\n//\n//===----------------------------------------------------------------------===//\n" + ] + }, + { + "git_blob": "0b1930b44d1c648b89099b6f2f796b4233f1c877", + "path": "libunwind/src/Unwind-seh.cpp", + "sha256": "058ae089c7d42ade35b730f87443b34018b21bea75a4ea2226e9fffdf7500160", + "source": "https://raw.githubusercontent.com/llvm/llvm-project/7a941dfb2877a0b8cf30323fefa85ad019e0b0f5/libunwind/src/Unwind-seh.cpp", + "source_notices": [ + "//===----------------------------------------------------------------------===//\n//\n// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n//\n//===----------------------------------------------------------------------===//\n//\n// Implements SEH-based Itanium C++ exceptions.\n//\n//===----------------------------------------------------------------------===//\n" + ] + }, + { + "git_blob": "1764499b304f1ac4ed665829d7cc47793ebf9766", + "path": "libunwind/src/UnwindLevel1-gcc-ext.c", + "sha256": "93d2bb050caf6b4f80819b93340ae5aad91f44e633996cc7dd32dca90da05fe0", + "source": "https://raw.githubusercontent.com/llvm/llvm-project/7a941dfb2877a0b8cf30323fefa85ad019e0b0f5/libunwind/src/UnwindLevel1-gcc-ext.c", + "source_notices": [ + "//===----------------------------------------------------------------------===//\n//\n// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n//\n//\n// Implements gcc extensions to the C++ ABI Exception Handling Level 1.\n//\n//===----------------------------------------------------------------------===//\n" + ] + }, + { + "git_blob": "834fdae6a76d3e28bde5ece20c45f367a47b1f12", + "path": "libunwind/src/UnwindLevel1.c", + "sha256": "16e16fbc0c85873df1a445d7b204fb363e46c68d6a2815dc68257108b5c944c8", + "source": "https://raw.githubusercontent.com/llvm/llvm-project/7a941dfb2877a0b8cf30323fefa85ad019e0b0f5/libunwind/src/UnwindLevel1.c", + "source_notices": [ + "//===----------------------------------------------------------------------===//\n//\n// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n//\n//\n// Implements C++ ABI Exception Handling Level 1 as documented at:\n// https://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html\n// using libunwind\n//\n//===----------------------------------------------------------------------===//\n" + ] + }, + { + "git_blob": "f44f6a1331968cf75de46a3cf64a0cc5c8f485b2", + "path": "libunwind/src/UnwindRegistersSave.S", + "sha256": "c5d0da824062363be10dc33bcd1156f16cd69811e71603c667f931a91fab5eb1", + "source": "https://raw.githubusercontent.com/llvm/llvm-project/7a941dfb2877a0b8cf30323fefa85ad019e0b0f5/libunwind/src/UnwindRegistersSave.S", + "source_notices": [ + "//===----------------------------------------------------------------------===//\n//\n// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n//\n//===----------------------------------------------------------------------===//\n" + ] + }, + { + "git_blob": "84c9d526f1d7594e456b5a31c11adff57f79551a", + "path": "libunwind/src/assembly.h", + "sha256": "1b802ab55f7b949a9e8b1a1c7c82b6a84d068610aeb823993bdeee1964c98eb3", + "source": "https://raw.githubusercontent.com/llvm/llvm-project/7a941dfb2877a0b8cf30323fefa85ad019e0b0f5/libunwind/src/assembly.h", + "source_notices": [ + "/* ===-- assembly.h - libUnwind assembler support macros -------------------===\n *\n * Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.\n * See https://llvm.org/LICENSE.txt for license information.\n * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n *\n * ===----------------------------------------------------------------------===\n *\n * This file defines macros for use in libUnwind assembler source.\n * This file is not part of the interface of this library.\n *\n * ===----------------------------------------------------------------------===\n */" + ] + }, + { + "git_blob": "f017403fa2234f7234e784eefeafe8eae6b1a59b", + "path": "libunwind/src/config.h", + "sha256": "fd99264860ced6f5b5cd4d5e0ef6eb444a4ab511a47a8b0d91bfab254ab21a1e", + "source": "https://raw.githubusercontent.com/llvm/llvm-project/7a941dfb2877a0b8cf30323fefa85ad019e0b0f5/libunwind/src/config.h", + "source_notices": [ + "//===----------------------------------------------------------------------===//\n//\n// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n//\n//\n// Defines macros used within libunwind project.\n//\n//===----------------------------------------------------------------------===//\n" + ] + }, + { + "git_blob": "68ad882347203ca193d05bcb2e3f34d4500edca0", + "path": "libunwind/src/dwarf2.h", + "sha256": "e10d240e2f19de7121a8afaf3d4841a65936723e69d636f7e3241e14802d0bfc", + "source": "https://raw.githubusercontent.com/llvm/llvm-project/7a941dfb2877a0b8cf30323fefa85ad019e0b0f5/libunwind/src/dwarf2.h", + "source_notices": [ + "//===----------------------------------------------------------------------===//\n//\n// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n//\n//===----------------------------------------------------------------------===//\n", + "/*\n These constants were taken from version 3 of the DWARF standard,\n which is Copyright (c) 2005 Free Standards Group, and\n Copyright (c) 1992, 1993 UNIX International, Inc.\n*/" + ] + }, + { + "git_blob": "0b9d56ec805bca92f0ed4b93be549c8cf982e809", + "path": "libunwind/src/libunwind.cpp", + "sha256": "51ccb27829d15988ca5678541b1b32bb3c2bdea87e02be31bf4faca48a0ee2ad", + "source": "https://raw.githubusercontent.com/llvm/llvm-project/7a941dfb2877a0b8cf30323fefa85ad019e0b0f5/libunwind/src/libunwind.cpp", + "source_notices": [ + "//===----------------------------------------------------------------------===//\n//\n// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n//\n//\n// Implements unw_* functions from \n//\n//===----------------------------------------------------------------------===//\n" + ] + }, + { + "git_blob": "8acf2212d1eae32433764070421727d73b07e372", + "path": "libunwind/src/libunwind_ext.h", + "sha256": "2efb28c93eb3c32cf491733b8e9c7b5715cb15fa0d6afb7b23ee4ca6de40cc98", + "source": "https://raw.githubusercontent.com/llvm/llvm-project/7a941dfb2877a0b8cf30323fefa85ad019e0b0f5/libunwind/src/libunwind_ext.h", + "source_notices": [ + "//===----------------------------------------------------------------------===//\n//\n// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n//\n//\n// Extensions to libunwind API.\n//\n//===----------------------------------------------------------------------===//\n" + ] + }, + { + "git_blob": "b00ca2c9327c74cc40f678cb4db3b56ed18dc405", + "path": "libunwind/src/shadow_stack_unwind.h", + "sha256": "c18056e575fcf2deed7ccfe469c1c7e6c1f390f64b28cac5aa304de3bb7a50fa", + "source": "https://raw.githubusercontent.com/llvm/llvm-project/7a941dfb2877a0b8cf30323fefa85ad019e0b0f5/libunwind/src/shadow_stack_unwind.h", + "source_notices": [ + "//===----------------------------------------------------------------------===//\n//\n// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n//\n//\n//===----------------------------------------------------------------------===//\n" + ] + }, + { + "git_blob": "26fc8ef70c024ab756b1fb1b03eed646a63a03ab", + "path": "mingw-w64-crt/cfguard/guard_dispatch.S", + "sha256": "a479e2319b9a2789afd6f91aedb3e6c640ed16b746df6babffbb17f810432811", + "source": "https://raw.githubusercontent.com/mingw-w64/mingw-w64/0d6dfbc348dfad714761c0a7151615767f96bcb5/mingw-w64-crt/cfguard/guard_dispatch.S", + "source_notices": [ + "/**\n * This file has no copyright assigned and is placed in the Public Domain.\n * This file is part of the mingw-w64 runtime package.\n * No warranty is given; refer to the file DISCLAIMER.PD within this package.\n */" + ] + }, + { + "git_blob": "5c736e162339dc590723f402380c4264601aefe3", + "path": "mingw-w64-crt/cfguard/mingw_cfguard_support.c", + "sha256": "a05b0eb6e9d6553ae934f81324fced9c3a6dc89f71ad073dad8ef174d424d693", + "source": "https://raw.githubusercontent.com/mingw-w64/mingw-w64/0d6dfbc348dfad714761c0a7151615767f96bcb5/mingw-w64-crt/cfguard/mingw_cfguard_support.c", + "source_notices": [ + "/**\n * This file has no copyright assigned and is placed in the Public Domain.\n * This file is part of the mingw-w64 runtime package.\n * No warranty is given; refer to the file DISCLAIMER.PD within this package.\n */" + ] + }, + { + "git_blob": "c8a8bfbeebcc7f325d7d1a729bc95bbf10b7d7d9", + "path": "mingw-w64-crt/crt/CRT_fp10.c", + "sha256": "fa9af6a9c58207fc596b3babe8f0b91ebbd75a9cf73e15aa849289e7eb300643", + "source": "https://raw.githubusercontent.com/mingw-w64/mingw-w64/0d6dfbc348dfad714761c0a7151615767f96bcb5/mingw-w64-crt/crt/CRT_fp10.c", + "source_notices": [ + "/**\n * This file has no copyright assigned and is placed in the Public Domain.\n * This file is part of the mingw-w64 runtime package.\n * No warranty is given; refer to the file DISCLAIMER.PD within this package.\n */" + ] + }, + { + "git_blob": "2bb5d94a279942260b3091ae7278ac6e000351c5", + "path": "mingw-w64-crt/crt/_newmode.c", + "sha256": "d85bb76f70f90b246c780ffbfbe6540a4d787b7801e98c8ac8c3edd50dcee971", + "source": "https://raw.githubusercontent.com/mingw-w64/mingw-w64/0d6dfbc348dfad714761c0a7151615767f96bcb5/mingw-w64-crt/crt/_newmode.c", + "source_notices": [ + "/**\n * This file has no copyright assigned and is placed in the Public Domain.\n * This file is part of the mingw-w64 runtime package.\n * No warranty is given; refer to the file DISCLAIMER.PD within this package.\n */" + ] + }, + { + "git_blob": "9e6931c4fc4b5dbd41d5338a9be793b170596802", + "path": "mingw-w64-crt/crt/cinitexe.c", + "sha256": "98c9238561c0ed1b3d7edfd1e32423dfda93c3ae285807873f637d03406bdcc9", + "source": "https://raw.githubusercontent.com/mingw-w64/mingw-w64/0d6dfbc348dfad714761c0a7151615767f96bcb5/mingw-w64-crt/crt/cinitexe.c", + "source_notices": [] + }, + { + "git_blob": "39c0d85610f70f746f91adc9af67ccf2480b937b", + "path": "mingw-w64-crt/crt/crtbegin.c", + "sha256": "692fdc825a472b8dfda48e65eace55caf5396c8536236b814b967007c978a8b0", + "source": "https://raw.githubusercontent.com/mingw-w64/mingw-w64/0d6dfbc348dfad714761c0a7151615767f96bcb5/mingw-w64-crt/crt/crtbegin.c", + "source_notices": [ + "/**\n * This file has no copyright assigned and is placed in the Public Domain.\n * This file is part of the mingw-w64 runtime package.\n * No warranty is given; refer to the file DISCLAIMER.PD within this package.\n */" + ] + }, + { + "git_blob": "15ee52a7c3348bdd64b7c4ecb0cbe626e6f8d092", + "path": "mingw-w64-crt/crt/crtdll.c", + "sha256": "158dcc7681f335dc36c4bcf86e41444d6507679cab80a68433666df690217990", + "source": "https://raw.githubusercontent.com/mingw-w64/mingw-w64/0d6dfbc348dfad714761c0a7151615767f96bcb5/mingw-w64-crt/crt/crtdll.c", + "source_notices": [ + "/**\n * This file has no copyright assigned and is placed in the Public Domain.\n * This file is part of the mingw-w64 runtime package.\n * No warranty is given; refer to the file DISCLAIMER.PD within this package.\n */" + ] + }, + { + "git_blob": "39c0d85610f70f746f91adc9af67ccf2480b937b", + "path": "mingw-w64-crt/crt/crtend.c", + "sha256": "692fdc825a472b8dfda48e65eace55caf5396c8536236b814b967007c978a8b0", + "source": "https://raw.githubusercontent.com/mingw-w64/mingw-w64/0d6dfbc348dfad714761c0a7151615767f96bcb5/mingw-w64-crt/crt/crtend.c", + "source_notices": [ + "/**\n * This file has no copyright assigned and is placed in the Public Domain.\n * This file is part of the mingw-w64 runtime package.\n * No warranty is given; refer to the file DISCLAIMER.PD within this package.\n */" + ] + }, + { + "git_blob": "ca9b0069160c14aca643a860386c9d5180fcbd0c", + "path": "mingw-w64-crt/crt/crtexe.c", + "sha256": "0624cd5c19dfcd4f6f17a3608582db6d0d4f92a468c391890b8f9114784a4b82", + "source": "https://raw.githubusercontent.com/mingw-w64/mingw-w64/0d6dfbc348dfad714761c0a7151615767f96bcb5/mingw-w64-crt/crt/crtexe.c", + "source_notices": [ + "/**\n * This file has no copyright assigned and is placed in the Public Domain.\n * This file is part of the mingw-w64 runtime package.\n * No warranty is given; refer to the file DISCLAIMER.PD within this package.\n */" + ] + }, + { + "git_blob": "d6be1bc7c2cf76471df995bdf7509590cd4d8f4d", + "path": "mingw-w64-crt/crt/dllargv.c", + "sha256": "98a1bf05b8a037d68ba1ea98ae40a2230a4e734196879fee4bd215c5c3283112", + "source": "https://raw.githubusercontent.com/mingw-w64/mingw-w64/0d6dfbc348dfad714761c0a7151615767f96bcb5/mingw-w64-crt/crt/dllargv.c", + "source_notices": [ + "/**\n * This file has no copyright assigned and is placed in the Public Domain.\n * This file is part of the mingw-w64 runtime package.\n * No warranty is given; refer to the file DISCLAIMER.PD within this package.\n */" + ] + }, + { + "git_blob": "7edf8360c91c182f8ec6e5a695c262332ee6c3ba", + "path": "mingw-w64-crt/crt/gccmain.c", + "sha256": "07c83e890740008bb74b26ecf60d97048cfeb74d89d973b6e8db4f437bc66ac9", + "source": "https://raw.githubusercontent.com/mingw-w64/mingw-w64/0d6dfbc348dfad714761c0a7151615767f96bcb5/mingw-w64-crt/crt/gccmain.c", + "source_notices": [ + "/**\n * This file has no copyright assigned and is placed in the Public Domain.\n * This file is part of the mingw-w64 runtime package.\n * No warranty is given; refer to the file DISCLAIMER.PD within this package.\n */" + ] + }, + { + "git_blob": "5aba73462de3018c7d35ec93d5afcc6eda8276ff", + "path": "mingw-w64-crt/crt/loadcfg.S", + "sha256": "ea3b2928757e53d5f68edbacfc271f4b6070f8a118bf44e50f6a4dd44baba545", + "source": "https://raw.githubusercontent.com/mingw-w64/mingw-w64/0d6dfbc348dfad714761c0a7151615767f96bcb5/mingw-w64-crt/crt/loadcfg.S", + "source_notices": [ + "/**\n * This file has no copyright assigned and is placed in the Public Domain.\n * This file is part of the mingw-w64 runtime package.\n * No warranty is given; refer to the file DISCLAIMER.PD within this package.\n */" + ] + }, + { + "git_blob": "687aab4a90bc7dc14ff14f86ce95136d5c1a2422", + "path": "mingw-w64-crt/crt/merr.c", + "sha256": "37d23d36e2041d5ea6e04e6674d84c227937ec477d246742fa062b5e61e1f5ea", + "source": "https://raw.githubusercontent.com/mingw-w64/mingw-w64/0d6dfbc348dfad714761c0a7151615767f96bcb5/mingw-w64-crt/crt/merr.c", + "source_notices": [ + "/**\n * This file has no copyright assigned and is placed in the Public Domain.\n * This file is part of the mingw-w64 runtime package.\n * No warranty is given; refer to the file DISCLAIMER.PD within this package.\n */" + ] + }, + { + "git_blob": "8143c6b72998473c4df1efa0654f36e59fbc2b53", + "path": "mingw-w64-crt/crt/mingw_dll_ctors_dtors.c", + "sha256": "682be7d58c77d70fdf116ad53168c893dbeaa0c85b8a9b86300493e66d3eeaae", + "source": "https://raw.githubusercontent.com/mingw-w64/mingw-w64/0d6dfbc348dfad714761c0a7151615767f96bcb5/mingw-w64-crt/crt/mingw_dll_ctors_dtors.c", + "source_notices": [ + "/**\n * This file has no copyright assigned and is placed in the Public Domain.\n * This file is part of the mingw-w64 runtime package.\n * No warranty is given; refer to the file DISCLAIMER.PD within this package.\n */" + ] + }, + { + "git_blob": "84413cdb7c9fed8e923e3620334e0f29ccf145f2", + "path": "mingw-w64-crt/crt/mingw_helpers.c", + "sha256": "a5639b68c3176c4c6a358c48f869c2a081434c07d7a0e435013f44909a2003c5", + "source": "https://raw.githubusercontent.com/mingw-w64/mingw-w64/0d6dfbc348dfad714761c0a7151615767f96bcb5/mingw-w64-crt/crt/mingw_helpers.c", + "source_notices": [ + "/**\n * This file has no copyright assigned and is placed in the Public Domain.\n * This file is part of the mingw-w64 runtime package.\n * No warranty is given; refer to the file DISCLAIMER.PD within this package.\n */" + ] + }, + { + "git_blob": "5008790e1eefed244e387f654bf4efbe5f3b2250", + "path": "mingw-w64-crt/crt/natstart.c", + "sha256": "c65fb9a3cb70536b8f757e07df8febb0b1a5fd72889bcff4fee924add1078d14", + "source": "https://raw.githubusercontent.com/mingw-w64/mingw-w64/0d6dfbc348dfad714761c0a7151615767f96bcb5/mingw-w64-crt/crt/natstart.c", + "source_notices": [ + "/**\n * This file has no copyright assigned and is placed in the Public Domain.\n * This file is part of the mingw-w64 runtime package.\n * No warranty is given; refer to the file DISCLAIMER.PD within this package.\n */" + ] + }, + { + "git_blob": "5b903c21d211138864d6b484c5b5d8c0c5df6cf3", + "path": "mingw-w64-crt/crt/pseudo-reloc-list.c", + "sha256": "853116d1ba135ea499714152be5705c72678a86fbd9bd5b90469b0ad51ab8acc", + "source": "https://raw.githubusercontent.com/mingw-w64/mingw-w64/0d6dfbc348dfad714761c0a7151615767f96bcb5/mingw-w64-crt/crt/pseudo-reloc-list.c", + "source_notices": [ + "/**\n * This file has no copyright assigned and is placed in the Public Domain.\n * This file is part of the mingw-w64 runtime package.\n * No warranty is given; refer to the file DISCLAIMER.PD within this package.\n */" + ] + }, + { + "git_blob": "dd2a28e8c2a82fbd0d00731c38e666c09738d1f3", + "path": "mingw-w64-crt/crt/pseudo-reloc.c", + "sha256": "24d57d7fec056a84268fcb3fa43dc7fc726c3c3f4c2ff4796652ca3dba33314a", + "source": "https://raw.githubusercontent.com/mingw-w64/mingw-w64/0d6dfbc348dfad714761c0a7151615767f96bcb5/mingw-w64-crt/crt/pseudo-reloc.c", + "source_notices": [ + "/* pseudo-reloc.c\n\n Contributed by Egor Duda \n Modified by addition of runtime_pseudo_reloc version 2\n by Kai Tietz \n\t\n THIS SOFTWARE IS NOT COPYRIGHTED\n\n This source code is offered for use in the public domain. You may\n use, modify or distribute it freely.\n\n This code is distributed in the hope that it will be useful but\n WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY\n DISCLAMED. This includes but is not limited to warranties of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n*/" + ] + }, + { + "git_blob": "6273a1c527116aa889d3958bf10de7306a20d2a2", + "path": "mingw-w64-crt/crt/tlssup.c", + "sha256": "3f330e9f97d39637245c74d8cd940b2e0238b50c56aee2ff579cff9314e48e9c", + "source": "https://raw.githubusercontent.com/mingw-w64/mingw-w64/0d6dfbc348dfad714761c0a7151615767f96bcb5/mingw-w64-crt/crt/tlssup.c", + "source_notices": [ + "/**\n * This file has no copyright assigned and is placed in the Public Domain.\n * This file is part of the mingw-w64 runtime package.\n * No warranty is given; refer to the file DISCLAIMER.PD within this package.\n *\n * Written by Kai Tietz \n */" + ] + }, + { + "git_blob": "fce059e14e90c9ca16425e793e43dcc803130561", + "path": "mingw-w64-crt/crt/usermatherr.c", + "sha256": "9642a86e0f68b59b958866afaca5aa602ae632d361565128237b71cff4759e48", + "source": "https://raw.githubusercontent.com/mingw-w64/mingw-w64/0d6dfbc348dfad714761c0a7151615767f96bcb5/mingw-w64-crt/crt/usermatherr.c", + "source_notices": [ + "/**\n * This file has no copyright assigned and is placed in the Public Domain.\n * This file is part of the mingw-w64 runtime package.\n * No warranty is given; refer to the file DISCLAIMER.PD within this package.\n */" + ] + }, + { + "git_blob": "b53cc18ec0c05a14962269e453b1d18b2ae1084d", + "path": "mingw-w64-crt/crt/wildcard.c", + "sha256": "a574c6324511ef40619b62a7bfdfcfd4a24a101b790074979a53fd54f9b6b363", + "source": "https://raw.githubusercontent.com/mingw-w64/mingw-w64/0d6dfbc348dfad714761c0a7151615767f96bcb5/mingw-w64-crt/crt/wildcard.c", + "source_notices": [ + "/**\n * This file has no copyright assigned and is placed in the Public Domain.\n * This file is part of the mingw-w64 runtime package.\n * No warranty is given; refer to the file DISCLAIMER.PD within this package.\n */" + ] + }, + { + "git_blob": "b2ec312f09f4f03755c048a20b29ad7454b823a8", + "path": "mingw-w64-crt/crt/xncommod.c", + "sha256": "011050a8ff748ea000d630892a16dff86ab9ffc43503befe52c1c0b79cb25479", + "source": "https://raw.githubusercontent.com/mingw-w64/mingw-w64/0d6dfbc348dfad714761c0a7151615767f96bcb5/mingw-w64-crt/crt/xncommod.c", + "source_notices": [ + "/**\n * This file has no copyright assigned and is placed in the Public Domain.\n * This file is part of the mingw-w64 runtime package.\n * No warranty is given; refer to the file DISCLAIMER.PD within this package.\n */" + ] + }, + { + "git_blob": "973ff45b16e494acf60cd8e26870460a754e9ac7", + "path": "mingw-w64-crt/crt/xthdloc.c", + "sha256": "3320df224c561ac5335121ad4e1f342b90bda7a291d0f14e89ce16c2cb17e54d", + "source": "https://raw.githubusercontent.com/mingw-w64/mingw-w64/0d6dfbc348dfad714761c0a7151615767f96bcb5/mingw-w64-crt/crt/xthdloc.c", + "source_notices": [ + "/**\n * This file has no copyright assigned and is placed in the Public Domain.\n * This file is part of the mingw-w64 runtime package.\n * No warranty is given; refer to the file DISCLAIMER.PD within this package.\n */" + ] + }, + { + "git_blob": "dcbbe11846a5d0c9cea993f7adcc87bd6a5fe958", + "path": "mingw-w64-crt/crt/xtxtmode.c", + "sha256": "a1c826ee8a440cf86dbb1ae6db80e727af5f079b5797f93a3e3a616b3db2bdb9", + "source": "https://raw.githubusercontent.com/mingw-w64/mingw-w64/0d6dfbc348dfad714761c0a7151615767f96bcb5/mingw-w64-crt/crt/xtxtmode.c", + "source_notices": [ + "/**\n * This file has no copyright assigned and is placed in the Public Domain.\n * This file is part of the mingw-w64 runtime package.\n * No warranty is given; refer to the file DISCLAIMER.PD within this package.\n */" + ] + }, + { + "git_blob": "c61e9fa8a52159f500db0e1497594b503dbcc5cd", + "path": "mingw-w64-crt/gdtoa/dmisc.c", + "sha256": "e73637f5fe5e171d13ab4ce51671f20af98c2707fe6c3ba4f25c7bcd108e0f85", + "source": "https://raw.githubusercontent.com/mingw-w64/mingw-w64/0d6dfbc348dfad714761c0a7151615767f96bcb5/mingw-w64-crt/gdtoa/dmisc.c", + "source_notices": [ + "/****************************************************************\n\nThe author of this software is David M. Gay.\n\nCopyright (C) 1998 by Lucent Technologies\nAll Rights Reserved\n\nPermission to use, copy, modify, and distribute this software and\nits documentation for any purpose and without fee is hereby\ngranted, provided that the above copyright notice appear in all\ncopies and that both that the copyright notice and this\npermission notice and warranty disclaimer appear in supporting\ndocumentation, and that the name of Lucent or any of its entities\nnot be used in advertising or publicity pertaining to\ndistribution of the software without specific, written prior\npermission.\n\nLUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,\nINCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.\nIN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY\nSPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\nWHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER\nIN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\nARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF\nTHIS SOFTWARE.\n\n****************************************************************/" + ] + }, + { + "git_blob": "1cfe1953e4c16b19dfd7a0446da5d20ec67f3de4", + "path": "mingw-w64-crt/gdtoa/gdtoa.c", + "sha256": "09ea9686def889450b16910562c5a884e87eee161c52e6c127d373688aac8559", + "source": "https://raw.githubusercontent.com/mingw-w64/mingw-w64/0d6dfbc348dfad714761c0a7151615767f96bcb5/mingw-w64-crt/gdtoa/gdtoa.c", + "source_notices": [ + "/****************************************************************\n\nThe author of this software is David M. Gay.\n\nCopyright (C) 1998, 1999 by Lucent Technologies\nAll Rights Reserved\n\nPermission to use, copy, modify, and distribute this software and\nits documentation for any purpose and without fee is hereby\ngranted, provided that the above copyright notice appear in all\ncopies and that both that the copyright notice and this\npermission notice and warranty disclaimer appear in supporting\ndocumentation, and that the name of Lucent or any of its entities\nnot be used in advertising or publicity pertaining to\ndistribution of the software without specific, written prior\npermission.\n\nLUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,\nINCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.\nIN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY\nSPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\nWHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER\nIN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\nARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF\nTHIS SOFTWARE.\n\n****************************************************************/" + ] + }, + { + "git_blob": "b2b4c54151a0da327338210940e5470cbca33f11", + "path": "mingw-w64-crt/gdtoa/gdtoa.h", + "sha256": "9ea503624739483d5a981d426a9b7a74fd2ee2e04599594e74b886d09beb0403", + "source": "https://raw.githubusercontent.com/mingw-w64/mingw-w64/0d6dfbc348dfad714761c0a7151615767f96bcb5/mingw-w64-crt/gdtoa/gdtoa.h", + "source_notices": [ + "/****************************************************************\n\nThe author of this software is David M. Gay.\n\nCopyright (C) 1998 by Lucent Technologies\nAll Rights Reserved\n\nPermission to use, copy, modify, and distribute this software and\nits documentation for any purpose and without fee is hereby\ngranted, provided that the above copyright notice appear in all\ncopies and that both that the copyright notice and this\npermission notice and warranty disclaimer appear in supporting\ndocumentation, and that the name of Lucent or any of its entities\nnot be used in advertising or publicity pertaining to\ndistribution of the software without specific, written prior\npermission.\n\nLUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,\nINCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.\nIN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY\nSPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\nWHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER\nIN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\nARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF\nTHIS SOFTWARE.\n\n****************************************************************/" + ] + }, + { + "git_blob": "cc2007cd5fa3f0654b14acee99db363d515c44ad", + "path": "mingw-w64-crt/gdtoa/gdtoaimp.h", + "sha256": "cd4e3796364b4eb9d9c5cf3ff00d3abc8e114102376454bc1d2413183ca3e44d", + "source": "https://raw.githubusercontent.com/mingw-w64/mingw-w64/0d6dfbc348dfad714761c0a7151615767f96bcb5/mingw-w64-crt/gdtoa/gdtoaimp.h", + "source_notices": [ + "/****************************************************************\n\nThe author of this software is David M. Gay.\n\nCopyright (C) 1998-2000 by Lucent Technologies\nAll Rights Reserved\n\nPermission to use, copy, modify, and distribute this software and\nits documentation for any purpose and without fee is hereby\ngranted, provided that the above copyright notice appear in all\ncopies and that both that the copyright notice and this\npermission notice and warranty disclaimer appear in supporting\ndocumentation, and that the name of Lucent or any of its entities\nnot be used in advertising or publicity pertaining to\ndistribution of the software without specific, written prior\npermission.\n\nLUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,\nINCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.\nIN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY\nSPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\nWHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER\nIN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\nARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF\nTHIS SOFTWARE.\n\n****************************************************************/" + ] + }, + { + "git_blob": "157e872319f033f3fcc838c26a93b7fc8b9c9826", + "path": "mingw-w64-crt/gdtoa/gmisc.c", + "sha256": "7b4327dfc978aacfb950bd4c6970696ccc76ddb7159800a244ef2a0bb593dc5c", + "source": "https://raw.githubusercontent.com/mingw-w64/mingw-w64/0d6dfbc348dfad714761c0a7151615767f96bcb5/mingw-w64-crt/gdtoa/gmisc.c", + "source_notices": [ + "/****************************************************************\n\nThe author of this software is David M. Gay.\n\nCopyright (C) 1998 by Lucent Technologies\nAll Rights Reserved\n\nPermission to use, copy, modify, and distribute this software and\nits documentation for any purpose and without fee is hereby\ngranted, provided that the above copyright notice appear in all\ncopies and that both that the copyright notice and this\npermission notice and warranty disclaimer appear in supporting\ndocumentation, and that the name of Lucent or any of its entities\nnot be used in advertising or publicity pertaining to\ndistribution of the software without specific, written prior\npermission.\n\nLUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,\nINCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.\nIN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY\nSPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\nWHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER\nIN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\nARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF\nTHIS SOFTWARE.\n\n****************************************************************/" + ] + }, + { + "git_blob": "960543a606eeda45b24a4886e6149963fe6bae79", + "path": "mingw-w64-crt/gdtoa/misc.c", + "sha256": "02486c21b11fe4538bde37d4c740b486a5eef76c8fdeec8e74a9c409c8acb6dc", + "source": "https://raw.githubusercontent.com/mingw-w64/mingw-w64/0d6dfbc348dfad714761c0a7151615767f96bcb5/mingw-w64-crt/gdtoa/misc.c", + "source_notices": [ + "/****************************************************************\n\nThe author of this software is David M. Gay.\n\nCopyright (C) 1998, 1999 by Lucent Technologies\nAll Rights Reserved\n\nPermission to use, copy, modify, and distribute this software and\nits documentation for any purpose and without fee is hereby\ngranted, provided that the above copyright notice appear in all\ncopies and that both that the copyright notice and this\npermission notice and warranty disclaimer appear in supporting\ndocumentation, and that the name of Lucent or any of its entities\nnot be used in advertising or publicity pertaining to\ndistribution of the software without specific, written prior\npermission.\n\nLUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,\nINCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.\nIN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY\nSPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\nWHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER\nIN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\nARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF\nTHIS SOFTWARE.\n\n****************************************************************/" + ] + }, + { + "git_blob": "30b11f284226c16e8e5320e4b3d164cc4a3c425d", + "path": "mingw-w64-crt/misc/__initenv.c", + "sha256": "7ca289f2891fef05351cc52879413c219b30de0258b950dbc7d4e10d05a01f97", + "source": "https://raw.githubusercontent.com/mingw-w64/mingw-w64/0d6dfbc348dfad714761c0a7151615767f96bcb5/mingw-w64-crt/misc/__initenv.c", + "source_notices": [ + "/**\n * This file has no copyright assigned and is placed in the Public Domain.\n * This file is part of the mingw-w64 runtime package.\n * No warranty is given; refer to the file DISCLAIMER.PD within this package.\n */" + ] + }, + { + "git_blob": "e3f880b38408689890dff6091f2fe1ef831f8c46", + "path": "mingw-w64-crt/misc/__p___initenv.c", + "sha256": "034d66df82c6da68cc4bc59ff829ff3f18edd431cbe2f6376d51667f9b90f509", + "source": "https://raw.githubusercontent.com/mingw-w64/mingw-w64/0d6dfbc348dfad714761c0a7151615767f96bcb5/mingw-w64-crt/misc/__p___initenv.c", + "source_notices": [ + "/**\n * This file has no copyright assigned and is placed in the Public Domain.\n * This file is part of the mingw-w64 runtime package.\n * No warranty is given; refer to the file DISCLAIMER.PD within this package.\n */" + ] + }, + { + "git_blob": "3f5b5261bffae5b63826829a9103d290d7bf5708", + "path": "mingw-w64-crt/misc/dllmain.c", + "sha256": "ad3eb745bc2e614de38f43700c274cae2b7edec7ee40950cc918d8b98d3d9505", + "source": "https://raw.githubusercontent.com/mingw-w64/mingw-w64/0d6dfbc348dfad714761c0a7151615767f96bcb5/mingw-w64-crt/misc/dllmain.c", + "source_notices": [] + }, + { + "git_blob": "21529682983729e6197d22fe532d974ca113fcf2", + "path": "mingw-w64-crt/misc/mingw_matherr.c", + "sha256": "47ccdea07abacdd4629596084796291ecf35f4ccf951fa6b5038b6db2eb4c336", + "source": "https://raw.githubusercontent.com/mingw-w64/mingw-w64/0d6dfbc348dfad714761c0a7151615767f96bcb5/mingw-w64-crt/misc/mingw_matherr.c", + "source_notices": [] + }, + { + "git_blob": "72fef92cc36e674db13500d74dee10a27a060631", + "path": "mingw-w64-crt/misc/ucrt__getmainargs.c", + "sha256": "7d9139300468218e77c1f4b1bc52ba2802b9273ad1f1b1c3e50be624039d2f5e", + "source": "https://raw.githubusercontent.com/mingw-w64/mingw-w64/0d6dfbc348dfad714761c0a7151615767f96bcb5/mingw-w64-crt/misc/ucrt__getmainargs.c", + "source_notices": [ + "/**\n * This file has no copyright assigned and is placed in the Public Domain.\n * This file is part of the mingw-w64 runtime package.\n * No warranty is given; refer to the file DISCLAIMER.PD within this package.\n */" + ] + }, + { + "git_blob": "0aad4f6c368a7f284568ffb1e96c8e48f011e56a", + "path": "mingw-w64-crt/misc/ucrt_amsg_exit.c", + "sha256": "ad8dd7eb0b4e2fef5b5c16f4934b7f5bca265246e18440ecb380ab8ffe1a8c6d", + "source": "https://raw.githubusercontent.com/mingw-w64/mingw-w64/0d6dfbc348dfad714761c0a7151615767f96bcb5/mingw-w64-crt/misc/ucrt_amsg_exit.c", + "source_notices": [ + "/**\n * This file has no copyright assigned and is placed in the Public Domain.\n * This file is part of the mingw-w64 runtime package.\n * No warranty is given; refer to the file DISCLAIMER.PD within this package.\n */" + ] + }, + { + "git_blob": "438941f7e3f473462007cffc12d967c88bf315e9", + "path": "mingw-w64-crt/stdio/mingw_fprintf.c", + "sha256": "c19ffaac9e0ee6056bf4cd0316d021596b37ac800d04dd187b01db01533d839e", + "source": "https://raw.githubusercontent.com/mingw-w64/mingw-w64/0d6dfbc348dfad714761c0a7151615767f96bcb5/mingw-w64-crt/stdio/mingw_fprintf.c", + "source_notices": [ + "/* fprintf.c\n *\n * $Id: fprintf.c,v 1.1 2008/08/11 22:41:55 keithmarshall Exp $\n *\n * Provides an implementation of the \"fprintf\" function, conforming\n * generally to C99 and SUSv3/POSIX specifications, with extensions\n * to support Microsoft's non-standard format specifications. This\n * is included in libmingwex.a, whence it may replace the Microsoft\n * function of the same name.\n *\n * Written by Keith Marshall \n *\n * This implementation of \"fprintf\" will normally be invoked by calling\n * \"__mingw_fprintf()\" in preference to a direct reference to \"fprintf()\"\n * itself; this leaves the MSVCRT implementation as the default, which\n * will be deployed when user code invokes \"fprint()\". Users who then\n * wish to use this implementation may either call \"__mingw_fprintf()\"\n * directly, or may use conditional preprocessor defines, to redirect\n * references to \"fprintf()\" to \"__mingw_fprintf()\".\n *\n * Compiling this module with \"-D INSTALL_AS_DEFAULT\" will change this\n * recommended convention, such that references to \"fprintf()\" in user\n * code will ALWAYS be redirected to \"__mingw_fprintf()\"; if this option\n * is adopted, then users wishing to use the MSVCRT implementation of\n * \"fprintf()\" will be forced to use a \"back-door\" mechanism to do so.\n * Such a \"back-door\" mechanism is provided with MinGW, allowing the\n * MSVCRT implementation to be called as \"__msvcrt_fprintf()\"; however,\n * since users may not expect this behaviour, a standard libmingwex.a\n * installation does not employ this option.\n *\n *\n * This is free software. You may redistribute and/or modify it as you\n * see fit, without restriction of copyright.\n *\n * This software is provided \"as is\", in the hope that it may be useful,\n * but WITHOUT WARRANTY OF ANY KIND, not even any implied warranty of\n * MERCHANTABILITY, nor of FITNESS FOR ANY PARTICULAR PURPOSE. At no\n * time will the author accept any form of liability for any damages,\n * however caused, resulting from the use of this software.\n *\n */" + ] + }, + { + "git_blob": "2bbe5203b52fe37cd977c6c11bc11c7924dab2ae", + "path": "mingw-w64-crt/stdio/mingw_pformat.c", + "sha256": "8ad4e2a80cb56291deb8ab42238c5084fa256a65dbf08412109f08c3ced85225", + "source": "https://raw.githubusercontent.com/mingw-w64/mingw-w64/0d6dfbc348dfad714761c0a7151615767f96bcb5/mingw-w64-crt/stdio/mingw_pformat.c", + "source_notices": [ + "/* pformat.c\n *\n * $Id: pformat.c,v 1.9 2011/01/07 22:57:00 keithmarshall Exp $\n *\n * Provides a core implementation of the formatting capabilities\n * common to the entire `printf()' family of functions; it conforms\n * generally to C99 and SUSv3/POSIX specifications, with extensions\n * to support Microsoft's non-standard format specifications.\n *\n * Written by Keith Marshall \n *\n * This is free software. You may redistribute and/or modify it as you\n * see fit, without restriction of copyright.\n *\n * This software is provided \"as is\", in the hope that it may be useful,\n * but WITHOUT WARRANTY OF ANY KIND, not even any implied warranty of\n * MERCHANTABILITY, nor of FITNESS FOR ANY PARTICULAR PURPOSE. At no\n * time will the author accept any form of liability for any damages,\n * however caused, resulting from the use of this software.\n *\n * The elements of this implementation which deal with the formatting\n * of floating point numbers, (i.e. the `%e', `%E', `%f', `%F', `%g'\n * and `%G' format specifiers, but excluding the hexadecimal floating\n * point `%a' and `%A' specifiers), make use of the `__gdtoa' function\n * written by David M. Gay, and are modelled on his sample code, which\n * has been deployed under its accompanying terms of use:--\n *\n ******************************************************************\n * Copyright (C) 1997, 1999, 2001 Lucent Technologies\n * All Rights Reserved\n *\n * Permission to use, copy, modify, and distribute this software and\n * its documentation for any purpose and without fee is hereby\n * granted, provided that the above copyright notice appear in all\n * copies and that both that the copyright notice and this\n * permission notice and warranty disclaimer appear in supporting\n * documentation, and that the name of Lucent or any of its entities\n * not be used in advertising or publicity pertaining to\n * distribution of the software without specific, written prior\n * permission.\n *\n * LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,\n * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.\n * IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY\n * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER\n * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\n * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF\n * THIS SOFTWARE.\n ******************************************************************\n *\n */" + ] + }, + { + "git_blob": "ee98ac1ac827877eaa8e5de8a675bdc9dcced18c", + "path": "mingw-w64-crt/stdio/ucrt___local_stdio_printf_options.c", + "sha256": "0c8ab6443db6f80930f95ee4f380faf7e0bd7a04ecafa0fcba8016bd77a0c98c", + "source": "https://raw.githubusercontent.com/mingw-w64/mingw-w64/0d6dfbc348dfad714761c0a7151615767f96bcb5/mingw-w64-crt/stdio/ucrt___local_stdio_printf_options.c", + "source_notices": [ + "/**\n * This file has no copyright assigned and is placed in the Public Domain.\n * This file is part of the mingw-w64 runtime package.\n * No warranty is given; refer to the file DISCLAIMER.PD within this package.\n */" + ] + }, + { + "git_blob": "0b06b14e9653a4dca58436e30cc2ad2feb9a5f2b", + "path": "mingw-w64-crt/stdio/ucrt_fprintf.c", + "sha256": "080d99fcea70a61cde96b5347e8a381c28778928584227cb6b70a543c3b0933c", + "source": "https://raw.githubusercontent.com/mingw-w64/mingw-w64/0d6dfbc348dfad714761c0a7151615767f96bcb5/mingw-w64-crt/stdio/ucrt_fprintf.c", + "source_notices": [ + "/**\n * This file has no copyright assigned and is placed in the Public Domain.\n * This file is part of the mingw-w64 runtime package.\n * No warranty is given; refer to the file DISCLAIMER.PD within this package.\n */" + ] + }, + { + "git_blob": "6ac1bb8c115a5cbf82d3d1a364fc3460eeb47380", + "path": "mingw-w64-crt/stdio/ucrt_vfprintf.c", + "sha256": "682fc042d9007afcfa6c022373178d22591af8c9cb93de9ab5525b3d17cd5b75", + "source": "https://raw.githubusercontent.com/mingw-w64/mingw-w64/0d6dfbc348dfad714761c0a7151615767f96bcb5/mingw-w64-crt/stdio/ucrt_vfprintf.c", + "source_notices": [ + "/**\n * This file has no copyright assigned and is placed in the Public Domain.\n * This file is part of the mingw-w64 runtime package.\n * No warranty is given; refer to the file DISCLAIMER.PD within this package.\n */" + ] + }, + { + "git_blob": "06c708316d72bda920940cbf8b0f735aa0abd729", + "path": "mingw-w64-crt/crt/crtdll_atexit_register.c", + "sha256": "53268a62ba7b80f6bda1bca04a5a43affddb8799c8db512b2051696af1192e08", + "source": "https://raw.githubusercontent.com/mingw-w64/mingw-w64/0d6dfbc348dfad714761c0a7151615767f96bcb5/mingw-w64-crt/crt/crtdll_atexit_register.c", + "source_notices": [ + "/**\n * This file has no copyright assigned and is placed in the Public Domain.\n * This file is part of the mingw-w64 runtime package.\n * No warranty is given; refer to the file DISCLAIMER.PD within this package.\n */" + ] + } + ], + "targets": { + "aarch64-pc-windows-gnullvm": { + "cpython_import_library": { + "bytes": 194246, + "file": "python3.lib", + "import_kinds": { + "data": 143, + "func": 759 + }, + "import_records": 902, + "modification_summary": "No changes to Python source or its stable-ABI declarations. Ktav uses the installed CPython stable-ABI import library; the CPython interpreter and its implementation/runtime DLLs are not redistributed in this wheel.", + "ordinary_non_executable_metadata_members": [ + [ + { + "bytes": 65, + "name": ".debug$S" + }, + { + "bytes": 20, + "name": ".idata$2" + }, + { + "bytes": 12, + "name": ".idata$6" + } + ], + [ + { + "bytes": 65, + "name": ".debug$S" + }, + { + "bytes": 20, + "name": ".idata$3" + } + ], + [ + { + "bytes": 65, + "name": ".debug$S" + }, + { + "bytes": 8, + "name": ".idata$5" + }, + { + "bytes": 8, + "name": ".idata$4" + } + ] + ], + "original_materials": [ + { + "bytes": 13809, + "commit": "64e080d38154391ff2d2caa1161c298bb7cd8f30", + "git_blob": "14603b95c2e23b5a4cd6ff93f50348d1817f7638", + "path": "LICENSE", + "sha256": "78b12c3a81360b357002334f0e70ea0e92eebf7a9b358805c03c48484945f3bb", + "source": "https://raw.githubusercontent.com/python/cpython/64e080d38154391ff2d2caa1161c298bb7cd8f30/LICENSE", + "text": "A. HISTORY OF THE SOFTWARE\n==========================\n\nPython was created in the early 1990s by Guido van Rossum at Stichting\nMathematisch Centrum (CWI, see https://www.cwi.nl) in the Netherlands\nas a successor of a language called ABC. Guido remains Python's\nprincipal author, although it includes many contributions from others.\n\nIn 1995, Guido continued his work on Python at the Corporation for\nNational Research Initiatives (CNRI, see https://www.cnri.reston.va.us)\nin Reston, Virginia where he released several versions of the\nsoftware.\n\nIn May 2000, Guido and the Python core development team moved to\nBeOpen.com to form the BeOpen PythonLabs team. In October of the same\nyear, the PythonLabs team moved to Digital Creations, which became\nZope Corporation. In 2001, the Python Software Foundation (PSF, see\nhttps://www.python.org/psf/) was formed, a non-profit organization\ncreated specifically to own Python-related Intellectual Property.\nZope Corporation was a sponsoring member of the PSF.\n\nAll Python releases are Open Source (see https://opensource.org for\nthe Open Source Definition). Historically, most, but not all, Python\nreleases have also been GPL-compatible; the table below summarizes\nthe various releases.\n\n Release Derived Year Owner GPL-\n from compatible? (1)\n\n 0.9.0 thru 1.2 1991-1995 CWI yes\n 1.3 thru 1.5.2 1.2 1995-1999 CNRI yes\n 1.6 1.5.2 2000 CNRI no\n 2.0 1.6 2000 BeOpen.com no\n 1.6.1 1.6 2001 CNRI yes (2)\n 2.1 2.0+1.6.1 2001 PSF no\n 2.0.1 2.0+1.6.1 2001 PSF yes\n 2.1.1 2.1+2.0.1 2001 PSF yes\n 2.1.2 2.1.1 2002 PSF yes\n 2.1.3 2.1.2 2002 PSF yes\n 2.2 and above 2.1.1 2001-now PSF yes\n\nFootnotes:\n\n(1) GPL-compatible doesn't mean that we're distributing Python under\n the GPL. All Python licenses, unlike the GPL, let you distribute\n a modified version without making your changes open source. The\n GPL-compatible licenses make it possible to combine Python with\n other software that is released under the GPL; the others don't.\n\n(2) According to Richard Stallman, 1.6.1 is not GPL-compatible,\n because its license has a choice of law clause. According to\n CNRI, however, Stallman's lawyer has told CNRI's lawyer that 1.6.1\n is \"not incompatible\" with the GPL.\n\nThanks to the many outside volunteers who have worked under Guido's\ndirection to make these releases possible.\n\n\nB. TERMS AND CONDITIONS FOR ACCESSING OR OTHERWISE USING PYTHON\n===============================================================\n\nPython software and documentation are licensed under the\nPython Software Foundation License Version 2.\n\nStarting with Python 3.8.6, examples, recipes, and other code in\nthe documentation are dual licensed under the PSF License Version 2\nand the Zero-Clause BSD license.\n\nSome software incorporated into Python is under different licenses.\nThe licenses are listed with code falling under that license.\n\n\nPYTHON SOFTWARE FOUNDATION LICENSE VERSION 2\n--------------------------------------------\n\n1. This LICENSE AGREEMENT is between the Python Software Foundation\n(\"PSF\"), and the Individual or Organization (\"Licensee\") accessing and\notherwise using this software (\"Python\") in source or binary form and\nits associated documentation.\n\n2. Subject to the terms and conditions of this License Agreement, PSF hereby\ngrants Licensee a nonexclusive, royalty-free, world-wide license to reproduce,\nanalyze, test, perform and/or display publicly, prepare derivative works,\ndistribute, and otherwise use Python alone or in any derivative version,\nprovided, however, that PSF's License Agreement and PSF's notice of copyright,\ni.e., \"Copyright (c) 2001-2024 Python Software Foundation; All Rights Reserved\"\nare retained in Python alone or in any derivative version prepared by Licensee.\n\n3. In the event Licensee prepares a derivative work that is based on\nor incorporates Python or any part thereof, and wants to make\nthe derivative work available to others as provided herein, then\nLicensee hereby agrees to include in any such work a brief summary of\nthe changes made to Python.\n\n4. PSF is making Python available to Licensee on an \"AS IS\"\nbasis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR\nIMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND\nDISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS\nFOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT\nINFRINGE ANY THIRD PARTY RIGHTS.\n\n5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON\nFOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS\nA RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON,\nOR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.\n\n6. This License Agreement will automatically terminate upon a material\nbreach of its terms and conditions.\n\n7. Nothing in this License Agreement shall be deemed to create any\nrelationship of agency, partnership, or joint venture between PSF and\nLicensee. This License Agreement does not grant permission to use PSF\ntrademarks or trade name in a trademark sense to endorse or promote\nproducts or services of Licensee, or any third party.\n\n8. By copying, installing or otherwise using Python, Licensee\nagrees to be bound by the terms and conditions of this License\nAgreement.\n\n\nBEOPEN.COM LICENSE AGREEMENT FOR PYTHON 2.0\n-------------------------------------------\n\nBEOPEN PYTHON OPEN SOURCE LICENSE AGREEMENT VERSION 1\n\n1. This LICENSE AGREEMENT is between BeOpen.com (\"BeOpen\"), having an\noffice at 160 Saratoga Avenue, Santa Clara, CA 95051, and the\nIndividual or Organization (\"Licensee\") accessing and otherwise using\nthis software in source or binary form and its associated\ndocumentation (\"the Software\").\n\n2. Subject to the terms and conditions of this BeOpen Python License\nAgreement, BeOpen hereby grants Licensee a non-exclusive,\nroyalty-free, world-wide license to reproduce, analyze, test, perform\nand/or display publicly, prepare derivative works, distribute, and\notherwise use the Software alone or in any derivative version,\nprovided, however, that the BeOpen Python License is retained in the\nSoftware, alone or in any derivative version prepared by Licensee.\n\n3. BeOpen is making the Software available to Licensee on an \"AS IS\"\nbasis. BEOPEN MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR\nIMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, BEOPEN MAKES NO AND\nDISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS\nFOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE WILL NOT\nINFRINGE ANY THIRD PARTY RIGHTS.\n\n4. BEOPEN SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE\nSOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS\nAS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY\nDERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.\n\n5. This License Agreement will automatically terminate upon a material\nbreach of its terms and conditions.\n\n6. This License Agreement shall be governed by and interpreted in all\nrespects by the law of the State of California, excluding conflict of\nlaw provisions. Nothing in this License Agreement shall be deemed to\ncreate any relationship of agency, partnership, or joint venture\nbetween BeOpen and Licensee. This License Agreement does not grant\npermission to use BeOpen trademarks or trade names in a trademark\nsense to endorse or promote products or services of Licensee, or any\nthird party. As an exception, the \"BeOpen Python\" logos available at\nhttp://www.pythonlabs.com/logos.html may be used according to the\npermissions granted on that web page.\n\n7. By copying, installing or otherwise using the software, Licensee\nagrees to be bound by the terms and conditions of this License\nAgreement.\n\n\nCNRI LICENSE AGREEMENT FOR PYTHON 1.6.1\n---------------------------------------\n\n1. This LICENSE AGREEMENT is between the Corporation for National\nResearch Initiatives, having an office at 1895 Preston White Drive,\nReston, VA 20191 (\"CNRI\"), and the Individual or Organization\n(\"Licensee\") accessing and otherwise using Python 1.6.1 software in\nsource or binary form and its associated documentation.\n\n2. Subject to the terms and conditions of this License Agreement, CNRI\nhereby grants Licensee a nonexclusive, royalty-free, world-wide\nlicense to reproduce, analyze, test, perform and/or display publicly,\nprepare derivative works, distribute, and otherwise use Python 1.6.1\nalone or in any derivative version, provided, however, that CNRI's\nLicense Agreement and CNRI's notice of copyright, i.e., \"Copyright (c)\n1995-2001 Corporation for National Research Initiatives; All Rights\nReserved\" are retained in Python 1.6.1 alone or in any derivative\nversion prepared by Licensee. Alternately, in lieu of CNRI's License\nAgreement, Licensee may substitute the following text (omitting the\nquotes): \"Python 1.6.1 is made available subject to the terms and\nconditions in CNRI's License Agreement. This Agreement together with\nPython 1.6.1 may be located on the internet using the following\nunique, persistent identifier (known as a handle): 1895.22/1013. This\nAgreement may also be obtained from a proxy server on the internet\nusing the following URL: http://hdl.handle.net/1895.22/1013\".\n\n3. In the event Licensee prepares a derivative work that is based on\nor incorporates Python 1.6.1 or any part thereof, and wants to make\nthe derivative work available to others as provided herein, then\nLicensee hereby agrees to include in any such work a brief summary of\nthe changes made to Python 1.6.1.\n\n4. CNRI is making Python 1.6.1 available to Licensee on an \"AS IS\"\nbasis. CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR\nIMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND\nDISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS\nFOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 1.6.1 WILL NOT\nINFRINGE ANY THIRD PARTY RIGHTS.\n\n5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON\n1.6.1 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS\nA RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 1.6.1,\nOR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.\n\n6. This License Agreement will automatically terminate upon a material\nbreach of its terms and conditions.\n\n7. This License Agreement shall be governed by the federal\nintellectual property law of the United States, including without\nlimitation the federal copyright law, and, to the extent such\nU.S. federal law does not apply, by the law of the Commonwealth of\nVirginia, excluding Virginia's conflict of law provisions.\nNotwithstanding the foregoing, with regard to derivative works based\non Python 1.6.1 that incorporate non-separable material that was\npreviously distributed under the GNU General Public License (GPL), the\nlaw of the Commonwealth of Virginia shall govern this License\nAgreement only as to issues arising under or with respect to\nParagraphs 4, 5, and 7 of this License Agreement. Nothing in this\nLicense Agreement shall be deemed to create any relationship of\nagency, partnership, or joint venture between CNRI and Licensee. This\nLicense Agreement does not grant permission to use CNRI trademarks or\ntrade name in a trademark sense to endorse or promote products or\nservices of Licensee, or any third party.\n\n8. By clicking on the \"ACCEPT\" button where indicated, or by copying,\ninstalling or otherwise using Python 1.6.1, Licensee agrees to be\nbound by the terms and conditions of this License Agreement.\n\n ACCEPT\n\n\nCWI LICENSE AGREEMENT FOR PYTHON 0.9.0 THROUGH 1.2\n--------------------------------------------------\n\nCopyright (c) 1991 - 1995, Stichting Mathematisch Centrum Amsterdam,\nThe Netherlands. All rights reserved.\n\nPermission to use, copy, modify, and distribute this software and its\ndocumentation for any purpose and without fee is hereby granted,\nprovided that the above copyright notice appear in all copies and that\nboth that copyright notice and this permission notice appear in\nsupporting documentation, and that the name of Stichting Mathematisch\nCentrum or CWI not be used in advertising or publicity pertaining to\ndistribution of the software without specific, written prior\npermission.\n\nSTICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO\nTHIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND\nFITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE\nFOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\nWHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\nACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT\nOF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n\nZERO-CLAUSE BSD LICENSE FOR CODE IN THE PYTHON DOCUMENTATION\n----------------------------------------------------------------------\n\nPermission to use, copy, modify, and/or distribute this software for any\npurpose with or without fee is hereby granted.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH\nREGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY\nAND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,\nINDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM\nLOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR\nOTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR\nPERFORMANCE OF THIS SOFTWARE.\n", + "version": "3.13.17" + }, + { + "bytes": 30185, + "commit": "64e080d38154391ff2d2caa1161c298bb7cd8f30", + "git_blob": "eb077df70a57d6aa0853d927b86b55826a07ee50", + "path": "PC/python3dll.c", + "sha256": "73614c8ef518ba5c13c2867caeb280e12834af64ee7965b3707dfea2d4de5fcf", + "source": "https://raw.githubusercontent.com/python/cpython/64e080d38154391ff2d2caa1161c298bb7cd8f30/PC/python3dll.c", + "text": "/* Re-export stable Python ABI */\n\n/* Generated by Tools/build/stable_abi.py */\n\n#ifdef _M_IX86\n#define DECORATE \"_\"\n#else\n#define DECORATE\n#endif\n\n#define EXPORT_FUNC(name) \\\n __pragma(comment(linker, \"/EXPORT:\" DECORATE #name \"=\" PYTHON_DLL_NAME \".\" #name))\n#define EXPORT_DATA(name) \\\n __pragma(comment(linker, \"/EXPORT:\" DECORATE #name \"=\" PYTHON_DLL_NAME \".\" #name \",DATA\"))\n\nEXPORT_FUNC(_Py_BuildValue_SizeT)\nEXPORT_FUNC(_Py_CheckRecursiveCall)\nEXPORT_FUNC(_Py_Dealloc)\nEXPORT_FUNC(_Py_DecRef)\nEXPORT_FUNC(_Py_IncRef)\nEXPORT_FUNC(_Py_NegativeRefcount)\nEXPORT_FUNC(_Py_SetRefcnt)\nEXPORT_FUNC(_Py_VaBuildValue_SizeT)\nEXPORT_FUNC(_PyArg_Parse_SizeT)\nEXPORT_FUNC(_PyArg_ParseTuple_SizeT)\nEXPORT_FUNC(_PyArg_ParseTupleAndKeywords_SizeT)\nEXPORT_FUNC(_PyArg_VaParse_SizeT)\nEXPORT_FUNC(_PyArg_VaParseTupleAndKeywords_SizeT)\nEXPORT_FUNC(_PyErr_BadInternalCall)\nEXPORT_FUNC(_PyObject_CallFunction_SizeT)\nEXPORT_FUNC(_PyObject_CallMethod_SizeT)\nEXPORT_FUNC(_PyObject_GC_New)\nEXPORT_FUNC(_PyObject_GC_NewVar)\nEXPORT_FUNC(_PyObject_GC_Resize)\nEXPORT_FUNC(_PyObject_New)\nEXPORT_FUNC(_PyObject_NewVar)\nEXPORT_FUNC(_PyState_AddModule)\nEXPORT_FUNC(_PyThreadState_Init)\nEXPORT_FUNC(_PyThreadState_Prealloc)\nEXPORT_FUNC(Py_AddPendingCall)\nEXPORT_FUNC(Py_AtExit)\nEXPORT_FUNC(Py_BuildValue)\nEXPORT_FUNC(Py_BytesMain)\nEXPORT_FUNC(Py_CompileString)\nEXPORT_FUNC(Py_DecodeLocale)\nEXPORT_FUNC(Py_DecRef)\nEXPORT_FUNC(Py_EncodeLocale)\nEXPORT_FUNC(Py_EndInterpreter)\nEXPORT_FUNC(Py_EnterRecursiveCall)\nEXPORT_FUNC(Py_Exit)\nEXPORT_FUNC(Py_FatalError)\nEXPORT_FUNC(Py_Finalize)\nEXPORT_FUNC(Py_FinalizeEx)\nEXPORT_FUNC(Py_GenericAlias)\nEXPORT_FUNC(Py_GetArgcArgv)\nEXPORT_FUNC(Py_GetBuildInfo)\nEXPORT_FUNC(Py_GetCompiler)\nEXPORT_FUNC(Py_GetConstant)\nEXPORT_FUNC(Py_GetConstantBorrowed)\nEXPORT_FUNC(Py_GetCopyright)\nEXPORT_FUNC(Py_GetExecPrefix)\nEXPORT_FUNC(Py_GetPath)\nEXPORT_FUNC(Py_GetPlatform)\nEXPORT_FUNC(Py_GetPrefix)\nEXPORT_FUNC(Py_GetProgramFullPath)\nEXPORT_FUNC(Py_GetProgramName)\nEXPORT_FUNC(Py_GetPythonHome)\nEXPORT_FUNC(Py_GetRecursionLimit)\nEXPORT_FUNC(Py_GetVersion)\nEXPORT_FUNC(Py_IncRef)\nEXPORT_FUNC(Py_Initialize)\nEXPORT_FUNC(Py_InitializeEx)\nEXPORT_FUNC(Py_Is)\nEXPORT_FUNC(Py_IsFalse)\nEXPORT_FUNC(Py_IsFinalizing)\nEXPORT_FUNC(Py_IsInitialized)\nEXPORT_FUNC(Py_IsNone)\nEXPORT_FUNC(Py_IsTrue)\nEXPORT_FUNC(Py_LeaveRecursiveCall)\nEXPORT_FUNC(Py_Main)\nEXPORT_FUNC(Py_MakePendingCalls)\nEXPORT_FUNC(Py_NewInterpreter)\nEXPORT_FUNC(Py_NewRef)\nEXPORT_FUNC(Py_ReprEnter)\nEXPORT_FUNC(Py_ReprLeave)\nEXPORT_FUNC(Py_SetPath)\nEXPORT_FUNC(Py_SetProgramName)\nEXPORT_FUNC(Py_SetPythonHome)\nEXPORT_FUNC(Py_SetRecursionLimit)\nEXPORT_FUNC(Py_VaBuildValue)\nEXPORT_FUNC(Py_XNewRef)\nEXPORT_FUNC(PyAIter_Check)\nEXPORT_FUNC(PyArg_Parse)\nEXPORT_FUNC(PyArg_ParseTuple)\nEXPORT_FUNC(PyArg_ParseTupleAndKeywords)\nEXPORT_FUNC(PyArg_UnpackTuple)\nEXPORT_FUNC(PyArg_ValidateKeywordArguments)\nEXPORT_FUNC(PyArg_VaParse)\nEXPORT_FUNC(PyArg_VaParseTupleAndKeywords)\nEXPORT_FUNC(PyBool_FromLong)\nEXPORT_FUNC(PyBuffer_FillContiguousStrides)\nEXPORT_FUNC(PyBuffer_FillInfo)\nEXPORT_FUNC(PyBuffer_FromContiguous)\nEXPORT_FUNC(PyBuffer_GetPointer)\nEXPORT_FUNC(PyBuffer_IsContiguous)\nEXPORT_FUNC(PyBuffer_Release)\nEXPORT_FUNC(PyBuffer_SizeFromFormat)\nEXPORT_FUNC(PyBuffer_ToContiguous)\nEXPORT_FUNC(PyByteArray_AsString)\nEXPORT_FUNC(PyByteArray_Concat)\nEXPORT_FUNC(PyByteArray_FromObject)\nEXPORT_FUNC(PyByteArray_FromStringAndSize)\nEXPORT_FUNC(PyByteArray_Resize)\nEXPORT_FUNC(PyByteArray_Size)\nEXPORT_FUNC(PyBytes_AsString)\nEXPORT_FUNC(PyBytes_AsStringAndSize)\nEXPORT_FUNC(PyBytes_Concat)\nEXPORT_FUNC(PyBytes_ConcatAndDel)\nEXPORT_FUNC(PyBytes_DecodeEscape)\nEXPORT_FUNC(PyBytes_FromFormat)\nEXPORT_FUNC(PyBytes_FromFormatV)\nEXPORT_FUNC(PyBytes_FromObject)\nEXPORT_FUNC(PyBytes_FromString)\nEXPORT_FUNC(PyBytes_FromStringAndSize)\nEXPORT_FUNC(PyBytes_Repr)\nEXPORT_FUNC(PyBytes_Size)\nEXPORT_FUNC(PyCallable_Check)\nEXPORT_FUNC(PyCallIter_New)\nEXPORT_FUNC(PyCapsule_GetContext)\nEXPORT_FUNC(PyCapsule_GetDestructor)\nEXPORT_FUNC(PyCapsule_GetName)\nEXPORT_FUNC(PyCapsule_GetPointer)\nEXPORT_FUNC(PyCapsule_Import)\nEXPORT_FUNC(PyCapsule_IsValid)\nEXPORT_FUNC(PyCapsule_New)\nEXPORT_FUNC(PyCapsule_SetContext)\nEXPORT_FUNC(PyCapsule_SetDestructor)\nEXPORT_FUNC(PyCapsule_SetName)\nEXPORT_FUNC(PyCapsule_SetPointer)\nEXPORT_FUNC(PyCFunction_Call)\nEXPORT_FUNC(PyCFunction_GetFlags)\nEXPORT_FUNC(PyCFunction_GetFunction)\nEXPORT_FUNC(PyCFunction_GetSelf)\nEXPORT_FUNC(PyCFunction_New)\nEXPORT_FUNC(PyCFunction_NewEx)\nEXPORT_FUNC(PyCMethod_New)\nEXPORT_FUNC(PyCodec_BackslashReplaceErrors)\nEXPORT_FUNC(PyCodec_Decode)\nEXPORT_FUNC(PyCodec_Decoder)\nEXPORT_FUNC(PyCodec_Encode)\nEXPORT_FUNC(PyCodec_Encoder)\nEXPORT_FUNC(PyCodec_IgnoreErrors)\nEXPORT_FUNC(PyCodec_IncrementalDecoder)\nEXPORT_FUNC(PyCodec_IncrementalEncoder)\nEXPORT_FUNC(PyCodec_KnownEncoding)\nEXPORT_FUNC(PyCodec_LookupError)\nEXPORT_FUNC(PyCodec_NameReplaceErrors)\nEXPORT_FUNC(PyCodec_Register)\nEXPORT_FUNC(PyCodec_RegisterError)\nEXPORT_FUNC(PyCodec_ReplaceErrors)\nEXPORT_FUNC(PyCodec_StreamReader)\nEXPORT_FUNC(PyCodec_StreamWriter)\nEXPORT_FUNC(PyCodec_StrictErrors)\nEXPORT_FUNC(PyCodec_Unregister)\nEXPORT_FUNC(PyCodec_XMLCharRefReplaceErrors)\nEXPORT_FUNC(PyComplex_FromDoubles)\nEXPORT_FUNC(PyComplex_ImagAsDouble)\nEXPORT_FUNC(PyComplex_RealAsDouble)\nEXPORT_FUNC(PyDescr_NewClassMethod)\nEXPORT_FUNC(PyDescr_NewGetSet)\nEXPORT_FUNC(PyDescr_NewMember)\nEXPORT_FUNC(PyDescr_NewMethod)\nEXPORT_FUNC(PyDict_Clear)\nEXPORT_FUNC(PyDict_Contains)\nEXPORT_FUNC(PyDict_Copy)\nEXPORT_FUNC(PyDict_DelItem)\nEXPORT_FUNC(PyDict_DelItemString)\nEXPORT_FUNC(PyDict_GetItem)\nEXPORT_FUNC(PyDict_GetItemRef)\nEXPORT_FUNC(PyDict_GetItemString)\nEXPORT_FUNC(PyDict_GetItemStringRef)\nEXPORT_FUNC(PyDict_GetItemWithError)\nEXPORT_FUNC(PyDict_Items)\nEXPORT_FUNC(PyDict_Keys)\nEXPORT_FUNC(PyDict_Merge)\nEXPORT_FUNC(PyDict_MergeFromSeq2)\nEXPORT_FUNC(PyDict_New)\nEXPORT_FUNC(PyDict_Next)\nEXPORT_FUNC(PyDict_SetItem)\nEXPORT_FUNC(PyDict_SetItemString)\nEXPORT_FUNC(PyDict_Size)\nEXPORT_FUNC(PyDict_Update)\nEXPORT_FUNC(PyDict_Values)\nEXPORT_FUNC(PyDictProxy_New)\nEXPORT_FUNC(PyErr_BadArgument)\nEXPORT_FUNC(PyErr_BadInternalCall)\nEXPORT_FUNC(PyErr_CheckSignals)\nEXPORT_FUNC(PyErr_Clear)\nEXPORT_FUNC(PyErr_Display)\nEXPORT_FUNC(PyErr_DisplayException)\nEXPORT_FUNC(PyErr_ExceptionMatches)\nEXPORT_FUNC(PyErr_Fetch)\nEXPORT_FUNC(PyErr_Format)\nEXPORT_FUNC(PyErr_FormatV)\nEXPORT_FUNC(PyErr_GetExcInfo)\nEXPORT_FUNC(PyErr_GetHandledException)\nEXPORT_FUNC(PyErr_GetRaisedException)\nEXPORT_FUNC(PyErr_GivenExceptionMatches)\nEXPORT_FUNC(PyErr_NewException)\nEXPORT_FUNC(PyErr_NewExceptionWithDoc)\nEXPORT_FUNC(PyErr_NoMemory)\nEXPORT_FUNC(PyErr_NormalizeException)\nEXPORT_FUNC(PyErr_Occurred)\nEXPORT_FUNC(PyErr_Print)\nEXPORT_FUNC(PyErr_PrintEx)\nEXPORT_FUNC(PyErr_ProgramText)\nEXPORT_FUNC(PyErr_ResourceWarning)\nEXPORT_FUNC(PyErr_Restore)\nEXPORT_FUNC(PyErr_SetExcFromWindowsErr)\nEXPORT_FUNC(PyErr_SetExcFromWindowsErrWithFilename)\nEXPORT_FUNC(PyErr_SetExcFromWindowsErrWithFilenameObject)\nEXPORT_FUNC(PyErr_SetExcFromWindowsErrWithFilenameObjects)\nEXPORT_FUNC(PyErr_SetExcInfo)\nEXPORT_FUNC(PyErr_SetFromErrno)\nEXPORT_FUNC(PyErr_SetFromErrnoWithFilename)\nEXPORT_FUNC(PyErr_SetFromErrnoWithFilenameObject)\nEXPORT_FUNC(PyErr_SetFromErrnoWithFilenameObjects)\nEXPORT_FUNC(PyErr_SetFromWindowsErr)\nEXPORT_FUNC(PyErr_SetFromWindowsErrWithFilename)\nEXPORT_FUNC(PyErr_SetHandledException)\nEXPORT_FUNC(PyErr_SetImportError)\nEXPORT_FUNC(PyErr_SetImportErrorSubclass)\nEXPORT_FUNC(PyErr_SetInterrupt)\nEXPORT_FUNC(PyErr_SetInterruptEx)\nEXPORT_FUNC(PyErr_SetNone)\nEXPORT_FUNC(PyErr_SetObject)\nEXPORT_FUNC(PyErr_SetRaisedException)\nEXPORT_FUNC(PyErr_SetString)\nEXPORT_FUNC(PyErr_SyntaxLocation)\nEXPORT_FUNC(PyErr_SyntaxLocationEx)\nEXPORT_FUNC(PyErr_WarnEx)\nEXPORT_FUNC(PyErr_WarnExplicit)\nEXPORT_FUNC(PyErr_WarnFormat)\nEXPORT_FUNC(PyErr_WriteUnraisable)\nEXPORT_FUNC(PyEval_AcquireLock)\nEXPORT_FUNC(PyEval_AcquireThread)\nEXPORT_FUNC(PyEval_CallFunction)\nEXPORT_FUNC(PyEval_CallMethod)\nEXPORT_FUNC(PyEval_CallObjectWithKeywords)\nEXPORT_FUNC(PyEval_EvalCode)\nEXPORT_FUNC(PyEval_EvalCodeEx)\nEXPORT_FUNC(PyEval_EvalFrame)\nEXPORT_FUNC(PyEval_EvalFrameEx)\nEXPORT_FUNC(PyEval_GetBuiltins)\nEXPORT_FUNC(PyEval_GetFrame)\nEXPORT_FUNC(PyEval_GetFrameBuiltins)\nEXPORT_FUNC(PyEval_GetFrameGlobals)\nEXPORT_FUNC(PyEval_GetFrameLocals)\nEXPORT_FUNC(PyEval_GetFuncDesc)\nEXPORT_FUNC(PyEval_GetFuncName)\nEXPORT_FUNC(PyEval_GetGlobals)\nEXPORT_FUNC(PyEval_GetLocals)\nEXPORT_FUNC(PyEval_InitThreads)\nEXPORT_FUNC(PyEval_ReleaseLock)\nEXPORT_FUNC(PyEval_ReleaseThread)\nEXPORT_FUNC(PyEval_RestoreThread)\nEXPORT_FUNC(PyEval_SaveThread)\nEXPORT_FUNC(PyEval_ThreadsInitialized)\nEXPORT_FUNC(PyException_GetArgs)\nEXPORT_FUNC(PyException_GetCause)\nEXPORT_FUNC(PyException_GetContext)\nEXPORT_FUNC(PyException_GetTraceback)\nEXPORT_FUNC(PyException_SetArgs)\nEXPORT_FUNC(PyException_SetCause)\nEXPORT_FUNC(PyException_SetContext)\nEXPORT_FUNC(PyException_SetTraceback)\nEXPORT_FUNC(PyExceptionClass_Name)\nEXPORT_FUNC(PyFile_FromFd)\nEXPORT_FUNC(PyFile_GetLine)\nEXPORT_FUNC(PyFile_WriteObject)\nEXPORT_FUNC(PyFile_WriteString)\nEXPORT_FUNC(PyFloat_AsDouble)\nEXPORT_FUNC(PyFloat_FromDouble)\nEXPORT_FUNC(PyFloat_FromString)\nEXPORT_FUNC(PyFloat_GetInfo)\nEXPORT_FUNC(PyFloat_GetMax)\nEXPORT_FUNC(PyFloat_GetMin)\nEXPORT_FUNC(PyFrame_GetCode)\nEXPORT_FUNC(PyFrame_GetLineNumber)\nEXPORT_FUNC(PyFrozenSet_New)\nEXPORT_FUNC(PyGC_Collect)\nEXPORT_FUNC(PyGC_Disable)\nEXPORT_FUNC(PyGC_Enable)\nEXPORT_FUNC(PyGC_IsEnabled)\nEXPORT_FUNC(PyGILState_Ensure)\nEXPORT_FUNC(PyGILState_GetThisThreadState)\nEXPORT_FUNC(PyGILState_Release)\nEXPORT_FUNC(PyImport_AddModule)\nEXPORT_FUNC(PyImport_AddModuleObject)\nEXPORT_FUNC(PyImport_AddModuleRef)\nEXPORT_FUNC(PyImport_AppendInittab)\nEXPORT_FUNC(PyImport_ExecCodeModule)\nEXPORT_FUNC(PyImport_ExecCodeModuleEx)\nEXPORT_FUNC(PyImport_ExecCodeModuleObject)\nEXPORT_FUNC(PyImport_ExecCodeModuleWithPathnames)\nEXPORT_FUNC(PyImport_GetImporter)\nEXPORT_FUNC(PyImport_GetMagicNumber)\nEXPORT_FUNC(PyImport_GetMagicTag)\nEXPORT_FUNC(PyImport_GetModule)\nEXPORT_FUNC(PyImport_GetModuleDict)\nEXPORT_FUNC(PyImport_Import)\nEXPORT_FUNC(PyImport_ImportFrozenModule)\nEXPORT_FUNC(PyImport_ImportFrozenModuleObject)\nEXPORT_FUNC(PyImport_ImportModule)\nEXPORT_FUNC(PyImport_ImportModuleLevel)\nEXPORT_FUNC(PyImport_ImportModuleLevelObject)\nEXPORT_FUNC(PyImport_ImportModuleNoBlock)\nEXPORT_FUNC(PyImport_ReloadModule)\nEXPORT_FUNC(PyIndex_Check)\nEXPORT_FUNC(PyInterpreterState_Clear)\nEXPORT_FUNC(PyInterpreterState_Delete)\nEXPORT_FUNC(PyInterpreterState_Get)\nEXPORT_FUNC(PyInterpreterState_GetDict)\nEXPORT_FUNC(PyInterpreterState_GetID)\nEXPORT_FUNC(PyInterpreterState_New)\nEXPORT_FUNC(PyIter_Check)\nEXPORT_FUNC(PyIter_Next)\nEXPORT_FUNC(PyIter_Send)\nEXPORT_FUNC(PyList_Append)\nEXPORT_FUNC(PyList_AsTuple)\nEXPORT_FUNC(PyList_GetItem)\nEXPORT_FUNC(PyList_GetItemRef)\nEXPORT_FUNC(PyList_GetSlice)\nEXPORT_FUNC(PyList_Insert)\nEXPORT_FUNC(PyList_New)\nEXPORT_FUNC(PyList_Reverse)\nEXPORT_FUNC(PyList_SetItem)\nEXPORT_FUNC(PyList_SetSlice)\nEXPORT_FUNC(PyList_Size)\nEXPORT_FUNC(PyList_Sort)\nEXPORT_FUNC(PyLong_AsDouble)\nEXPORT_FUNC(PyLong_AsInt)\nEXPORT_FUNC(PyLong_AsLong)\nEXPORT_FUNC(PyLong_AsLongAndOverflow)\nEXPORT_FUNC(PyLong_AsLongLong)\nEXPORT_FUNC(PyLong_AsLongLongAndOverflow)\nEXPORT_FUNC(PyLong_AsSize_t)\nEXPORT_FUNC(PyLong_AsSsize_t)\nEXPORT_FUNC(PyLong_AsUnsignedLong)\nEXPORT_FUNC(PyLong_AsUnsignedLongLong)\nEXPORT_FUNC(PyLong_AsUnsignedLongLongMask)\nEXPORT_FUNC(PyLong_AsUnsignedLongMask)\nEXPORT_FUNC(PyLong_AsVoidPtr)\nEXPORT_FUNC(PyLong_FromDouble)\nEXPORT_FUNC(PyLong_FromLong)\nEXPORT_FUNC(PyLong_FromLongLong)\nEXPORT_FUNC(PyLong_FromSize_t)\nEXPORT_FUNC(PyLong_FromSsize_t)\nEXPORT_FUNC(PyLong_FromString)\nEXPORT_FUNC(PyLong_FromUnsignedLong)\nEXPORT_FUNC(PyLong_FromUnsignedLongLong)\nEXPORT_FUNC(PyLong_FromVoidPtr)\nEXPORT_FUNC(PyLong_GetInfo)\nEXPORT_FUNC(PyMapping_Check)\nEXPORT_FUNC(PyMapping_GetItemString)\nEXPORT_FUNC(PyMapping_GetOptionalItem)\nEXPORT_FUNC(PyMapping_GetOptionalItemString)\nEXPORT_FUNC(PyMapping_HasKey)\nEXPORT_FUNC(PyMapping_HasKeyString)\nEXPORT_FUNC(PyMapping_HasKeyStringWithError)\nEXPORT_FUNC(PyMapping_HasKeyWithError)\nEXPORT_FUNC(PyMapping_Items)\nEXPORT_FUNC(PyMapping_Keys)\nEXPORT_FUNC(PyMapping_Length)\nEXPORT_FUNC(PyMapping_SetItemString)\nEXPORT_FUNC(PyMapping_Size)\nEXPORT_FUNC(PyMapping_Values)\nEXPORT_FUNC(PyMarshal_ReadObjectFromString)\nEXPORT_FUNC(PyMarshal_WriteObjectToString)\nEXPORT_FUNC(PyMem_Calloc)\nEXPORT_FUNC(PyMem_Free)\nEXPORT_FUNC(PyMem_Malloc)\nEXPORT_FUNC(PyMem_RawCalloc)\nEXPORT_FUNC(PyMem_RawFree)\nEXPORT_FUNC(PyMem_RawMalloc)\nEXPORT_FUNC(PyMem_RawRealloc)\nEXPORT_FUNC(PyMem_Realloc)\nEXPORT_FUNC(PyMember_GetOne)\nEXPORT_FUNC(PyMember_SetOne)\nEXPORT_FUNC(PyMemoryView_FromBuffer)\nEXPORT_FUNC(PyMemoryView_FromMemory)\nEXPORT_FUNC(PyMemoryView_FromObject)\nEXPORT_FUNC(PyMemoryView_GetContiguous)\nEXPORT_FUNC(PyModule_Add)\nEXPORT_FUNC(PyModule_AddFunctions)\nEXPORT_FUNC(PyModule_AddIntConstant)\nEXPORT_FUNC(PyModule_AddObject)\nEXPORT_FUNC(PyModule_AddObjectRef)\nEXPORT_FUNC(PyModule_AddStringConstant)\nEXPORT_FUNC(PyModule_AddType)\nEXPORT_FUNC(PyModule_Create2)\nEXPORT_FUNC(PyModule_ExecDef)\nEXPORT_FUNC(PyModule_FromDefAndSpec2)\nEXPORT_FUNC(PyModule_GetDef)\nEXPORT_FUNC(PyModule_GetDict)\nEXPORT_FUNC(PyModule_GetFilename)\nEXPORT_FUNC(PyModule_GetFilenameObject)\nEXPORT_FUNC(PyModule_GetName)\nEXPORT_FUNC(PyModule_GetNameObject)\nEXPORT_FUNC(PyModule_GetState)\nEXPORT_FUNC(PyModule_New)\nEXPORT_FUNC(PyModule_NewObject)\nEXPORT_FUNC(PyModule_SetDocString)\nEXPORT_FUNC(PyModuleDef_Init)\nEXPORT_FUNC(PyNumber_Absolute)\nEXPORT_FUNC(PyNumber_Add)\nEXPORT_FUNC(PyNumber_And)\nEXPORT_FUNC(PyNumber_AsSsize_t)\nEXPORT_FUNC(PyNumber_Check)\nEXPORT_FUNC(PyNumber_Divmod)\nEXPORT_FUNC(PyNumber_Float)\nEXPORT_FUNC(PyNumber_FloorDivide)\nEXPORT_FUNC(PyNumber_Index)\nEXPORT_FUNC(PyNumber_InPlaceAdd)\nEXPORT_FUNC(PyNumber_InPlaceAnd)\nEXPORT_FUNC(PyNumber_InPlaceFloorDivide)\nEXPORT_FUNC(PyNumber_InPlaceLshift)\nEXPORT_FUNC(PyNumber_InPlaceMatrixMultiply)\nEXPORT_FUNC(PyNumber_InPlaceMultiply)\nEXPORT_FUNC(PyNumber_InPlaceOr)\nEXPORT_FUNC(PyNumber_InPlacePower)\nEXPORT_FUNC(PyNumber_InPlaceRemainder)\nEXPORT_FUNC(PyNumber_InPlaceRshift)\nEXPORT_FUNC(PyNumber_InPlaceSubtract)\nEXPORT_FUNC(PyNumber_InPlaceTrueDivide)\nEXPORT_FUNC(PyNumber_InPlaceXor)\nEXPORT_FUNC(PyNumber_Invert)\nEXPORT_FUNC(PyNumber_Long)\nEXPORT_FUNC(PyNumber_Lshift)\nEXPORT_FUNC(PyNumber_MatrixMultiply)\nEXPORT_FUNC(PyNumber_Multiply)\nEXPORT_FUNC(PyNumber_Negative)\nEXPORT_FUNC(PyNumber_Or)\nEXPORT_FUNC(PyNumber_Positive)\nEXPORT_FUNC(PyNumber_Power)\nEXPORT_FUNC(PyNumber_Remainder)\nEXPORT_FUNC(PyNumber_Rshift)\nEXPORT_FUNC(PyNumber_Subtract)\nEXPORT_FUNC(PyNumber_ToBase)\nEXPORT_FUNC(PyNumber_TrueDivide)\nEXPORT_FUNC(PyNumber_Xor)\nEXPORT_FUNC(PyObject_AsCharBuffer)\nEXPORT_FUNC(PyObject_ASCII)\nEXPORT_FUNC(PyObject_AsFileDescriptor)\nEXPORT_FUNC(PyObject_AsReadBuffer)\nEXPORT_FUNC(PyObject_AsWriteBuffer)\nEXPORT_FUNC(PyObject_Bytes)\nEXPORT_FUNC(PyObject_Call)\nEXPORT_FUNC(PyObject_CallFunction)\nEXPORT_FUNC(PyObject_CallFunctionObjArgs)\nEXPORT_FUNC(PyObject_CallMethod)\nEXPORT_FUNC(PyObject_CallMethodObjArgs)\nEXPORT_FUNC(PyObject_CallNoArgs)\nEXPORT_FUNC(PyObject_CallObject)\nEXPORT_FUNC(PyObject_Calloc)\nEXPORT_FUNC(PyObject_CheckBuffer)\nEXPORT_FUNC(PyObject_CheckReadBuffer)\nEXPORT_FUNC(PyObject_ClearWeakRefs)\nEXPORT_FUNC(PyObject_CopyData)\nEXPORT_FUNC(PyObject_DelAttr)\nEXPORT_FUNC(PyObject_DelAttrString)\nEXPORT_FUNC(PyObject_DelItem)\nEXPORT_FUNC(PyObject_DelItemString)\nEXPORT_FUNC(PyObject_Dir)\nEXPORT_FUNC(PyObject_Format)\nEXPORT_FUNC(PyObject_Free)\nEXPORT_FUNC(PyObject_GC_Del)\nEXPORT_FUNC(PyObject_GC_IsFinalized)\nEXPORT_FUNC(PyObject_GC_IsTracked)\nEXPORT_FUNC(PyObject_GC_Track)\nEXPORT_FUNC(PyObject_GC_UnTrack)\nEXPORT_FUNC(PyObject_GenericGetAttr)\nEXPORT_FUNC(PyObject_GenericGetDict)\nEXPORT_FUNC(PyObject_GenericSetAttr)\nEXPORT_FUNC(PyObject_GenericSetDict)\nEXPORT_FUNC(PyObject_GetAIter)\nEXPORT_FUNC(PyObject_GetAttr)\nEXPORT_FUNC(PyObject_GetAttrString)\nEXPORT_FUNC(PyObject_GetBuffer)\nEXPORT_FUNC(PyObject_GetItem)\nEXPORT_FUNC(PyObject_GetIter)\nEXPORT_FUNC(PyObject_GetOptionalAttr)\nEXPORT_FUNC(PyObject_GetOptionalAttrString)\nEXPORT_FUNC(PyObject_GetTypeData)\nEXPORT_FUNC(PyObject_HasAttr)\nEXPORT_FUNC(PyObject_HasAttrString)\nEXPORT_FUNC(PyObject_HasAttrStringWithError)\nEXPORT_FUNC(PyObject_HasAttrWithError)\nEXPORT_FUNC(PyObject_Hash)\nEXPORT_FUNC(PyObject_HashNotImplemented)\nEXPORT_FUNC(PyObject_Init)\nEXPORT_FUNC(PyObject_InitVar)\nEXPORT_FUNC(PyObject_IsInstance)\nEXPORT_FUNC(PyObject_IsSubclass)\nEXPORT_FUNC(PyObject_IsTrue)\nEXPORT_FUNC(PyObject_Length)\nEXPORT_FUNC(PyObject_Malloc)\nEXPORT_FUNC(PyObject_Not)\nEXPORT_FUNC(PyObject_Realloc)\nEXPORT_FUNC(PyObject_Repr)\nEXPORT_FUNC(PyObject_RichCompare)\nEXPORT_FUNC(PyObject_RichCompareBool)\nEXPORT_FUNC(PyObject_SelfIter)\nEXPORT_FUNC(PyObject_SetAttr)\nEXPORT_FUNC(PyObject_SetAttrString)\nEXPORT_FUNC(PyObject_SetItem)\nEXPORT_FUNC(PyObject_Size)\nEXPORT_FUNC(PyObject_Str)\nEXPORT_FUNC(PyObject_Type)\nEXPORT_FUNC(PyObject_Vectorcall)\nEXPORT_FUNC(PyObject_VectorcallMethod)\nEXPORT_FUNC(PyOS_CheckStack)\nEXPORT_FUNC(PyOS_double_to_string)\nEXPORT_FUNC(PyOS_FSPath)\nEXPORT_FUNC(PyOS_getsig)\nEXPORT_FUNC(PyOS_InterruptOccurred)\nEXPORT_FUNC(PyOS_mystricmp)\nEXPORT_FUNC(PyOS_mystrnicmp)\nEXPORT_FUNC(PyOS_setsig)\nEXPORT_FUNC(PyOS_snprintf)\nEXPORT_FUNC(PyOS_string_to_double)\nEXPORT_FUNC(PyOS_strtol)\nEXPORT_FUNC(PyOS_strtoul)\nEXPORT_FUNC(PyOS_vsnprintf)\nEXPORT_FUNC(PySeqIter_New)\nEXPORT_FUNC(PySequence_Check)\nEXPORT_FUNC(PySequence_Concat)\nEXPORT_FUNC(PySequence_Contains)\nEXPORT_FUNC(PySequence_Count)\nEXPORT_FUNC(PySequence_DelItem)\nEXPORT_FUNC(PySequence_DelSlice)\nEXPORT_FUNC(PySequence_Fast)\nEXPORT_FUNC(PySequence_GetItem)\nEXPORT_FUNC(PySequence_GetSlice)\nEXPORT_FUNC(PySequence_In)\nEXPORT_FUNC(PySequence_Index)\nEXPORT_FUNC(PySequence_InPlaceConcat)\nEXPORT_FUNC(PySequence_InPlaceRepeat)\nEXPORT_FUNC(PySequence_Length)\nEXPORT_FUNC(PySequence_List)\nEXPORT_FUNC(PySequence_Repeat)\nEXPORT_FUNC(PySequence_SetItem)\nEXPORT_FUNC(PySequence_SetSlice)\nEXPORT_FUNC(PySequence_Size)\nEXPORT_FUNC(PySequence_Tuple)\nEXPORT_FUNC(PySet_Add)\nEXPORT_FUNC(PySet_Clear)\nEXPORT_FUNC(PySet_Contains)\nEXPORT_FUNC(PySet_Discard)\nEXPORT_FUNC(PySet_New)\nEXPORT_FUNC(PySet_Pop)\nEXPORT_FUNC(PySet_Size)\nEXPORT_FUNC(PySlice_AdjustIndices)\nEXPORT_FUNC(PySlice_GetIndices)\nEXPORT_FUNC(PySlice_GetIndicesEx)\nEXPORT_FUNC(PySlice_New)\nEXPORT_FUNC(PySlice_Unpack)\nEXPORT_FUNC(PyState_AddModule)\nEXPORT_FUNC(PyState_FindModule)\nEXPORT_FUNC(PyState_RemoveModule)\nEXPORT_FUNC(PyStructSequence_GetItem)\nEXPORT_FUNC(PyStructSequence_New)\nEXPORT_FUNC(PyStructSequence_NewType)\nEXPORT_FUNC(PyStructSequence_SetItem)\nEXPORT_FUNC(PySys_AddWarnOption)\nEXPORT_FUNC(PySys_AddWarnOptionUnicode)\nEXPORT_FUNC(PySys_AddXOption)\nEXPORT_FUNC(PySys_Audit)\nEXPORT_FUNC(PySys_AuditTuple)\nEXPORT_FUNC(PySys_FormatStderr)\nEXPORT_FUNC(PySys_FormatStdout)\nEXPORT_FUNC(PySys_GetObject)\nEXPORT_FUNC(PySys_GetXOptions)\nEXPORT_FUNC(PySys_HasWarnOptions)\nEXPORT_FUNC(PySys_ResetWarnOptions)\nEXPORT_FUNC(PySys_SetArgv)\nEXPORT_FUNC(PySys_SetArgvEx)\nEXPORT_FUNC(PySys_SetObject)\nEXPORT_FUNC(PySys_SetPath)\nEXPORT_FUNC(PySys_WriteStderr)\nEXPORT_FUNC(PySys_WriteStdout)\nEXPORT_FUNC(PyThread_acquire_lock)\nEXPORT_FUNC(PyThread_acquire_lock_timed)\nEXPORT_FUNC(PyThread_allocate_lock)\nEXPORT_FUNC(PyThread_create_key)\nEXPORT_FUNC(PyThread_delete_key)\nEXPORT_FUNC(PyThread_delete_key_value)\nEXPORT_FUNC(PyThread_exit_thread)\nEXPORT_FUNC(PyThread_free_lock)\nEXPORT_FUNC(PyThread_get_key_value)\nEXPORT_FUNC(PyThread_get_stacksize)\nEXPORT_FUNC(PyThread_get_thread_ident)\nEXPORT_FUNC(PyThread_get_thread_native_id)\nEXPORT_FUNC(PyThread_GetInfo)\nEXPORT_FUNC(PyThread_init_thread)\nEXPORT_FUNC(PyThread_ReInitTLS)\nEXPORT_FUNC(PyThread_release_lock)\nEXPORT_FUNC(PyThread_set_key_value)\nEXPORT_FUNC(PyThread_set_stacksize)\nEXPORT_FUNC(PyThread_start_new_thread)\nEXPORT_FUNC(PyThread_tss_alloc)\nEXPORT_FUNC(PyThread_tss_create)\nEXPORT_FUNC(PyThread_tss_delete)\nEXPORT_FUNC(PyThread_tss_free)\nEXPORT_FUNC(PyThread_tss_get)\nEXPORT_FUNC(PyThread_tss_is_created)\nEXPORT_FUNC(PyThread_tss_set)\nEXPORT_FUNC(PyThreadState_Clear)\nEXPORT_FUNC(PyThreadState_Delete)\nEXPORT_FUNC(PyThreadState_DeleteCurrent)\nEXPORT_FUNC(PyThreadState_Get)\nEXPORT_FUNC(PyThreadState_GetDict)\nEXPORT_FUNC(PyThreadState_GetFrame)\nEXPORT_FUNC(PyThreadState_GetID)\nEXPORT_FUNC(PyThreadState_GetInterpreter)\nEXPORT_FUNC(PyThreadState_New)\nEXPORT_FUNC(PyThreadState_SetAsyncExc)\nEXPORT_FUNC(PyThreadState_Swap)\nEXPORT_FUNC(PyTraceBack_Here)\nEXPORT_FUNC(PyTraceBack_Print)\nEXPORT_FUNC(PyTuple_GetItem)\nEXPORT_FUNC(PyTuple_GetSlice)\nEXPORT_FUNC(PyTuple_New)\nEXPORT_FUNC(PyTuple_Pack)\nEXPORT_FUNC(PyTuple_SetItem)\nEXPORT_FUNC(PyTuple_Size)\nEXPORT_FUNC(PyType_ClearCache)\nEXPORT_FUNC(PyType_FromMetaclass)\nEXPORT_FUNC(PyType_FromModuleAndSpec)\nEXPORT_FUNC(PyType_FromSpec)\nEXPORT_FUNC(PyType_FromSpecWithBases)\nEXPORT_FUNC(PyType_GenericAlloc)\nEXPORT_FUNC(PyType_GenericNew)\nEXPORT_FUNC(PyType_GetFlags)\nEXPORT_FUNC(PyType_GetFullyQualifiedName)\nEXPORT_FUNC(PyType_GetModule)\nEXPORT_FUNC(PyType_GetModuleByDef)\nEXPORT_FUNC(PyType_GetModuleName)\nEXPORT_FUNC(PyType_GetModuleState)\nEXPORT_FUNC(PyType_GetName)\nEXPORT_FUNC(PyType_GetQualName)\nEXPORT_FUNC(PyType_GetSlot)\nEXPORT_FUNC(PyType_GetTypeDataSize)\nEXPORT_FUNC(PyType_IsSubtype)\nEXPORT_FUNC(PyType_Modified)\nEXPORT_FUNC(PyType_Ready)\nEXPORT_FUNC(PyUnicode_Append)\nEXPORT_FUNC(PyUnicode_AppendAndDel)\nEXPORT_FUNC(PyUnicode_AsASCIIString)\nEXPORT_FUNC(PyUnicode_AsCharmapString)\nEXPORT_FUNC(PyUnicode_AsDecodedObject)\nEXPORT_FUNC(PyUnicode_AsDecodedUnicode)\nEXPORT_FUNC(PyUnicode_AsEncodedObject)\nEXPORT_FUNC(PyUnicode_AsEncodedString)\nEXPORT_FUNC(PyUnicode_AsEncodedUnicode)\nEXPORT_FUNC(PyUnicode_AsLatin1String)\nEXPORT_FUNC(PyUnicode_AsMBCSString)\nEXPORT_FUNC(PyUnicode_AsRawUnicodeEscapeString)\nEXPORT_FUNC(PyUnicode_AsUCS4)\nEXPORT_FUNC(PyUnicode_AsUCS4Copy)\nEXPORT_FUNC(PyUnicode_AsUnicodeEscapeString)\nEXPORT_FUNC(PyUnicode_AsUTF16String)\nEXPORT_FUNC(PyUnicode_AsUTF32String)\nEXPORT_FUNC(PyUnicode_AsUTF8AndSize)\nEXPORT_FUNC(PyUnicode_AsUTF8String)\nEXPORT_FUNC(PyUnicode_AsWideChar)\nEXPORT_FUNC(PyUnicode_AsWideCharString)\nEXPORT_FUNC(PyUnicode_BuildEncodingMap)\nEXPORT_FUNC(PyUnicode_Compare)\nEXPORT_FUNC(PyUnicode_CompareWithASCIIString)\nEXPORT_FUNC(PyUnicode_Concat)\nEXPORT_FUNC(PyUnicode_Contains)\nEXPORT_FUNC(PyUnicode_Count)\nEXPORT_FUNC(PyUnicode_Decode)\nEXPORT_FUNC(PyUnicode_DecodeASCII)\nEXPORT_FUNC(PyUnicode_DecodeCharmap)\nEXPORT_FUNC(PyUnicode_DecodeCodePageStateful)\nEXPORT_FUNC(PyUnicode_DecodeFSDefault)\nEXPORT_FUNC(PyUnicode_DecodeFSDefaultAndSize)\nEXPORT_FUNC(PyUnicode_DecodeLatin1)\nEXPORT_FUNC(PyUnicode_DecodeLocale)\nEXPORT_FUNC(PyUnicode_DecodeLocaleAndSize)\nEXPORT_FUNC(PyUnicode_DecodeMBCS)\nEXPORT_FUNC(PyUnicode_DecodeMBCSStateful)\nEXPORT_FUNC(PyUnicode_DecodeRawUnicodeEscape)\nEXPORT_FUNC(PyUnicode_DecodeUnicodeEscape)\nEXPORT_FUNC(PyUnicode_DecodeUTF16)\nEXPORT_FUNC(PyUnicode_DecodeUTF16Stateful)\nEXPORT_FUNC(PyUnicode_DecodeUTF32)\nEXPORT_FUNC(PyUnicode_DecodeUTF32Stateful)\nEXPORT_FUNC(PyUnicode_DecodeUTF7)\nEXPORT_FUNC(PyUnicode_DecodeUTF7Stateful)\nEXPORT_FUNC(PyUnicode_DecodeUTF8)\nEXPORT_FUNC(PyUnicode_DecodeUTF8Stateful)\nEXPORT_FUNC(PyUnicode_EncodeCodePage)\nEXPORT_FUNC(PyUnicode_EncodeFSDefault)\nEXPORT_FUNC(PyUnicode_EncodeLocale)\nEXPORT_FUNC(PyUnicode_EqualToUTF8)\nEXPORT_FUNC(PyUnicode_EqualToUTF8AndSize)\nEXPORT_FUNC(PyUnicode_Find)\nEXPORT_FUNC(PyUnicode_FindChar)\nEXPORT_FUNC(PyUnicode_Format)\nEXPORT_FUNC(PyUnicode_FromEncodedObject)\nEXPORT_FUNC(PyUnicode_FromFormat)\nEXPORT_FUNC(PyUnicode_FromFormatV)\nEXPORT_FUNC(PyUnicode_FromObject)\nEXPORT_FUNC(PyUnicode_FromOrdinal)\nEXPORT_FUNC(PyUnicode_FromString)\nEXPORT_FUNC(PyUnicode_FromStringAndSize)\nEXPORT_FUNC(PyUnicode_FromWideChar)\nEXPORT_FUNC(PyUnicode_FSConverter)\nEXPORT_FUNC(PyUnicode_FSDecoder)\nEXPORT_FUNC(PyUnicode_GetDefaultEncoding)\nEXPORT_FUNC(PyUnicode_GetLength)\nEXPORT_FUNC(PyUnicode_GetSize)\nEXPORT_FUNC(PyUnicode_InternFromString)\nEXPORT_FUNC(PyUnicode_InternImmortal)\nEXPORT_FUNC(PyUnicode_InternInPlace)\nEXPORT_FUNC(PyUnicode_IsIdentifier)\nEXPORT_FUNC(PyUnicode_Join)\nEXPORT_FUNC(PyUnicode_Partition)\nEXPORT_FUNC(PyUnicode_ReadChar)\nEXPORT_FUNC(PyUnicode_Replace)\nEXPORT_FUNC(PyUnicode_Resize)\nEXPORT_FUNC(PyUnicode_RichCompare)\nEXPORT_FUNC(PyUnicode_RPartition)\nEXPORT_FUNC(PyUnicode_RSplit)\nEXPORT_FUNC(PyUnicode_Split)\nEXPORT_FUNC(PyUnicode_Splitlines)\nEXPORT_FUNC(PyUnicode_Substring)\nEXPORT_FUNC(PyUnicode_Tailmatch)\nEXPORT_FUNC(PyUnicode_Translate)\nEXPORT_FUNC(PyUnicode_WriteChar)\nEXPORT_FUNC(PyUnicodeDecodeError_Create)\nEXPORT_FUNC(PyUnicodeDecodeError_GetEncoding)\nEXPORT_FUNC(PyUnicodeDecodeError_GetEnd)\nEXPORT_FUNC(PyUnicodeDecodeError_GetObject)\nEXPORT_FUNC(PyUnicodeDecodeError_GetReason)\nEXPORT_FUNC(PyUnicodeDecodeError_GetStart)\nEXPORT_FUNC(PyUnicodeDecodeError_SetEnd)\nEXPORT_FUNC(PyUnicodeDecodeError_SetReason)\nEXPORT_FUNC(PyUnicodeDecodeError_SetStart)\nEXPORT_FUNC(PyUnicodeEncodeError_GetEncoding)\nEXPORT_FUNC(PyUnicodeEncodeError_GetEnd)\nEXPORT_FUNC(PyUnicodeEncodeError_GetObject)\nEXPORT_FUNC(PyUnicodeEncodeError_GetReason)\nEXPORT_FUNC(PyUnicodeEncodeError_GetStart)\nEXPORT_FUNC(PyUnicodeEncodeError_SetEnd)\nEXPORT_FUNC(PyUnicodeEncodeError_SetReason)\nEXPORT_FUNC(PyUnicodeEncodeError_SetStart)\nEXPORT_FUNC(PyUnicodeTranslateError_GetEnd)\nEXPORT_FUNC(PyUnicodeTranslateError_GetObject)\nEXPORT_FUNC(PyUnicodeTranslateError_GetReason)\nEXPORT_FUNC(PyUnicodeTranslateError_GetStart)\nEXPORT_FUNC(PyUnicodeTranslateError_SetEnd)\nEXPORT_FUNC(PyUnicodeTranslateError_SetReason)\nEXPORT_FUNC(PyUnicodeTranslateError_SetStart)\nEXPORT_FUNC(PyVectorcall_Call)\nEXPORT_FUNC(PyVectorcall_NARGS)\nEXPORT_FUNC(PyWeakref_GetObject)\nEXPORT_FUNC(PyWeakref_GetRef)\nEXPORT_FUNC(PyWeakref_NewProxy)\nEXPORT_FUNC(PyWeakref_NewRef)\nEXPORT_FUNC(PyWrapper_New)\n\nEXPORT_DATA(_Py_EllipsisObject)\nEXPORT_DATA(_Py_FalseStruct)\nEXPORT_DATA(_Py_NoneStruct)\nEXPORT_DATA(_Py_NotImplementedStruct)\nEXPORT_DATA(_Py_RefTotal)\nEXPORT_DATA(_Py_SwappedOp)\nEXPORT_DATA(_Py_TrueStruct)\nEXPORT_DATA(_PyWeakref_CallableProxyType)\nEXPORT_DATA(_PyWeakref_ProxyType)\nEXPORT_DATA(_PyWeakref_RefType)\nEXPORT_DATA(Py_FileSystemDefaultEncodeErrors)\nEXPORT_DATA(Py_FileSystemDefaultEncoding)\nEXPORT_DATA(Py_GenericAliasType)\nEXPORT_DATA(Py_HasFileSystemDefaultEncoding)\nEXPORT_DATA(Py_UTF8Mode)\nEXPORT_DATA(Py_Version)\nEXPORT_DATA(PyBaseObject_Type)\nEXPORT_DATA(PyBool_Type)\nEXPORT_DATA(PyByteArray_Type)\nEXPORT_DATA(PyByteArrayIter_Type)\nEXPORT_DATA(PyBytes_Type)\nEXPORT_DATA(PyBytesIter_Type)\nEXPORT_DATA(PyCallIter_Type)\nEXPORT_DATA(PyCapsule_Type)\nEXPORT_DATA(PyCFunction_Type)\nEXPORT_DATA(PyClassMethodDescr_Type)\nEXPORT_DATA(PyComplex_Type)\nEXPORT_DATA(PyDict_Type)\nEXPORT_DATA(PyDictItems_Type)\nEXPORT_DATA(PyDictIterItem_Type)\nEXPORT_DATA(PyDictIterKey_Type)\nEXPORT_DATA(PyDictIterValue_Type)\nEXPORT_DATA(PyDictKeys_Type)\nEXPORT_DATA(PyDictProxy_Type)\nEXPORT_DATA(PyDictRevIterItem_Type)\nEXPORT_DATA(PyDictRevIterKey_Type)\nEXPORT_DATA(PyDictRevIterValue_Type)\nEXPORT_DATA(PyDictValues_Type)\nEXPORT_DATA(PyEllipsis_Type)\nEXPORT_DATA(PyEnum_Type)\nEXPORT_DATA(PyExc_ArithmeticError)\nEXPORT_DATA(PyExc_AssertionError)\nEXPORT_DATA(PyExc_AttributeError)\nEXPORT_DATA(PyExc_BaseException)\nEXPORT_DATA(PyExc_BaseExceptionGroup)\nEXPORT_DATA(PyExc_BlockingIOError)\nEXPORT_DATA(PyExc_BrokenPipeError)\nEXPORT_DATA(PyExc_BufferError)\nEXPORT_DATA(PyExc_BytesWarning)\nEXPORT_DATA(PyExc_ChildProcessError)\nEXPORT_DATA(PyExc_ConnectionAbortedError)\nEXPORT_DATA(PyExc_ConnectionError)\nEXPORT_DATA(PyExc_ConnectionRefusedError)\nEXPORT_DATA(PyExc_ConnectionResetError)\nEXPORT_DATA(PyExc_DeprecationWarning)\nEXPORT_DATA(PyExc_EncodingWarning)\nEXPORT_DATA(PyExc_EnvironmentError)\nEXPORT_DATA(PyExc_EOFError)\nEXPORT_DATA(PyExc_Exception)\nEXPORT_DATA(PyExc_FileExistsError)\nEXPORT_DATA(PyExc_FileNotFoundError)\nEXPORT_DATA(PyExc_FloatingPointError)\nEXPORT_DATA(PyExc_FutureWarning)\nEXPORT_DATA(PyExc_GeneratorExit)\nEXPORT_DATA(PyExc_ImportError)\nEXPORT_DATA(PyExc_ImportWarning)\nEXPORT_DATA(PyExc_IndentationError)\nEXPORT_DATA(PyExc_IndexError)\nEXPORT_DATA(PyExc_InterruptedError)\nEXPORT_DATA(PyExc_IOError)\nEXPORT_DATA(PyExc_IsADirectoryError)\nEXPORT_DATA(PyExc_KeyboardInterrupt)\nEXPORT_DATA(PyExc_KeyError)\nEXPORT_DATA(PyExc_LookupError)\nEXPORT_DATA(PyExc_MemoryError)\nEXPORT_DATA(PyExc_ModuleNotFoundError)\nEXPORT_DATA(PyExc_NameError)\nEXPORT_DATA(PyExc_NotADirectoryError)\nEXPORT_DATA(PyExc_NotImplementedError)\nEXPORT_DATA(PyExc_OSError)\nEXPORT_DATA(PyExc_OverflowError)\nEXPORT_DATA(PyExc_PendingDeprecationWarning)\nEXPORT_DATA(PyExc_PermissionError)\nEXPORT_DATA(PyExc_ProcessLookupError)\nEXPORT_DATA(PyExc_RecursionError)\nEXPORT_DATA(PyExc_ReferenceError)\nEXPORT_DATA(PyExc_ResourceWarning)\nEXPORT_DATA(PyExc_RuntimeError)\nEXPORT_DATA(PyExc_RuntimeWarning)\nEXPORT_DATA(PyExc_StopAsyncIteration)\nEXPORT_DATA(PyExc_StopIteration)\nEXPORT_DATA(PyExc_SyntaxError)\nEXPORT_DATA(PyExc_SyntaxWarning)\nEXPORT_DATA(PyExc_SystemError)\nEXPORT_DATA(PyExc_SystemExit)\nEXPORT_DATA(PyExc_TabError)\nEXPORT_DATA(PyExc_TimeoutError)\nEXPORT_DATA(PyExc_TypeError)\nEXPORT_DATA(PyExc_UnboundLocalError)\nEXPORT_DATA(PyExc_UnicodeDecodeError)\nEXPORT_DATA(PyExc_UnicodeEncodeError)\nEXPORT_DATA(PyExc_UnicodeError)\nEXPORT_DATA(PyExc_UnicodeTranslateError)\nEXPORT_DATA(PyExc_UnicodeWarning)\nEXPORT_DATA(PyExc_UserWarning)\nEXPORT_DATA(PyExc_ValueError)\nEXPORT_DATA(PyExc_Warning)\nEXPORT_DATA(PyExc_WindowsError)\nEXPORT_DATA(PyExc_ZeroDivisionError)\nEXPORT_DATA(PyFilter_Type)\nEXPORT_DATA(PyFloat_Type)\nEXPORT_DATA(PyFrozenSet_Type)\nEXPORT_DATA(PyGetSetDescr_Type)\nEXPORT_DATA(PyList_Type)\nEXPORT_DATA(PyListIter_Type)\nEXPORT_DATA(PyListRevIter_Type)\nEXPORT_DATA(PyLong_Type)\nEXPORT_DATA(PyLongRangeIter_Type)\nEXPORT_DATA(PyMap_Type)\nEXPORT_DATA(PyMemberDescr_Type)\nEXPORT_DATA(PyMemoryView_Type)\nEXPORT_DATA(PyMethodDescr_Type)\nEXPORT_DATA(PyModule_Type)\nEXPORT_DATA(PyModuleDef_Type)\nEXPORT_DATA(PyOS_InputHook)\nEXPORT_DATA(PyProperty_Type)\nEXPORT_DATA(PyRange_Type)\nEXPORT_DATA(PyRangeIter_Type)\nEXPORT_DATA(PyReversed_Type)\nEXPORT_DATA(PySeqIter_Type)\nEXPORT_DATA(PySet_Type)\nEXPORT_DATA(PySetIter_Type)\nEXPORT_DATA(PySlice_Type)\nEXPORT_DATA(PyStructSequence_UnnamedField)\nEXPORT_DATA(PySuper_Type)\nEXPORT_DATA(PyTraceBack_Type)\nEXPORT_DATA(PyTuple_Type)\nEXPORT_DATA(PyTupleIter_Type)\nEXPORT_DATA(PyType_Type)\nEXPORT_DATA(PyUnicode_Type)\nEXPORT_DATA(PyUnicodeIter_Type)\nEXPORT_DATA(PyWrapperDescr_Type)\nEXPORT_DATA(PyZip_Type)\n", + "version": "3.13.17" + } + ], + "sha256": "57a93c420485d823edb29ee0630d93bc38a4e833622cc2305c3be356af41bb18", + "source_commit": "64e080d38154391ff2d2caa1161c298bb7cd8f30", + "version": "3.13.17" + }, + "retained_objects": [ + "Unwind-seh.cpp.obj", + "UnwindLevel1-gcc-ext.c.obj", + "UnwindLevel1.c.obj", + "UnwindRegistersSave.S.obj", + "crtbegin.o", + "crtend.o", + "dllcrt2.o", + "libarm64_libmingw32_a-cinitexe.o", + "libarm64_libmingw32_a-crtdll_atexit_register.o", + "libarm64_libmingw32_a-mingw_dll_ctors_dtors.o", + "libarm64_libmingw32_a-mingw_helpers.o", + "libarm64_libmingw32_a-natstart.o", + "libarm64_libmingw32_a-pseudo-reloc-list.o", + "libarm64_libmingw32_a-pseudo-reloc.o", + "libarm64_libmingw32_a-tlssup.o", + "libarm64_libmingwex_a-dllmain.o", + "libarm64_libmingwex_a-loadcfg.o", + "libarm64_libmingwex_a-mingw_cfguard_support.o", + "libarm64_libucrt_extra_a-ucrt___local_stdio_printf_options.o", + "libarm64_libucrt_extra_a-ucrt_fprintf.o", + "libarm64_libucrt_extra_a-ucrt_vfprintf.o", + "libunwind.cpp.obj" + ], + "sdk_inputs": [ + { + "path": "aarch64-w64-mingw32/lib/crtbegin.o", + "sha256": "4ba912486f310ab85d7d06a90d4c46c22485edce0255a03a1f74c0c8510eff9e", + "size": 1309 + }, + { + "path": "aarch64-w64-mingw32/lib/crtend.o", + "sha256": "eefb57102e2168200514c7565f27b14fe519bb5a853e7f00d3e4dd2a5c777470", + "size": 1309 + }, + { + "path": "aarch64-w64-mingw32/lib/dllcrt2.o", + "sha256": "bf86f93b1ce6b401fc837edbac68e8b9ae474a3af335c67ba1659febd25305a6", + "size": 42872 + }, + { + "path": "aarch64-w64-mingw32/lib/libdbghelp.a", + "sha256": "b5d39301a039d48a1513c6222e5254efd96eb641508fac9a4635cd5e5da27968", + "size": 103246 + }, + { + "path": "aarch64-w64-mingw32/lib/libkernel32.a", + "sha256": "f5197cec7cea31be9847bfb6c3912bea507319be63740a6887d02a0b6bbdd893", + "size": 1009086 + }, + { + "path": "aarch64-w64-mingw32/lib/libmingw32.a", + "sha256": "ff45b29605c4c9de82d6ad40fcc7fe4139b37467df10541b286336f7929257c3", + "size": 339550 + }, + { + "path": "aarch64-w64-mingw32/lib/libmingwex.a", + "sha256": "d1108040c5e08d84d58108f4dfe40cb3793a0eee61d0a1fa6919f8469be5ea73", + "size": 4168698 + }, + { + "path": "aarch64-w64-mingw32/lib/libmsvcrt.a", + "sha256": "062c0d06384cb8f8db2666eda5e50dab9a300e2e662f9dda2711bd48576ae34d", + "size": 1796926 + }, + { + "path": "aarch64-w64-mingw32/lib/libntdll.a", + "sha256": "3c91ccf64aa2c8d2fa47bedf45531ec2c972ff37200eb541afb16d7259cce96c", + "size": 1126014 + }, + { + "path": "aarch64-w64-mingw32/lib/libunwind.a", + "sha256": "1a983632d86eb33d808534b4853e5185f31419adc787048c9f5bc3e76a79d9b9", + "size": 231528 + }, + { + "path": "aarch64-w64-mingw32/lib/libuser32.a", + "sha256": "fd1f8ad6376a7d57a01c9d0361f89ca038c8c03dde43b8298f094e73f24c6bf9", + "size": 434306 + }, + { + "path": "aarch64-w64-mingw32/lib/libuserenv.a", + "sha256": "fbc1da1ed5165754594e48fa8e1199a0fbe395e6ab29531d33633c14725789c5", + "size": 40108 + }, + { + "path": "aarch64-w64-mingw32/lib/libws2_32.a", + "sha256": "04d7f776550e3732b37cef7b2759862e82b2485e99cc9baf354df573da489483", + "size": 411636 + }, + { + "path": "lib/clang/23/lib/windows/libclang_rt.builtins-aarch64.a", + "sha256": "0eadf5121c8d6c9169769130d8e83a549251b91bd2686150d75f3914eb8ac6df", + "size": 760636 + } + ] + }, + "x86_64-pc-windows-gnullvm": { + "cpython_import_library": { + "bytes": 194246, + "file": "python3.lib", + "import_kinds": { + "data": 143, + "func": 759 + }, + "import_records": 902, + "modification_summary": "No changes to Python source or its stable-ABI declarations. Ktav uses the installed CPython stable-ABI import library; the CPython interpreter and its implementation/runtime DLLs are not redistributed in this wheel.", + "ordinary_non_executable_metadata_members": [ + [ + { + "bytes": 65, + "name": ".debug$S" + }, + { + "bytes": 20, + "name": ".idata$2" + }, + { + "bytes": 12, + "name": ".idata$6" + } + ], + [ + { + "bytes": 65, + "name": ".debug$S" + }, + { + "bytes": 20, + "name": ".idata$3" + } + ], + [ + { + "bytes": 65, + "name": ".debug$S" + }, + { + "bytes": 8, + "name": ".idata$5" + }, + { + "bytes": 8, + "name": ".idata$4" + } + ] + ], + "original_materials": [ + { + "bytes": 13809, + "commit": "cbc944f4bc59639a444dd971c737788ba2283a91", + "git_blob": "14603b95c2e23b5a4cd6ff93f50348d1817f7638", + "path": "LICENSE", + "sha256": "78b12c3a81360b357002334f0e70ea0e92eebf7a9b358805c03c48484945f3bb", + "source": "https://raw.githubusercontent.com/python/cpython/cbc944f4bc59639a444dd971c737788ba2283a91/LICENSE", + "text": "A. HISTORY OF THE SOFTWARE\n==========================\n\nPython was created in the early 1990s by Guido van Rossum at Stichting\nMathematisch Centrum (CWI, see https://www.cwi.nl) in the Netherlands\nas a successor of a language called ABC. Guido remains Python's\nprincipal author, although it includes many contributions from others.\n\nIn 1995, Guido continued his work on Python at the Corporation for\nNational Research Initiatives (CNRI, see https://www.cnri.reston.va.us)\nin Reston, Virginia where he released several versions of the\nsoftware.\n\nIn May 2000, Guido and the Python core development team moved to\nBeOpen.com to form the BeOpen PythonLabs team. In October of the same\nyear, the PythonLabs team moved to Digital Creations, which became\nZope Corporation. In 2001, the Python Software Foundation (PSF, see\nhttps://www.python.org/psf/) was formed, a non-profit organization\ncreated specifically to own Python-related Intellectual Property.\nZope Corporation was a sponsoring member of the PSF.\n\nAll Python releases are Open Source (see https://opensource.org for\nthe Open Source Definition). Historically, most, but not all, Python\nreleases have also been GPL-compatible; the table below summarizes\nthe various releases.\n\n Release Derived Year Owner GPL-\n from compatible? (1)\n\n 0.9.0 thru 1.2 1991-1995 CWI yes\n 1.3 thru 1.5.2 1.2 1995-1999 CNRI yes\n 1.6 1.5.2 2000 CNRI no\n 2.0 1.6 2000 BeOpen.com no\n 1.6.1 1.6 2001 CNRI yes (2)\n 2.1 2.0+1.6.1 2001 PSF no\n 2.0.1 2.0+1.6.1 2001 PSF yes\n 2.1.1 2.1+2.0.1 2001 PSF yes\n 2.1.2 2.1.1 2002 PSF yes\n 2.1.3 2.1.2 2002 PSF yes\n 2.2 and above 2.1.1 2001-now PSF yes\n\nFootnotes:\n\n(1) GPL-compatible doesn't mean that we're distributing Python under\n the GPL. All Python licenses, unlike the GPL, let you distribute\n a modified version without making your changes open source. The\n GPL-compatible licenses make it possible to combine Python with\n other software that is released under the GPL; the others don't.\n\n(2) According to Richard Stallman, 1.6.1 is not GPL-compatible,\n because its license has a choice of law clause. According to\n CNRI, however, Stallman's lawyer has told CNRI's lawyer that 1.6.1\n is \"not incompatible\" with the GPL.\n\nThanks to the many outside volunteers who have worked under Guido's\ndirection to make these releases possible.\n\n\nB. TERMS AND CONDITIONS FOR ACCESSING OR OTHERWISE USING PYTHON\n===============================================================\n\nPython software and documentation are licensed under the\nPython Software Foundation License Version 2.\n\nStarting with Python 3.8.6, examples, recipes, and other code in\nthe documentation are dual licensed under the PSF License Version 2\nand the Zero-Clause BSD license.\n\nSome software incorporated into Python is under different licenses.\nThe licenses are listed with code falling under that license.\n\n\nPYTHON SOFTWARE FOUNDATION LICENSE VERSION 2\n--------------------------------------------\n\n1. This LICENSE AGREEMENT is between the Python Software Foundation\n(\"PSF\"), and the Individual or Organization (\"Licensee\") accessing and\notherwise using this software (\"Python\") in source or binary form and\nits associated documentation.\n\n2. Subject to the terms and conditions of this License Agreement, PSF hereby\ngrants Licensee a nonexclusive, royalty-free, world-wide license to reproduce,\nanalyze, test, perform and/or display publicly, prepare derivative works,\ndistribute, and otherwise use Python alone or in any derivative version,\nprovided, however, that PSF's License Agreement and PSF's notice of copyright,\ni.e., \"Copyright (c) 2001-2024 Python Software Foundation; All Rights Reserved\"\nare retained in Python alone or in any derivative version prepared by Licensee.\n\n3. In the event Licensee prepares a derivative work that is based on\nor incorporates Python or any part thereof, and wants to make\nthe derivative work available to others as provided herein, then\nLicensee hereby agrees to include in any such work a brief summary of\nthe changes made to Python.\n\n4. PSF is making Python available to Licensee on an \"AS IS\"\nbasis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR\nIMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND\nDISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS\nFOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT\nINFRINGE ANY THIRD PARTY RIGHTS.\n\n5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON\nFOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS\nA RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON,\nOR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.\n\n6. This License Agreement will automatically terminate upon a material\nbreach of its terms and conditions.\n\n7. Nothing in this License Agreement shall be deemed to create any\nrelationship of agency, partnership, or joint venture between PSF and\nLicensee. This License Agreement does not grant permission to use PSF\ntrademarks or trade name in a trademark sense to endorse or promote\nproducts or services of Licensee, or any third party.\n\n8. By copying, installing or otherwise using Python, Licensee\nagrees to be bound by the terms and conditions of this License\nAgreement.\n\n\nBEOPEN.COM LICENSE AGREEMENT FOR PYTHON 2.0\n-------------------------------------------\n\nBEOPEN PYTHON OPEN SOURCE LICENSE AGREEMENT VERSION 1\n\n1. This LICENSE AGREEMENT is between BeOpen.com (\"BeOpen\"), having an\noffice at 160 Saratoga Avenue, Santa Clara, CA 95051, and the\nIndividual or Organization (\"Licensee\") accessing and otherwise using\nthis software in source or binary form and its associated\ndocumentation (\"the Software\").\n\n2. Subject to the terms and conditions of this BeOpen Python License\nAgreement, BeOpen hereby grants Licensee a non-exclusive,\nroyalty-free, world-wide license to reproduce, analyze, test, perform\nand/or display publicly, prepare derivative works, distribute, and\notherwise use the Software alone or in any derivative version,\nprovided, however, that the BeOpen Python License is retained in the\nSoftware, alone or in any derivative version prepared by Licensee.\n\n3. BeOpen is making the Software available to Licensee on an \"AS IS\"\nbasis. BEOPEN MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR\nIMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, BEOPEN MAKES NO AND\nDISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS\nFOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE WILL NOT\nINFRINGE ANY THIRD PARTY RIGHTS.\n\n4. BEOPEN SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE\nSOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS\nAS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY\nDERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.\n\n5. This License Agreement will automatically terminate upon a material\nbreach of its terms and conditions.\n\n6. This License Agreement shall be governed by and interpreted in all\nrespects by the law of the State of California, excluding conflict of\nlaw provisions. Nothing in this License Agreement shall be deemed to\ncreate any relationship of agency, partnership, or joint venture\nbetween BeOpen and Licensee. This License Agreement does not grant\npermission to use BeOpen trademarks or trade names in a trademark\nsense to endorse or promote products or services of Licensee, or any\nthird party. As an exception, the \"BeOpen Python\" logos available at\nhttp://www.pythonlabs.com/logos.html may be used according to the\npermissions granted on that web page.\n\n7. By copying, installing or otherwise using the software, Licensee\nagrees to be bound by the terms and conditions of this License\nAgreement.\n\n\nCNRI LICENSE AGREEMENT FOR PYTHON 1.6.1\n---------------------------------------\n\n1. This LICENSE AGREEMENT is between the Corporation for National\nResearch Initiatives, having an office at 1895 Preston White Drive,\nReston, VA 20191 (\"CNRI\"), and the Individual or Organization\n(\"Licensee\") accessing and otherwise using Python 1.6.1 software in\nsource or binary form and its associated documentation.\n\n2. Subject to the terms and conditions of this License Agreement, CNRI\nhereby grants Licensee a nonexclusive, royalty-free, world-wide\nlicense to reproduce, analyze, test, perform and/or display publicly,\nprepare derivative works, distribute, and otherwise use Python 1.6.1\nalone or in any derivative version, provided, however, that CNRI's\nLicense Agreement and CNRI's notice of copyright, i.e., \"Copyright (c)\n1995-2001 Corporation for National Research Initiatives; All Rights\nReserved\" are retained in Python 1.6.1 alone or in any derivative\nversion prepared by Licensee. Alternately, in lieu of CNRI's License\nAgreement, Licensee may substitute the following text (omitting the\nquotes): \"Python 1.6.1 is made available subject to the terms and\nconditions in CNRI's License Agreement. This Agreement together with\nPython 1.6.1 may be located on the internet using the following\nunique, persistent identifier (known as a handle): 1895.22/1013. This\nAgreement may also be obtained from a proxy server on the internet\nusing the following URL: http://hdl.handle.net/1895.22/1013\".\n\n3. In the event Licensee prepares a derivative work that is based on\nor incorporates Python 1.6.1 or any part thereof, and wants to make\nthe derivative work available to others as provided herein, then\nLicensee hereby agrees to include in any such work a brief summary of\nthe changes made to Python 1.6.1.\n\n4. CNRI is making Python 1.6.1 available to Licensee on an \"AS IS\"\nbasis. CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR\nIMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND\nDISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS\nFOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 1.6.1 WILL NOT\nINFRINGE ANY THIRD PARTY RIGHTS.\n\n5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON\n1.6.1 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS\nA RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 1.6.1,\nOR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.\n\n6. This License Agreement will automatically terminate upon a material\nbreach of its terms and conditions.\n\n7. This License Agreement shall be governed by the federal\nintellectual property law of the United States, including without\nlimitation the federal copyright law, and, to the extent such\nU.S. federal law does not apply, by the law of the Commonwealth of\nVirginia, excluding Virginia's conflict of law provisions.\nNotwithstanding the foregoing, with regard to derivative works based\non Python 1.6.1 that incorporate non-separable material that was\npreviously distributed under the GNU General Public License (GPL), the\nlaw of the Commonwealth of Virginia shall govern this License\nAgreement only as to issues arising under or with respect to\nParagraphs 4, 5, and 7 of this License Agreement. Nothing in this\nLicense Agreement shall be deemed to create any relationship of\nagency, partnership, or joint venture between CNRI and Licensee. This\nLicense Agreement does not grant permission to use CNRI trademarks or\ntrade name in a trademark sense to endorse or promote products or\nservices of Licensee, or any third party.\n\n8. By clicking on the \"ACCEPT\" button where indicated, or by copying,\ninstalling or otherwise using Python 1.6.1, Licensee agrees to be\nbound by the terms and conditions of this License Agreement.\n\n ACCEPT\n\n\nCWI LICENSE AGREEMENT FOR PYTHON 0.9.0 THROUGH 1.2\n--------------------------------------------------\n\nCopyright (c) 1991 - 1995, Stichting Mathematisch Centrum Amsterdam,\nThe Netherlands. All rights reserved.\n\nPermission to use, copy, modify, and distribute this software and its\ndocumentation for any purpose and without fee is hereby granted,\nprovided that the above copyright notice appear in all copies and that\nboth that copyright notice and this permission notice appear in\nsupporting documentation, and that the name of Stichting Mathematisch\nCentrum or CWI not be used in advertising or publicity pertaining to\ndistribution of the software without specific, written prior\npermission.\n\nSTICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO\nTHIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND\nFITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE\nFOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\nWHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\nACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT\nOF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n\nZERO-CLAUSE BSD LICENSE FOR CODE IN THE PYTHON DOCUMENTATION\n----------------------------------------------------------------------\n\nPermission to use, copy, modify, and/or distribute this software for any\npurpose with or without fee is hereby granted.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH\nREGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY\nAND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,\nINDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM\nLOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR\nOTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR\nPERFORMANCE OF THIS SOFTWARE.\n", + "version": "3.13.16" + }, + { + "bytes": 30185, + "commit": "cbc944f4bc59639a444dd971c737788ba2283a91", + "git_blob": "eb077df70a57d6aa0853d927b86b55826a07ee50", + "path": "PC/python3dll.c", + "sha256": "73614c8ef518ba5c13c2867caeb280e12834af64ee7965b3707dfea2d4de5fcf", + "source": "https://raw.githubusercontent.com/python/cpython/cbc944f4bc59639a444dd971c737788ba2283a91/PC/python3dll.c", + "text": "/* Re-export stable Python ABI */\n\n/* Generated by Tools/build/stable_abi.py */\n\n#ifdef _M_IX86\n#define DECORATE \"_\"\n#else\n#define DECORATE\n#endif\n\n#define EXPORT_FUNC(name) \\\n __pragma(comment(linker, \"/EXPORT:\" DECORATE #name \"=\" PYTHON_DLL_NAME \".\" #name))\n#define EXPORT_DATA(name) \\\n __pragma(comment(linker, \"/EXPORT:\" DECORATE #name \"=\" PYTHON_DLL_NAME \".\" #name \",DATA\"))\n\nEXPORT_FUNC(_Py_BuildValue_SizeT)\nEXPORT_FUNC(_Py_CheckRecursiveCall)\nEXPORT_FUNC(_Py_Dealloc)\nEXPORT_FUNC(_Py_DecRef)\nEXPORT_FUNC(_Py_IncRef)\nEXPORT_FUNC(_Py_NegativeRefcount)\nEXPORT_FUNC(_Py_SetRefcnt)\nEXPORT_FUNC(_Py_VaBuildValue_SizeT)\nEXPORT_FUNC(_PyArg_Parse_SizeT)\nEXPORT_FUNC(_PyArg_ParseTuple_SizeT)\nEXPORT_FUNC(_PyArg_ParseTupleAndKeywords_SizeT)\nEXPORT_FUNC(_PyArg_VaParse_SizeT)\nEXPORT_FUNC(_PyArg_VaParseTupleAndKeywords_SizeT)\nEXPORT_FUNC(_PyErr_BadInternalCall)\nEXPORT_FUNC(_PyObject_CallFunction_SizeT)\nEXPORT_FUNC(_PyObject_CallMethod_SizeT)\nEXPORT_FUNC(_PyObject_GC_New)\nEXPORT_FUNC(_PyObject_GC_NewVar)\nEXPORT_FUNC(_PyObject_GC_Resize)\nEXPORT_FUNC(_PyObject_New)\nEXPORT_FUNC(_PyObject_NewVar)\nEXPORT_FUNC(_PyState_AddModule)\nEXPORT_FUNC(_PyThreadState_Init)\nEXPORT_FUNC(_PyThreadState_Prealloc)\nEXPORT_FUNC(Py_AddPendingCall)\nEXPORT_FUNC(Py_AtExit)\nEXPORT_FUNC(Py_BuildValue)\nEXPORT_FUNC(Py_BytesMain)\nEXPORT_FUNC(Py_CompileString)\nEXPORT_FUNC(Py_DecodeLocale)\nEXPORT_FUNC(Py_DecRef)\nEXPORT_FUNC(Py_EncodeLocale)\nEXPORT_FUNC(Py_EndInterpreter)\nEXPORT_FUNC(Py_EnterRecursiveCall)\nEXPORT_FUNC(Py_Exit)\nEXPORT_FUNC(Py_FatalError)\nEXPORT_FUNC(Py_Finalize)\nEXPORT_FUNC(Py_FinalizeEx)\nEXPORT_FUNC(Py_GenericAlias)\nEXPORT_FUNC(Py_GetArgcArgv)\nEXPORT_FUNC(Py_GetBuildInfo)\nEXPORT_FUNC(Py_GetCompiler)\nEXPORT_FUNC(Py_GetConstant)\nEXPORT_FUNC(Py_GetConstantBorrowed)\nEXPORT_FUNC(Py_GetCopyright)\nEXPORT_FUNC(Py_GetExecPrefix)\nEXPORT_FUNC(Py_GetPath)\nEXPORT_FUNC(Py_GetPlatform)\nEXPORT_FUNC(Py_GetPrefix)\nEXPORT_FUNC(Py_GetProgramFullPath)\nEXPORT_FUNC(Py_GetProgramName)\nEXPORT_FUNC(Py_GetPythonHome)\nEXPORT_FUNC(Py_GetRecursionLimit)\nEXPORT_FUNC(Py_GetVersion)\nEXPORT_FUNC(Py_IncRef)\nEXPORT_FUNC(Py_Initialize)\nEXPORT_FUNC(Py_InitializeEx)\nEXPORT_FUNC(Py_Is)\nEXPORT_FUNC(Py_IsFalse)\nEXPORT_FUNC(Py_IsFinalizing)\nEXPORT_FUNC(Py_IsInitialized)\nEXPORT_FUNC(Py_IsNone)\nEXPORT_FUNC(Py_IsTrue)\nEXPORT_FUNC(Py_LeaveRecursiveCall)\nEXPORT_FUNC(Py_Main)\nEXPORT_FUNC(Py_MakePendingCalls)\nEXPORT_FUNC(Py_NewInterpreter)\nEXPORT_FUNC(Py_NewRef)\nEXPORT_FUNC(Py_ReprEnter)\nEXPORT_FUNC(Py_ReprLeave)\nEXPORT_FUNC(Py_SetPath)\nEXPORT_FUNC(Py_SetProgramName)\nEXPORT_FUNC(Py_SetPythonHome)\nEXPORT_FUNC(Py_SetRecursionLimit)\nEXPORT_FUNC(Py_VaBuildValue)\nEXPORT_FUNC(Py_XNewRef)\nEXPORT_FUNC(PyAIter_Check)\nEXPORT_FUNC(PyArg_Parse)\nEXPORT_FUNC(PyArg_ParseTuple)\nEXPORT_FUNC(PyArg_ParseTupleAndKeywords)\nEXPORT_FUNC(PyArg_UnpackTuple)\nEXPORT_FUNC(PyArg_ValidateKeywordArguments)\nEXPORT_FUNC(PyArg_VaParse)\nEXPORT_FUNC(PyArg_VaParseTupleAndKeywords)\nEXPORT_FUNC(PyBool_FromLong)\nEXPORT_FUNC(PyBuffer_FillContiguousStrides)\nEXPORT_FUNC(PyBuffer_FillInfo)\nEXPORT_FUNC(PyBuffer_FromContiguous)\nEXPORT_FUNC(PyBuffer_GetPointer)\nEXPORT_FUNC(PyBuffer_IsContiguous)\nEXPORT_FUNC(PyBuffer_Release)\nEXPORT_FUNC(PyBuffer_SizeFromFormat)\nEXPORT_FUNC(PyBuffer_ToContiguous)\nEXPORT_FUNC(PyByteArray_AsString)\nEXPORT_FUNC(PyByteArray_Concat)\nEXPORT_FUNC(PyByteArray_FromObject)\nEXPORT_FUNC(PyByteArray_FromStringAndSize)\nEXPORT_FUNC(PyByteArray_Resize)\nEXPORT_FUNC(PyByteArray_Size)\nEXPORT_FUNC(PyBytes_AsString)\nEXPORT_FUNC(PyBytes_AsStringAndSize)\nEXPORT_FUNC(PyBytes_Concat)\nEXPORT_FUNC(PyBytes_ConcatAndDel)\nEXPORT_FUNC(PyBytes_DecodeEscape)\nEXPORT_FUNC(PyBytes_FromFormat)\nEXPORT_FUNC(PyBytes_FromFormatV)\nEXPORT_FUNC(PyBytes_FromObject)\nEXPORT_FUNC(PyBytes_FromString)\nEXPORT_FUNC(PyBytes_FromStringAndSize)\nEXPORT_FUNC(PyBytes_Repr)\nEXPORT_FUNC(PyBytes_Size)\nEXPORT_FUNC(PyCallable_Check)\nEXPORT_FUNC(PyCallIter_New)\nEXPORT_FUNC(PyCapsule_GetContext)\nEXPORT_FUNC(PyCapsule_GetDestructor)\nEXPORT_FUNC(PyCapsule_GetName)\nEXPORT_FUNC(PyCapsule_GetPointer)\nEXPORT_FUNC(PyCapsule_Import)\nEXPORT_FUNC(PyCapsule_IsValid)\nEXPORT_FUNC(PyCapsule_New)\nEXPORT_FUNC(PyCapsule_SetContext)\nEXPORT_FUNC(PyCapsule_SetDestructor)\nEXPORT_FUNC(PyCapsule_SetName)\nEXPORT_FUNC(PyCapsule_SetPointer)\nEXPORT_FUNC(PyCFunction_Call)\nEXPORT_FUNC(PyCFunction_GetFlags)\nEXPORT_FUNC(PyCFunction_GetFunction)\nEXPORT_FUNC(PyCFunction_GetSelf)\nEXPORT_FUNC(PyCFunction_New)\nEXPORT_FUNC(PyCFunction_NewEx)\nEXPORT_FUNC(PyCMethod_New)\nEXPORT_FUNC(PyCodec_BackslashReplaceErrors)\nEXPORT_FUNC(PyCodec_Decode)\nEXPORT_FUNC(PyCodec_Decoder)\nEXPORT_FUNC(PyCodec_Encode)\nEXPORT_FUNC(PyCodec_Encoder)\nEXPORT_FUNC(PyCodec_IgnoreErrors)\nEXPORT_FUNC(PyCodec_IncrementalDecoder)\nEXPORT_FUNC(PyCodec_IncrementalEncoder)\nEXPORT_FUNC(PyCodec_KnownEncoding)\nEXPORT_FUNC(PyCodec_LookupError)\nEXPORT_FUNC(PyCodec_NameReplaceErrors)\nEXPORT_FUNC(PyCodec_Register)\nEXPORT_FUNC(PyCodec_RegisterError)\nEXPORT_FUNC(PyCodec_ReplaceErrors)\nEXPORT_FUNC(PyCodec_StreamReader)\nEXPORT_FUNC(PyCodec_StreamWriter)\nEXPORT_FUNC(PyCodec_StrictErrors)\nEXPORT_FUNC(PyCodec_Unregister)\nEXPORT_FUNC(PyCodec_XMLCharRefReplaceErrors)\nEXPORT_FUNC(PyComplex_FromDoubles)\nEXPORT_FUNC(PyComplex_ImagAsDouble)\nEXPORT_FUNC(PyComplex_RealAsDouble)\nEXPORT_FUNC(PyDescr_NewClassMethod)\nEXPORT_FUNC(PyDescr_NewGetSet)\nEXPORT_FUNC(PyDescr_NewMember)\nEXPORT_FUNC(PyDescr_NewMethod)\nEXPORT_FUNC(PyDict_Clear)\nEXPORT_FUNC(PyDict_Contains)\nEXPORT_FUNC(PyDict_Copy)\nEXPORT_FUNC(PyDict_DelItem)\nEXPORT_FUNC(PyDict_DelItemString)\nEXPORT_FUNC(PyDict_GetItem)\nEXPORT_FUNC(PyDict_GetItemRef)\nEXPORT_FUNC(PyDict_GetItemString)\nEXPORT_FUNC(PyDict_GetItemStringRef)\nEXPORT_FUNC(PyDict_GetItemWithError)\nEXPORT_FUNC(PyDict_Items)\nEXPORT_FUNC(PyDict_Keys)\nEXPORT_FUNC(PyDict_Merge)\nEXPORT_FUNC(PyDict_MergeFromSeq2)\nEXPORT_FUNC(PyDict_New)\nEXPORT_FUNC(PyDict_Next)\nEXPORT_FUNC(PyDict_SetItem)\nEXPORT_FUNC(PyDict_SetItemString)\nEXPORT_FUNC(PyDict_Size)\nEXPORT_FUNC(PyDict_Update)\nEXPORT_FUNC(PyDict_Values)\nEXPORT_FUNC(PyDictProxy_New)\nEXPORT_FUNC(PyErr_BadArgument)\nEXPORT_FUNC(PyErr_BadInternalCall)\nEXPORT_FUNC(PyErr_CheckSignals)\nEXPORT_FUNC(PyErr_Clear)\nEXPORT_FUNC(PyErr_Display)\nEXPORT_FUNC(PyErr_DisplayException)\nEXPORT_FUNC(PyErr_ExceptionMatches)\nEXPORT_FUNC(PyErr_Fetch)\nEXPORT_FUNC(PyErr_Format)\nEXPORT_FUNC(PyErr_FormatV)\nEXPORT_FUNC(PyErr_GetExcInfo)\nEXPORT_FUNC(PyErr_GetHandledException)\nEXPORT_FUNC(PyErr_GetRaisedException)\nEXPORT_FUNC(PyErr_GivenExceptionMatches)\nEXPORT_FUNC(PyErr_NewException)\nEXPORT_FUNC(PyErr_NewExceptionWithDoc)\nEXPORT_FUNC(PyErr_NoMemory)\nEXPORT_FUNC(PyErr_NormalizeException)\nEXPORT_FUNC(PyErr_Occurred)\nEXPORT_FUNC(PyErr_Print)\nEXPORT_FUNC(PyErr_PrintEx)\nEXPORT_FUNC(PyErr_ProgramText)\nEXPORT_FUNC(PyErr_ResourceWarning)\nEXPORT_FUNC(PyErr_Restore)\nEXPORT_FUNC(PyErr_SetExcFromWindowsErr)\nEXPORT_FUNC(PyErr_SetExcFromWindowsErrWithFilename)\nEXPORT_FUNC(PyErr_SetExcFromWindowsErrWithFilenameObject)\nEXPORT_FUNC(PyErr_SetExcFromWindowsErrWithFilenameObjects)\nEXPORT_FUNC(PyErr_SetExcInfo)\nEXPORT_FUNC(PyErr_SetFromErrno)\nEXPORT_FUNC(PyErr_SetFromErrnoWithFilename)\nEXPORT_FUNC(PyErr_SetFromErrnoWithFilenameObject)\nEXPORT_FUNC(PyErr_SetFromErrnoWithFilenameObjects)\nEXPORT_FUNC(PyErr_SetFromWindowsErr)\nEXPORT_FUNC(PyErr_SetFromWindowsErrWithFilename)\nEXPORT_FUNC(PyErr_SetHandledException)\nEXPORT_FUNC(PyErr_SetImportError)\nEXPORT_FUNC(PyErr_SetImportErrorSubclass)\nEXPORT_FUNC(PyErr_SetInterrupt)\nEXPORT_FUNC(PyErr_SetInterruptEx)\nEXPORT_FUNC(PyErr_SetNone)\nEXPORT_FUNC(PyErr_SetObject)\nEXPORT_FUNC(PyErr_SetRaisedException)\nEXPORT_FUNC(PyErr_SetString)\nEXPORT_FUNC(PyErr_SyntaxLocation)\nEXPORT_FUNC(PyErr_SyntaxLocationEx)\nEXPORT_FUNC(PyErr_WarnEx)\nEXPORT_FUNC(PyErr_WarnExplicit)\nEXPORT_FUNC(PyErr_WarnFormat)\nEXPORT_FUNC(PyErr_WriteUnraisable)\nEXPORT_FUNC(PyEval_AcquireLock)\nEXPORT_FUNC(PyEval_AcquireThread)\nEXPORT_FUNC(PyEval_CallFunction)\nEXPORT_FUNC(PyEval_CallMethod)\nEXPORT_FUNC(PyEval_CallObjectWithKeywords)\nEXPORT_FUNC(PyEval_EvalCode)\nEXPORT_FUNC(PyEval_EvalCodeEx)\nEXPORT_FUNC(PyEval_EvalFrame)\nEXPORT_FUNC(PyEval_EvalFrameEx)\nEXPORT_FUNC(PyEval_GetBuiltins)\nEXPORT_FUNC(PyEval_GetFrame)\nEXPORT_FUNC(PyEval_GetFrameBuiltins)\nEXPORT_FUNC(PyEval_GetFrameGlobals)\nEXPORT_FUNC(PyEval_GetFrameLocals)\nEXPORT_FUNC(PyEval_GetFuncDesc)\nEXPORT_FUNC(PyEval_GetFuncName)\nEXPORT_FUNC(PyEval_GetGlobals)\nEXPORT_FUNC(PyEval_GetLocals)\nEXPORT_FUNC(PyEval_InitThreads)\nEXPORT_FUNC(PyEval_ReleaseLock)\nEXPORT_FUNC(PyEval_ReleaseThread)\nEXPORT_FUNC(PyEval_RestoreThread)\nEXPORT_FUNC(PyEval_SaveThread)\nEXPORT_FUNC(PyEval_ThreadsInitialized)\nEXPORT_FUNC(PyException_GetArgs)\nEXPORT_FUNC(PyException_GetCause)\nEXPORT_FUNC(PyException_GetContext)\nEXPORT_FUNC(PyException_GetTraceback)\nEXPORT_FUNC(PyException_SetArgs)\nEXPORT_FUNC(PyException_SetCause)\nEXPORT_FUNC(PyException_SetContext)\nEXPORT_FUNC(PyException_SetTraceback)\nEXPORT_FUNC(PyExceptionClass_Name)\nEXPORT_FUNC(PyFile_FromFd)\nEXPORT_FUNC(PyFile_GetLine)\nEXPORT_FUNC(PyFile_WriteObject)\nEXPORT_FUNC(PyFile_WriteString)\nEXPORT_FUNC(PyFloat_AsDouble)\nEXPORT_FUNC(PyFloat_FromDouble)\nEXPORT_FUNC(PyFloat_FromString)\nEXPORT_FUNC(PyFloat_GetInfo)\nEXPORT_FUNC(PyFloat_GetMax)\nEXPORT_FUNC(PyFloat_GetMin)\nEXPORT_FUNC(PyFrame_GetCode)\nEXPORT_FUNC(PyFrame_GetLineNumber)\nEXPORT_FUNC(PyFrozenSet_New)\nEXPORT_FUNC(PyGC_Collect)\nEXPORT_FUNC(PyGC_Disable)\nEXPORT_FUNC(PyGC_Enable)\nEXPORT_FUNC(PyGC_IsEnabled)\nEXPORT_FUNC(PyGILState_Ensure)\nEXPORT_FUNC(PyGILState_GetThisThreadState)\nEXPORT_FUNC(PyGILState_Release)\nEXPORT_FUNC(PyImport_AddModule)\nEXPORT_FUNC(PyImport_AddModuleObject)\nEXPORT_FUNC(PyImport_AddModuleRef)\nEXPORT_FUNC(PyImport_AppendInittab)\nEXPORT_FUNC(PyImport_ExecCodeModule)\nEXPORT_FUNC(PyImport_ExecCodeModuleEx)\nEXPORT_FUNC(PyImport_ExecCodeModuleObject)\nEXPORT_FUNC(PyImport_ExecCodeModuleWithPathnames)\nEXPORT_FUNC(PyImport_GetImporter)\nEXPORT_FUNC(PyImport_GetMagicNumber)\nEXPORT_FUNC(PyImport_GetMagicTag)\nEXPORT_FUNC(PyImport_GetModule)\nEXPORT_FUNC(PyImport_GetModuleDict)\nEXPORT_FUNC(PyImport_Import)\nEXPORT_FUNC(PyImport_ImportFrozenModule)\nEXPORT_FUNC(PyImport_ImportFrozenModuleObject)\nEXPORT_FUNC(PyImport_ImportModule)\nEXPORT_FUNC(PyImport_ImportModuleLevel)\nEXPORT_FUNC(PyImport_ImportModuleLevelObject)\nEXPORT_FUNC(PyImport_ImportModuleNoBlock)\nEXPORT_FUNC(PyImport_ReloadModule)\nEXPORT_FUNC(PyIndex_Check)\nEXPORT_FUNC(PyInterpreterState_Clear)\nEXPORT_FUNC(PyInterpreterState_Delete)\nEXPORT_FUNC(PyInterpreterState_Get)\nEXPORT_FUNC(PyInterpreterState_GetDict)\nEXPORT_FUNC(PyInterpreterState_GetID)\nEXPORT_FUNC(PyInterpreterState_New)\nEXPORT_FUNC(PyIter_Check)\nEXPORT_FUNC(PyIter_Next)\nEXPORT_FUNC(PyIter_Send)\nEXPORT_FUNC(PyList_Append)\nEXPORT_FUNC(PyList_AsTuple)\nEXPORT_FUNC(PyList_GetItem)\nEXPORT_FUNC(PyList_GetItemRef)\nEXPORT_FUNC(PyList_GetSlice)\nEXPORT_FUNC(PyList_Insert)\nEXPORT_FUNC(PyList_New)\nEXPORT_FUNC(PyList_Reverse)\nEXPORT_FUNC(PyList_SetItem)\nEXPORT_FUNC(PyList_SetSlice)\nEXPORT_FUNC(PyList_Size)\nEXPORT_FUNC(PyList_Sort)\nEXPORT_FUNC(PyLong_AsDouble)\nEXPORT_FUNC(PyLong_AsInt)\nEXPORT_FUNC(PyLong_AsLong)\nEXPORT_FUNC(PyLong_AsLongAndOverflow)\nEXPORT_FUNC(PyLong_AsLongLong)\nEXPORT_FUNC(PyLong_AsLongLongAndOverflow)\nEXPORT_FUNC(PyLong_AsSize_t)\nEXPORT_FUNC(PyLong_AsSsize_t)\nEXPORT_FUNC(PyLong_AsUnsignedLong)\nEXPORT_FUNC(PyLong_AsUnsignedLongLong)\nEXPORT_FUNC(PyLong_AsUnsignedLongLongMask)\nEXPORT_FUNC(PyLong_AsUnsignedLongMask)\nEXPORT_FUNC(PyLong_AsVoidPtr)\nEXPORT_FUNC(PyLong_FromDouble)\nEXPORT_FUNC(PyLong_FromLong)\nEXPORT_FUNC(PyLong_FromLongLong)\nEXPORT_FUNC(PyLong_FromSize_t)\nEXPORT_FUNC(PyLong_FromSsize_t)\nEXPORT_FUNC(PyLong_FromString)\nEXPORT_FUNC(PyLong_FromUnsignedLong)\nEXPORT_FUNC(PyLong_FromUnsignedLongLong)\nEXPORT_FUNC(PyLong_FromVoidPtr)\nEXPORT_FUNC(PyLong_GetInfo)\nEXPORT_FUNC(PyMapping_Check)\nEXPORT_FUNC(PyMapping_GetItemString)\nEXPORT_FUNC(PyMapping_GetOptionalItem)\nEXPORT_FUNC(PyMapping_GetOptionalItemString)\nEXPORT_FUNC(PyMapping_HasKey)\nEXPORT_FUNC(PyMapping_HasKeyString)\nEXPORT_FUNC(PyMapping_HasKeyStringWithError)\nEXPORT_FUNC(PyMapping_HasKeyWithError)\nEXPORT_FUNC(PyMapping_Items)\nEXPORT_FUNC(PyMapping_Keys)\nEXPORT_FUNC(PyMapping_Length)\nEXPORT_FUNC(PyMapping_SetItemString)\nEXPORT_FUNC(PyMapping_Size)\nEXPORT_FUNC(PyMapping_Values)\nEXPORT_FUNC(PyMarshal_ReadObjectFromString)\nEXPORT_FUNC(PyMarshal_WriteObjectToString)\nEXPORT_FUNC(PyMem_Calloc)\nEXPORT_FUNC(PyMem_Free)\nEXPORT_FUNC(PyMem_Malloc)\nEXPORT_FUNC(PyMem_RawCalloc)\nEXPORT_FUNC(PyMem_RawFree)\nEXPORT_FUNC(PyMem_RawMalloc)\nEXPORT_FUNC(PyMem_RawRealloc)\nEXPORT_FUNC(PyMem_Realloc)\nEXPORT_FUNC(PyMember_GetOne)\nEXPORT_FUNC(PyMember_SetOne)\nEXPORT_FUNC(PyMemoryView_FromBuffer)\nEXPORT_FUNC(PyMemoryView_FromMemory)\nEXPORT_FUNC(PyMemoryView_FromObject)\nEXPORT_FUNC(PyMemoryView_GetContiguous)\nEXPORT_FUNC(PyModule_Add)\nEXPORT_FUNC(PyModule_AddFunctions)\nEXPORT_FUNC(PyModule_AddIntConstant)\nEXPORT_FUNC(PyModule_AddObject)\nEXPORT_FUNC(PyModule_AddObjectRef)\nEXPORT_FUNC(PyModule_AddStringConstant)\nEXPORT_FUNC(PyModule_AddType)\nEXPORT_FUNC(PyModule_Create2)\nEXPORT_FUNC(PyModule_ExecDef)\nEXPORT_FUNC(PyModule_FromDefAndSpec2)\nEXPORT_FUNC(PyModule_GetDef)\nEXPORT_FUNC(PyModule_GetDict)\nEXPORT_FUNC(PyModule_GetFilename)\nEXPORT_FUNC(PyModule_GetFilenameObject)\nEXPORT_FUNC(PyModule_GetName)\nEXPORT_FUNC(PyModule_GetNameObject)\nEXPORT_FUNC(PyModule_GetState)\nEXPORT_FUNC(PyModule_New)\nEXPORT_FUNC(PyModule_NewObject)\nEXPORT_FUNC(PyModule_SetDocString)\nEXPORT_FUNC(PyModuleDef_Init)\nEXPORT_FUNC(PyNumber_Absolute)\nEXPORT_FUNC(PyNumber_Add)\nEXPORT_FUNC(PyNumber_And)\nEXPORT_FUNC(PyNumber_AsSsize_t)\nEXPORT_FUNC(PyNumber_Check)\nEXPORT_FUNC(PyNumber_Divmod)\nEXPORT_FUNC(PyNumber_Float)\nEXPORT_FUNC(PyNumber_FloorDivide)\nEXPORT_FUNC(PyNumber_Index)\nEXPORT_FUNC(PyNumber_InPlaceAdd)\nEXPORT_FUNC(PyNumber_InPlaceAnd)\nEXPORT_FUNC(PyNumber_InPlaceFloorDivide)\nEXPORT_FUNC(PyNumber_InPlaceLshift)\nEXPORT_FUNC(PyNumber_InPlaceMatrixMultiply)\nEXPORT_FUNC(PyNumber_InPlaceMultiply)\nEXPORT_FUNC(PyNumber_InPlaceOr)\nEXPORT_FUNC(PyNumber_InPlacePower)\nEXPORT_FUNC(PyNumber_InPlaceRemainder)\nEXPORT_FUNC(PyNumber_InPlaceRshift)\nEXPORT_FUNC(PyNumber_InPlaceSubtract)\nEXPORT_FUNC(PyNumber_InPlaceTrueDivide)\nEXPORT_FUNC(PyNumber_InPlaceXor)\nEXPORT_FUNC(PyNumber_Invert)\nEXPORT_FUNC(PyNumber_Long)\nEXPORT_FUNC(PyNumber_Lshift)\nEXPORT_FUNC(PyNumber_MatrixMultiply)\nEXPORT_FUNC(PyNumber_Multiply)\nEXPORT_FUNC(PyNumber_Negative)\nEXPORT_FUNC(PyNumber_Or)\nEXPORT_FUNC(PyNumber_Positive)\nEXPORT_FUNC(PyNumber_Power)\nEXPORT_FUNC(PyNumber_Remainder)\nEXPORT_FUNC(PyNumber_Rshift)\nEXPORT_FUNC(PyNumber_Subtract)\nEXPORT_FUNC(PyNumber_ToBase)\nEXPORT_FUNC(PyNumber_TrueDivide)\nEXPORT_FUNC(PyNumber_Xor)\nEXPORT_FUNC(PyObject_AsCharBuffer)\nEXPORT_FUNC(PyObject_ASCII)\nEXPORT_FUNC(PyObject_AsFileDescriptor)\nEXPORT_FUNC(PyObject_AsReadBuffer)\nEXPORT_FUNC(PyObject_AsWriteBuffer)\nEXPORT_FUNC(PyObject_Bytes)\nEXPORT_FUNC(PyObject_Call)\nEXPORT_FUNC(PyObject_CallFunction)\nEXPORT_FUNC(PyObject_CallFunctionObjArgs)\nEXPORT_FUNC(PyObject_CallMethod)\nEXPORT_FUNC(PyObject_CallMethodObjArgs)\nEXPORT_FUNC(PyObject_CallNoArgs)\nEXPORT_FUNC(PyObject_CallObject)\nEXPORT_FUNC(PyObject_Calloc)\nEXPORT_FUNC(PyObject_CheckBuffer)\nEXPORT_FUNC(PyObject_CheckReadBuffer)\nEXPORT_FUNC(PyObject_ClearWeakRefs)\nEXPORT_FUNC(PyObject_CopyData)\nEXPORT_FUNC(PyObject_DelAttr)\nEXPORT_FUNC(PyObject_DelAttrString)\nEXPORT_FUNC(PyObject_DelItem)\nEXPORT_FUNC(PyObject_DelItemString)\nEXPORT_FUNC(PyObject_Dir)\nEXPORT_FUNC(PyObject_Format)\nEXPORT_FUNC(PyObject_Free)\nEXPORT_FUNC(PyObject_GC_Del)\nEXPORT_FUNC(PyObject_GC_IsFinalized)\nEXPORT_FUNC(PyObject_GC_IsTracked)\nEXPORT_FUNC(PyObject_GC_Track)\nEXPORT_FUNC(PyObject_GC_UnTrack)\nEXPORT_FUNC(PyObject_GenericGetAttr)\nEXPORT_FUNC(PyObject_GenericGetDict)\nEXPORT_FUNC(PyObject_GenericSetAttr)\nEXPORT_FUNC(PyObject_GenericSetDict)\nEXPORT_FUNC(PyObject_GetAIter)\nEXPORT_FUNC(PyObject_GetAttr)\nEXPORT_FUNC(PyObject_GetAttrString)\nEXPORT_FUNC(PyObject_GetBuffer)\nEXPORT_FUNC(PyObject_GetItem)\nEXPORT_FUNC(PyObject_GetIter)\nEXPORT_FUNC(PyObject_GetOptionalAttr)\nEXPORT_FUNC(PyObject_GetOptionalAttrString)\nEXPORT_FUNC(PyObject_GetTypeData)\nEXPORT_FUNC(PyObject_HasAttr)\nEXPORT_FUNC(PyObject_HasAttrString)\nEXPORT_FUNC(PyObject_HasAttrStringWithError)\nEXPORT_FUNC(PyObject_HasAttrWithError)\nEXPORT_FUNC(PyObject_Hash)\nEXPORT_FUNC(PyObject_HashNotImplemented)\nEXPORT_FUNC(PyObject_Init)\nEXPORT_FUNC(PyObject_InitVar)\nEXPORT_FUNC(PyObject_IsInstance)\nEXPORT_FUNC(PyObject_IsSubclass)\nEXPORT_FUNC(PyObject_IsTrue)\nEXPORT_FUNC(PyObject_Length)\nEXPORT_FUNC(PyObject_Malloc)\nEXPORT_FUNC(PyObject_Not)\nEXPORT_FUNC(PyObject_Realloc)\nEXPORT_FUNC(PyObject_Repr)\nEXPORT_FUNC(PyObject_RichCompare)\nEXPORT_FUNC(PyObject_RichCompareBool)\nEXPORT_FUNC(PyObject_SelfIter)\nEXPORT_FUNC(PyObject_SetAttr)\nEXPORT_FUNC(PyObject_SetAttrString)\nEXPORT_FUNC(PyObject_SetItem)\nEXPORT_FUNC(PyObject_Size)\nEXPORT_FUNC(PyObject_Str)\nEXPORT_FUNC(PyObject_Type)\nEXPORT_FUNC(PyObject_Vectorcall)\nEXPORT_FUNC(PyObject_VectorcallMethod)\nEXPORT_FUNC(PyOS_CheckStack)\nEXPORT_FUNC(PyOS_double_to_string)\nEXPORT_FUNC(PyOS_FSPath)\nEXPORT_FUNC(PyOS_getsig)\nEXPORT_FUNC(PyOS_InterruptOccurred)\nEXPORT_FUNC(PyOS_mystricmp)\nEXPORT_FUNC(PyOS_mystrnicmp)\nEXPORT_FUNC(PyOS_setsig)\nEXPORT_FUNC(PyOS_snprintf)\nEXPORT_FUNC(PyOS_string_to_double)\nEXPORT_FUNC(PyOS_strtol)\nEXPORT_FUNC(PyOS_strtoul)\nEXPORT_FUNC(PyOS_vsnprintf)\nEXPORT_FUNC(PySeqIter_New)\nEXPORT_FUNC(PySequence_Check)\nEXPORT_FUNC(PySequence_Concat)\nEXPORT_FUNC(PySequence_Contains)\nEXPORT_FUNC(PySequence_Count)\nEXPORT_FUNC(PySequence_DelItem)\nEXPORT_FUNC(PySequence_DelSlice)\nEXPORT_FUNC(PySequence_Fast)\nEXPORT_FUNC(PySequence_GetItem)\nEXPORT_FUNC(PySequence_GetSlice)\nEXPORT_FUNC(PySequence_In)\nEXPORT_FUNC(PySequence_Index)\nEXPORT_FUNC(PySequence_InPlaceConcat)\nEXPORT_FUNC(PySequence_InPlaceRepeat)\nEXPORT_FUNC(PySequence_Length)\nEXPORT_FUNC(PySequence_List)\nEXPORT_FUNC(PySequence_Repeat)\nEXPORT_FUNC(PySequence_SetItem)\nEXPORT_FUNC(PySequence_SetSlice)\nEXPORT_FUNC(PySequence_Size)\nEXPORT_FUNC(PySequence_Tuple)\nEXPORT_FUNC(PySet_Add)\nEXPORT_FUNC(PySet_Clear)\nEXPORT_FUNC(PySet_Contains)\nEXPORT_FUNC(PySet_Discard)\nEXPORT_FUNC(PySet_New)\nEXPORT_FUNC(PySet_Pop)\nEXPORT_FUNC(PySet_Size)\nEXPORT_FUNC(PySlice_AdjustIndices)\nEXPORT_FUNC(PySlice_GetIndices)\nEXPORT_FUNC(PySlice_GetIndicesEx)\nEXPORT_FUNC(PySlice_New)\nEXPORT_FUNC(PySlice_Unpack)\nEXPORT_FUNC(PyState_AddModule)\nEXPORT_FUNC(PyState_FindModule)\nEXPORT_FUNC(PyState_RemoveModule)\nEXPORT_FUNC(PyStructSequence_GetItem)\nEXPORT_FUNC(PyStructSequence_New)\nEXPORT_FUNC(PyStructSequence_NewType)\nEXPORT_FUNC(PyStructSequence_SetItem)\nEXPORT_FUNC(PySys_AddWarnOption)\nEXPORT_FUNC(PySys_AddWarnOptionUnicode)\nEXPORT_FUNC(PySys_AddXOption)\nEXPORT_FUNC(PySys_Audit)\nEXPORT_FUNC(PySys_AuditTuple)\nEXPORT_FUNC(PySys_FormatStderr)\nEXPORT_FUNC(PySys_FormatStdout)\nEXPORT_FUNC(PySys_GetObject)\nEXPORT_FUNC(PySys_GetXOptions)\nEXPORT_FUNC(PySys_HasWarnOptions)\nEXPORT_FUNC(PySys_ResetWarnOptions)\nEXPORT_FUNC(PySys_SetArgv)\nEXPORT_FUNC(PySys_SetArgvEx)\nEXPORT_FUNC(PySys_SetObject)\nEXPORT_FUNC(PySys_SetPath)\nEXPORT_FUNC(PySys_WriteStderr)\nEXPORT_FUNC(PySys_WriteStdout)\nEXPORT_FUNC(PyThread_acquire_lock)\nEXPORT_FUNC(PyThread_acquire_lock_timed)\nEXPORT_FUNC(PyThread_allocate_lock)\nEXPORT_FUNC(PyThread_create_key)\nEXPORT_FUNC(PyThread_delete_key)\nEXPORT_FUNC(PyThread_delete_key_value)\nEXPORT_FUNC(PyThread_exit_thread)\nEXPORT_FUNC(PyThread_free_lock)\nEXPORT_FUNC(PyThread_get_key_value)\nEXPORT_FUNC(PyThread_get_stacksize)\nEXPORT_FUNC(PyThread_get_thread_ident)\nEXPORT_FUNC(PyThread_get_thread_native_id)\nEXPORT_FUNC(PyThread_GetInfo)\nEXPORT_FUNC(PyThread_init_thread)\nEXPORT_FUNC(PyThread_ReInitTLS)\nEXPORT_FUNC(PyThread_release_lock)\nEXPORT_FUNC(PyThread_set_key_value)\nEXPORT_FUNC(PyThread_set_stacksize)\nEXPORT_FUNC(PyThread_start_new_thread)\nEXPORT_FUNC(PyThread_tss_alloc)\nEXPORT_FUNC(PyThread_tss_create)\nEXPORT_FUNC(PyThread_tss_delete)\nEXPORT_FUNC(PyThread_tss_free)\nEXPORT_FUNC(PyThread_tss_get)\nEXPORT_FUNC(PyThread_tss_is_created)\nEXPORT_FUNC(PyThread_tss_set)\nEXPORT_FUNC(PyThreadState_Clear)\nEXPORT_FUNC(PyThreadState_Delete)\nEXPORT_FUNC(PyThreadState_DeleteCurrent)\nEXPORT_FUNC(PyThreadState_Get)\nEXPORT_FUNC(PyThreadState_GetDict)\nEXPORT_FUNC(PyThreadState_GetFrame)\nEXPORT_FUNC(PyThreadState_GetID)\nEXPORT_FUNC(PyThreadState_GetInterpreter)\nEXPORT_FUNC(PyThreadState_New)\nEXPORT_FUNC(PyThreadState_SetAsyncExc)\nEXPORT_FUNC(PyThreadState_Swap)\nEXPORT_FUNC(PyTraceBack_Here)\nEXPORT_FUNC(PyTraceBack_Print)\nEXPORT_FUNC(PyTuple_GetItem)\nEXPORT_FUNC(PyTuple_GetSlice)\nEXPORT_FUNC(PyTuple_New)\nEXPORT_FUNC(PyTuple_Pack)\nEXPORT_FUNC(PyTuple_SetItem)\nEXPORT_FUNC(PyTuple_Size)\nEXPORT_FUNC(PyType_ClearCache)\nEXPORT_FUNC(PyType_FromMetaclass)\nEXPORT_FUNC(PyType_FromModuleAndSpec)\nEXPORT_FUNC(PyType_FromSpec)\nEXPORT_FUNC(PyType_FromSpecWithBases)\nEXPORT_FUNC(PyType_GenericAlloc)\nEXPORT_FUNC(PyType_GenericNew)\nEXPORT_FUNC(PyType_GetFlags)\nEXPORT_FUNC(PyType_GetFullyQualifiedName)\nEXPORT_FUNC(PyType_GetModule)\nEXPORT_FUNC(PyType_GetModuleByDef)\nEXPORT_FUNC(PyType_GetModuleName)\nEXPORT_FUNC(PyType_GetModuleState)\nEXPORT_FUNC(PyType_GetName)\nEXPORT_FUNC(PyType_GetQualName)\nEXPORT_FUNC(PyType_GetSlot)\nEXPORT_FUNC(PyType_GetTypeDataSize)\nEXPORT_FUNC(PyType_IsSubtype)\nEXPORT_FUNC(PyType_Modified)\nEXPORT_FUNC(PyType_Ready)\nEXPORT_FUNC(PyUnicode_Append)\nEXPORT_FUNC(PyUnicode_AppendAndDel)\nEXPORT_FUNC(PyUnicode_AsASCIIString)\nEXPORT_FUNC(PyUnicode_AsCharmapString)\nEXPORT_FUNC(PyUnicode_AsDecodedObject)\nEXPORT_FUNC(PyUnicode_AsDecodedUnicode)\nEXPORT_FUNC(PyUnicode_AsEncodedObject)\nEXPORT_FUNC(PyUnicode_AsEncodedString)\nEXPORT_FUNC(PyUnicode_AsEncodedUnicode)\nEXPORT_FUNC(PyUnicode_AsLatin1String)\nEXPORT_FUNC(PyUnicode_AsMBCSString)\nEXPORT_FUNC(PyUnicode_AsRawUnicodeEscapeString)\nEXPORT_FUNC(PyUnicode_AsUCS4)\nEXPORT_FUNC(PyUnicode_AsUCS4Copy)\nEXPORT_FUNC(PyUnicode_AsUnicodeEscapeString)\nEXPORT_FUNC(PyUnicode_AsUTF16String)\nEXPORT_FUNC(PyUnicode_AsUTF32String)\nEXPORT_FUNC(PyUnicode_AsUTF8AndSize)\nEXPORT_FUNC(PyUnicode_AsUTF8String)\nEXPORT_FUNC(PyUnicode_AsWideChar)\nEXPORT_FUNC(PyUnicode_AsWideCharString)\nEXPORT_FUNC(PyUnicode_BuildEncodingMap)\nEXPORT_FUNC(PyUnicode_Compare)\nEXPORT_FUNC(PyUnicode_CompareWithASCIIString)\nEXPORT_FUNC(PyUnicode_Concat)\nEXPORT_FUNC(PyUnicode_Contains)\nEXPORT_FUNC(PyUnicode_Count)\nEXPORT_FUNC(PyUnicode_Decode)\nEXPORT_FUNC(PyUnicode_DecodeASCII)\nEXPORT_FUNC(PyUnicode_DecodeCharmap)\nEXPORT_FUNC(PyUnicode_DecodeCodePageStateful)\nEXPORT_FUNC(PyUnicode_DecodeFSDefault)\nEXPORT_FUNC(PyUnicode_DecodeFSDefaultAndSize)\nEXPORT_FUNC(PyUnicode_DecodeLatin1)\nEXPORT_FUNC(PyUnicode_DecodeLocale)\nEXPORT_FUNC(PyUnicode_DecodeLocaleAndSize)\nEXPORT_FUNC(PyUnicode_DecodeMBCS)\nEXPORT_FUNC(PyUnicode_DecodeMBCSStateful)\nEXPORT_FUNC(PyUnicode_DecodeRawUnicodeEscape)\nEXPORT_FUNC(PyUnicode_DecodeUnicodeEscape)\nEXPORT_FUNC(PyUnicode_DecodeUTF16)\nEXPORT_FUNC(PyUnicode_DecodeUTF16Stateful)\nEXPORT_FUNC(PyUnicode_DecodeUTF32)\nEXPORT_FUNC(PyUnicode_DecodeUTF32Stateful)\nEXPORT_FUNC(PyUnicode_DecodeUTF7)\nEXPORT_FUNC(PyUnicode_DecodeUTF7Stateful)\nEXPORT_FUNC(PyUnicode_DecodeUTF8)\nEXPORT_FUNC(PyUnicode_DecodeUTF8Stateful)\nEXPORT_FUNC(PyUnicode_EncodeCodePage)\nEXPORT_FUNC(PyUnicode_EncodeFSDefault)\nEXPORT_FUNC(PyUnicode_EncodeLocale)\nEXPORT_FUNC(PyUnicode_EqualToUTF8)\nEXPORT_FUNC(PyUnicode_EqualToUTF8AndSize)\nEXPORT_FUNC(PyUnicode_Find)\nEXPORT_FUNC(PyUnicode_FindChar)\nEXPORT_FUNC(PyUnicode_Format)\nEXPORT_FUNC(PyUnicode_FromEncodedObject)\nEXPORT_FUNC(PyUnicode_FromFormat)\nEXPORT_FUNC(PyUnicode_FromFormatV)\nEXPORT_FUNC(PyUnicode_FromObject)\nEXPORT_FUNC(PyUnicode_FromOrdinal)\nEXPORT_FUNC(PyUnicode_FromString)\nEXPORT_FUNC(PyUnicode_FromStringAndSize)\nEXPORT_FUNC(PyUnicode_FromWideChar)\nEXPORT_FUNC(PyUnicode_FSConverter)\nEXPORT_FUNC(PyUnicode_FSDecoder)\nEXPORT_FUNC(PyUnicode_GetDefaultEncoding)\nEXPORT_FUNC(PyUnicode_GetLength)\nEXPORT_FUNC(PyUnicode_GetSize)\nEXPORT_FUNC(PyUnicode_InternFromString)\nEXPORT_FUNC(PyUnicode_InternImmortal)\nEXPORT_FUNC(PyUnicode_InternInPlace)\nEXPORT_FUNC(PyUnicode_IsIdentifier)\nEXPORT_FUNC(PyUnicode_Join)\nEXPORT_FUNC(PyUnicode_Partition)\nEXPORT_FUNC(PyUnicode_ReadChar)\nEXPORT_FUNC(PyUnicode_Replace)\nEXPORT_FUNC(PyUnicode_Resize)\nEXPORT_FUNC(PyUnicode_RichCompare)\nEXPORT_FUNC(PyUnicode_RPartition)\nEXPORT_FUNC(PyUnicode_RSplit)\nEXPORT_FUNC(PyUnicode_Split)\nEXPORT_FUNC(PyUnicode_Splitlines)\nEXPORT_FUNC(PyUnicode_Substring)\nEXPORT_FUNC(PyUnicode_Tailmatch)\nEXPORT_FUNC(PyUnicode_Translate)\nEXPORT_FUNC(PyUnicode_WriteChar)\nEXPORT_FUNC(PyUnicodeDecodeError_Create)\nEXPORT_FUNC(PyUnicodeDecodeError_GetEncoding)\nEXPORT_FUNC(PyUnicodeDecodeError_GetEnd)\nEXPORT_FUNC(PyUnicodeDecodeError_GetObject)\nEXPORT_FUNC(PyUnicodeDecodeError_GetReason)\nEXPORT_FUNC(PyUnicodeDecodeError_GetStart)\nEXPORT_FUNC(PyUnicodeDecodeError_SetEnd)\nEXPORT_FUNC(PyUnicodeDecodeError_SetReason)\nEXPORT_FUNC(PyUnicodeDecodeError_SetStart)\nEXPORT_FUNC(PyUnicodeEncodeError_GetEncoding)\nEXPORT_FUNC(PyUnicodeEncodeError_GetEnd)\nEXPORT_FUNC(PyUnicodeEncodeError_GetObject)\nEXPORT_FUNC(PyUnicodeEncodeError_GetReason)\nEXPORT_FUNC(PyUnicodeEncodeError_GetStart)\nEXPORT_FUNC(PyUnicodeEncodeError_SetEnd)\nEXPORT_FUNC(PyUnicodeEncodeError_SetReason)\nEXPORT_FUNC(PyUnicodeEncodeError_SetStart)\nEXPORT_FUNC(PyUnicodeTranslateError_GetEnd)\nEXPORT_FUNC(PyUnicodeTranslateError_GetObject)\nEXPORT_FUNC(PyUnicodeTranslateError_GetReason)\nEXPORT_FUNC(PyUnicodeTranslateError_GetStart)\nEXPORT_FUNC(PyUnicodeTranslateError_SetEnd)\nEXPORT_FUNC(PyUnicodeTranslateError_SetReason)\nEXPORT_FUNC(PyUnicodeTranslateError_SetStart)\nEXPORT_FUNC(PyVectorcall_Call)\nEXPORT_FUNC(PyVectorcall_NARGS)\nEXPORT_FUNC(PyWeakref_GetObject)\nEXPORT_FUNC(PyWeakref_GetRef)\nEXPORT_FUNC(PyWeakref_NewProxy)\nEXPORT_FUNC(PyWeakref_NewRef)\nEXPORT_FUNC(PyWrapper_New)\n\nEXPORT_DATA(_Py_EllipsisObject)\nEXPORT_DATA(_Py_FalseStruct)\nEXPORT_DATA(_Py_NoneStruct)\nEXPORT_DATA(_Py_NotImplementedStruct)\nEXPORT_DATA(_Py_RefTotal)\nEXPORT_DATA(_Py_SwappedOp)\nEXPORT_DATA(_Py_TrueStruct)\nEXPORT_DATA(_PyWeakref_CallableProxyType)\nEXPORT_DATA(_PyWeakref_ProxyType)\nEXPORT_DATA(_PyWeakref_RefType)\nEXPORT_DATA(Py_FileSystemDefaultEncodeErrors)\nEXPORT_DATA(Py_FileSystemDefaultEncoding)\nEXPORT_DATA(Py_GenericAliasType)\nEXPORT_DATA(Py_HasFileSystemDefaultEncoding)\nEXPORT_DATA(Py_UTF8Mode)\nEXPORT_DATA(Py_Version)\nEXPORT_DATA(PyBaseObject_Type)\nEXPORT_DATA(PyBool_Type)\nEXPORT_DATA(PyByteArray_Type)\nEXPORT_DATA(PyByteArrayIter_Type)\nEXPORT_DATA(PyBytes_Type)\nEXPORT_DATA(PyBytesIter_Type)\nEXPORT_DATA(PyCallIter_Type)\nEXPORT_DATA(PyCapsule_Type)\nEXPORT_DATA(PyCFunction_Type)\nEXPORT_DATA(PyClassMethodDescr_Type)\nEXPORT_DATA(PyComplex_Type)\nEXPORT_DATA(PyDict_Type)\nEXPORT_DATA(PyDictItems_Type)\nEXPORT_DATA(PyDictIterItem_Type)\nEXPORT_DATA(PyDictIterKey_Type)\nEXPORT_DATA(PyDictIterValue_Type)\nEXPORT_DATA(PyDictKeys_Type)\nEXPORT_DATA(PyDictProxy_Type)\nEXPORT_DATA(PyDictRevIterItem_Type)\nEXPORT_DATA(PyDictRevIterKey_Type)\nEXPORT_DATA(PyDictRevIterValue_Type)\nEXPORT_DATA(PyDictValues_Type)\nEXPORT_DATA(PyEllipsis_Type)\nEXPORT_DATA(PyEnum_Type)\nEXPORT_DATA(PyExc_ArithmeticError)\nEXPORT_DATA(PyExc_AssertionError)\nEXPORT_DATA(PyExc_AttributeError)\nEXPORT_DATA(PyExc_BaseException)\nEXPORT_DATA(PyExc_BaseExceptionGroup)\nEXPORT_DATA(PyExc_BlockingIOError)\nEXPORT_DATA(PyExc_BrokenPipeError)\nEXPORT_DATA(PyExc_BufferError)\nEXPORT_DATA(PyExc_BytesWarning)\nEXPORT_DATA(PyExc_ChildProcessError)\nEXPORT_DATA(PyExc_ConnectionAbortedError)\nEXPORT_DATA(PyExc_ConnectionError)\nEXPORT_DATA(PyExc_ConnectionRefusedError)\nEXPORT_DATA(PyExc_ConnectionResetError)\nEXPORT_DATA(PyExc_DeprecationWarning)\nEXPORT_DATA(PyExc_EncodingWarning)\nEXPORT_DATA(PyExc_EnvironmentError)\nEXPORT_DATA(PyExc_EOFError)\nEXPORT_DATA(PyExc_Exception)\nEXPORT_DATA(PyExc_FileExistsError)\nEXPORT_DATA(PyExc_FileNotFoundError)\nEXPORT_DATA(PyExc_FloatingPointError)\nEXPORT_DATA(PyExc_FutureWarning)\nEXPORT_DATA(PyExc_GeneratorExit)\nEXPORT_DATA(PyExc_ImportError)\nEXPORT_DATA(PyExc_ImportWarning)\nEXPORT_DATA(PyExc_IndentationError)\nEXPORT_DATA(PyExc_IndexError)\nEXPORT_DATA(PyExc_InterruptedError)\nEXPORT_DATA(PyExc_IOError)\nEXPORT_DATA(PyExc_IsADirectoryError)\nEXPORT_DATA(PyExc_KeyboardInterrupt)\nEXPORT_DATA(PyExc_KeyError)\nEXPORT_DATA(PyExc_LookupError)\nEXPORT_DATA(PyExc_MemoryError)\nEXPORT_DATA(PyExc_ModuleNotFoundError)\nEXPORT_DATA(PyExc_NameError)\nEXPORT_DATA(PyExc_NotADirectoryError)\nEXPORT_DATA(PyExc_NotImplementedError)\nEXPORT_DATA(PyExc_OSError)\nEXPORT_DATA(PyExc_OverflowError)\nEXPORT_DATA(PyExc_PendingDeprecationWarning)\nEXPORT_DATA(PyExc_PermissionError)\nEXPORT_DATA(PyExc_ProcessLookupError)\nEXPORT_DATA(PyExc_RecursionError)\nEXPORT_DATA(PyExc_ReferenceError)\nEXPORT_DATA(PyExc_ResourceWarning)\nEXPORT_DATA(PyExc_RuntimeError)\nEXPORT_DATA(PyExc_RuntimeWarning)\nEXPORT_DATA(PyExc_StopAsyncIteration)\nEXPORT_DATA(PyExc_StopIteration)\nEXPORT_DATA(PyExc_SyntaxError)\nEXPORT_DATA(PyExc_SyntaxWarning)\nEXPORT_DATA(PyExc_SystemError)\nEXPORT_DATA(PyExc_SystemExit)\nEXPORT_DATA(PyExc_TabError)\nEXPORT_DATA(PyExc_TimeoutError)\nEXPORT_DATA(PyExc_TypeError)\nEXPORT_DATA(PyExc_UnboundLocalError)\nEXPORT_DATA(PyExc_UnicodeDecodeError)\nEXPORT_DATA(PyExc_UnicodeEncodeError)\nEXPORT_DATA(PyExc_UnicodeError)\nEXPORT_DATA(PyExc_UnicodeTranslateError)\nEXPORT_DATA(PyExc_UnicodeWarning)\nEXPORT_DATA(PyExc_UserWarning)\nEXPORT_DATA(PyExc_ValueError)\nEXPORT_DATA(PyExc_Warning)\nEXPORT_DATA(PyExc_WindowsError)\nEXPORT_DATA(PyExc_ZeroDivisionError)\nEXPORT_DATA(PyFilter_Type)\nEXPORT_DATA(PyFloat_Type)\nEXPORT_DATA(PyFrozenSet_Type)\nEXPORT_DATA(PyGetSetDescr_Type)\nEXPORT_DATA(PyList_Type)\nEXPORT_DATA(PyListIter_Type)\nEXPORT_DATA(PyListRevIter_Type)\nEXPORT_DATA(PyLong_Type)\nEXPORT_DATA(PyLongRangeIter_Type)\nEXPORT_DATA(PyMap_Type)\nEXPORT_DATA(PyMemberDescr_Type)\nEXPORT_DATA(PyMemoryView_Type)\nEXPORT_DATA(PyMethodDescr_Type)\nEXPORT_DATA(PyModule_Type)\nEXPORT_DATA(PyModuleDef_Type)\nEXPORT_DATA(PyOS_InputHook)\nEXPORT_DATA(PyProperty_Type)\nEXPORT_DATA(PyRange_Type)\nEXPORT_DATA(PyRangeIter_Type)\nEXPORT_DATA(PyReversed_Type)\nEXPORT_DATA(PySeqIter_Type)\nEXPORT_DATA(PySet_Type)\nEXPORT_DATA(PySetIter_Type)\nEXPORT_DATA(PySlice_Type)\nEXPORT_DATA(PyStructSequence_UnnamedField)\nEXPORT_DATA(PySuper_Type)\nEXPORT_DATA(PyTraceBack_Type)\nEXPORT_DATA(PyTuple_Type)\nEXPORT_DATA(PyTupleIter_Type)\nEXPORT_DATA(PyType_Type)\nEXPORT_DATA(PyUnicode_Type)\nEXPORT_DATA(PyUnicodeIter_Type)\nEXPORT_DATA(PyWrapperDescr_Type)\nEXPORT_DATA(PyZip_Type)\n", + "version": "3.13.16" + } + ], + "sha256": "218b31a6dedd733cdfe8ba0182cea477a6e150a02bda3deda08a261b3ef5d571", + "source_commit": "cbc944f4bc59639a444dd971c737788ba2283a91", + "version": "3.13.16" + }, + "retained_objects": [ + "Unwind-seh.cpp.obj", + "UnwindLevel1-gcc-ext.c.obj", + "UnwindLevel1.c.obj", + "UnwindRegistersSave.S.obj", + "crtbegin.o", + "crtend.o", + "dllcrt2.o", + "lib64_libmingw32_a-cinitexe.o", + "lib64_libmingw32_a-crtdll_atexit_register.o", + "lib64_libmingw32_a-mingw_dll_ctors_dtors.o", + "lib64_libmingw32_a-mingw_helpers.o", + "lib64_libmingw32_a-natstart.o", + "lib64_libmingw32_a-pseudo-reloc-list.o", + "lib64_libmingw32_a-pseudo-reloc.o", + "lib64_libmingw32_a-tlssup.o", + "lib64_libmingwex_a-dllmain.o", + "lib64_libmingwex_a-dmisc.o", + "lib64_libmingwex_a-gdtoa.o", + "lib64_libmingwex_a-gmisc.o", + "lib64_libmingwex_a-guard_dispatch.o", + "lib64_libmingwex_a-loadcfg.o", + "lib64_libmingwex_a-mingw_cfguard_support.o", + "lib64_libmingwex_a-mingw_fprintf.o", + "lib64_libmingwex_a-mingw_pformat.o", + "lib64_libmingwex_a-misc.o", + "lib64_libucrt_extra_a-ucrt___local_stdio_printf_options.o", + "lib64_libucrt_extra_a-ucrt_fprintf.o", + "lib64_libucrt_extra_a-ucrt_vfprintf.o", + "libunwind.cpp.obj" + ], + "sdk_inputs": [ + { + "path": "lib/clang/23/lib/windows/libclang_rt.builtins-x86_64.a", + "sha256": "fa6a7a18518ff5372a5f32a386ab0d689099d8c258c18eaea2431f514154c55b", + "size": 256660 + }, + { + "path": "x86_64-w64-mingw32/lib/crtbegin.o", + "sha256": "5dc28a7b0ecf42c769578f63b8d169c89c958f95c2e67b3a0d8f94b8bd3c6c60", + "size": 628 + }, + { + "path": "x86_64-w64-mingw32/lib/crtend.o", + "sha256": "c6b583eaf68b432e35507013e3213557edd2656c33916a7878c8efda18b95c19", + "size": 628 + }, + { + "path": "x86_64-w64-mingw32/lib/dllcrt2.o", + "sha256": "a846dd86db648a38ad5ee3d382295b6fa084df639a8c26cd32646ff9f71bfc47", + "size": 17632 + }, + { + "path": "x86_64-w64-mingw32/lib/libdbghelp.a", + "sha256": "f407acff38e12cb5227a6bad5f023f31c3705aca0a5339fc9b25ec433342e3cc", + "size": 49766 + }, + { + "path": "x86_64-w64-mingw32/lib/libkernel32.a", + "sha256": "6ee0041fafa7ad06bd7dc8b6b387e4ca066e76ccfeca56ae84af5a2a6d42a345", + "size": 577358 + }, + { + "path": "x86_64-w64-mingw32/lib/libmingw32.a", + "sha256": "a0aad43b481208ad1ff583102c00bd2a1e56e855b2045e282ed03845a44feef7", + "size": 139220 + }, + { + "path": "x86_64-w64-mingw32/lib/libmingwex.a", + "sha256": "df21bc3944a8766542f1b4c1ffc864e2b5dd997f2d14604eec12d3ba9258bfea", + "size": 1881848 + }, + { + "path": "x86_64-w64-mingw32/lib/libmsvcrt.a", + "sha256": "e4e38200c764bbfd217f3290f64b9bd3cc963dd782b34f3d747df27780360f62", + "size": 848642 + }, + { + "path": "x86_64-w64-mingw32/lib/libntdll.a", + "sha256": "a9e9a4eacaff6d5c186c514a8f27840df9de44c86de4dfbfe38dab0cae907cea", + "size": 545270 + }, + { + "path": "x86_64-w64-mingw32/lib/libunwind.a", + "sha256": "b37299ed084757834d7cee8d723aff6b349890d61505478602e9e981dcca839c", + "size": 91768 + }, + { + "path": "x86_64-w64-mingw32/lib/libuser32.a", + "sha256": "6b2bea0a02e6a95bcbb684ff4ede16a4a51c8bd0a55e7e15b81255c4467190bb", + "size": 207984 + }, + { + "path": "x86_64-w64-mingw32/lib/libuserenv.a", + "sha256": "c9d73aedfcbde527e9a41e9c1bb72924bd6fac5e8a4d0cae13483e8b72e2f94b", + "size": 19538 + }, + { + "path": "x86_64-w64-mingw32/lib/libws2_32.a", + "sha256": "fbb7fff6f90697b20285a1e4dfdeffb8cc5fdf7c8932ea39bc66bd722e3e33ad", + "size": 171084 + } + ] + } + } +} From 1de392531adbcf6aa7470d07a35f95af546e5b43 Mon Sep 17 00:00:00 2001 From: Arch-AI-tech Date: Sun, 1 Nov 2026 21:23:39 +0100 Subject: [PATCH 51/54] ci(licenses): reuse verified GNU Windows Python wheel producer Replace only the shared native release workflow's two MSVC Windows jobs with the proven genuine GNU producer. Preserve four non-Windows roles, original-source forwarding, package/dependency versions and all original musllinux/native/publication/download gates. Upload the same fully accepted wheels under existing windows-x86_64 and windows-aarch64 native release roles only after source/grant, RECORD/native input and genuine installed-wheel consumer gates. Validate exact combined six targets and add seven negative mutations for omitted source/CPU/library/resource/consumer/upload contracts. Generate an EN/RU/ZH installation-unit proposal only in CI. Explain actual targets and unchanged tags, no consumer build tools, complete package-owned notices, external CPython/runtime and historical limits. No generator/build/test runs locally; no actual registry/store/release publication, tag, merge, deployment or historical overwrite occurs. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/CI.yml | 9 ++- .github/workflows/native-release-build.yml | 11 ++- .github/workflows/windows-gnu-pyo3-probe.yml | 62 +++++++++++++- ci/check_python_gnu_distribution.rb | 65 +++++++++++++++ .../python-windows-native-install.md.txt | 81 +++++++++++++++++++ 5 files changed, 218 insertions(+), 10 deletions(-) create mode 100644 ci/check_python_gnu_distribution.rb create mode 100644 ci/fixtures/python-windows-native-install.md.txt diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index ea0f06b..e365a94 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -89,8 +89,12 @@ jobs: native_triggers = native_workflow['on'] || native_workflow[true] abort 'missing native workflow contract' unless native_triggers.fetch('workflow_call').fetch('inputs').fetch('source-ref').fetch('required') targets = native_workflow.fetch('jobs').fetch('native').fetch('strategy').fetch('matrix').fetch('include').map { |item| item.fetch('target') } - expected = %w[x86_64-unknown-linux-gnu aarch64-unknown-linux-gnu x86_64-apple-darwin aarch64-apple-darwin x86_64-pc-windows-msvc aarch64-pc-windows-msvc] - abort 'incomplete native release target matrix' unless targets.sort == expected.sort + windows_call = native_workflow.fetch('jobs').fetch('windows-gnu') + abort 'wrong native GNU release caller' unless windows_call.fetch('uses') == './.github/workflows/windows-gnu-pyo3-probe.yml' && windows_call.fetch('with').fetch('source-ref') == '${{ inputs.source-ref }}' + gnu = YAML.load_file('.github/workflows/windows-gnu-pyo3-probe.yml') + gnu_targets = gnu.fetch('jobs').fetch('compatibility').fetch('strategy').fetch('matrix').fetch('include').map { |item| item.fetch('target') } + expected = %w[x86_64-unknown-linux-gnu aarch64-unknown-linux-gnu x86_64-apple-darwin aarch64-apple-darwin x86_64-pc-windows-gnullvm aarch64-pc-windows-gnullvm] + abort 'incomplete combined native release target matrix' unless (targets + gnu_targets).sort == expected.sort caller = YAML.load_file('.github/workflows/release-license-check.yml').fetch('jobs').fetch('native-release') abort 'wrong native release evidence caller' unless caller.fetch('uses') == native_path abort 'native evidence source is not pinned' unless caller.fetch('with').fetch('source-ref').match?(/\A[0-9a-f]{40}\z/) @@ -103,6 +107,7 @@ jobs: abort "unsafe artifact download in #{name}" unless downloads.length == 1 && downloads[0].fetch('with').fetch('pattern') == 'wheels-*' end RUBY + ruby ci/check_python_gnu_distribution.rb rust-lint: name: Rust fmt + clippy diff --git a/.github/workflows/native-release-build.yml b/.github/workflows/native-release-build.yml index 6e5ce52..baad42f 100644 --- a/.github/workflows/native-release-build.yml +++ b/.github/workflows/native-release-build.yml @@ -12,6 +12,11 @@ permissions: contents: read jobs: + windows-gnu: + uses: ./.github/workflows/windows-gnu-pyo3-probe.yml + with: + source-ref: ${{ inputs.source-ref }} + native: name: Native release wheel (${{ matrix.label }}) runs-on: ${{ matrix.runner }} @@ -32,12 +37,6 @@ jobs: - label: macos-aarch64 target: aarch64-apple-darwin runner: macos-latest - - label: windows-x86_64 - target: x86_64-pc-windows-msvc - runner: windows-latest - - label: windows-aarch64 - target: aarch64-pc-windows-msvc - runner: windows-latest env: SOURCE_REF: ${{ inputs.source-ref }} TARGET_TRIPLE: ${{ matrix.target }} diff --git a/.github/workflows/windows-gnu-pyo3-probe.yml b/.github/workflows/windows-gnu-pyo3-probe.yml index 32a1168..d0e4225 100644 --- a/.github/workflows/windows-gnu-pyo3-probe.yml +++ b/.github/workflows/windows-gnu-pyo3-probe.yml @@ -1,14 +1,19 @@ name: Windows GNU PyO3 source-bound notice candidates on: - pull_request: + workflow_call: + inputs: + source-ref: + description: Exact reviewed original Python 0.8.1 source + required: true + type: string workflow_dispatch: permissions: contents: read env: - SOURCE_REF: 566bbfce6c6b48387d7a0a089939a26744013dea + SOURCE_REF: ${{ inputs.source-ref || '566bbfce6c6b48387d7a0a089939a26744013dea' }} jobs: compatibility: @@ -21,10 +26,12 @@ jobs: include: - os: windows-latest target: x86_64-pc-windows-gnullvm + label: windows-x86_64 python-arch: x64 python-version: '3.13.16' - os: windows-11-arm target: aarch64-pc-windows-gnullvm + label: windows-aarch64 python-arch: arm64 python-version: '3.13.17' env: @@ -399,6 +406,12 @@ jobs: name: python-gnu-notice-candidate-${{ matrix.target }} path: ${{ runner.temp }}/python-gnu-notice-candidate/ if-no-files-found: error + - name: Upload fully verified original native release wheel role + uses: actions/upload-artifact@v4 + with: + name: wheels-native-${{ matrix.label }} + path: ${{ runner.temp }}/python-gnu-notice-candidate/*.whl + if-no-files-found: error - name: Upload inert compatibility diagnostics only if: always() uses: actions/upload-artifact@v4 @@ -411,3 +424,48 @@ jobs: ${{ runner.temp }}/python-gnu-probe-evidence/*.lock ${{ runner.temp }}/python-gnu-probe-evidence/*.xml if-no-files-found: warn + + docs-proposal: + name: Generate Windows GNU runtime documentation proposal + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: '24' + cache: npm + - run: npm ci + - name: Generate and check the reviewed EN RU ZH runtime unit + shell: python + run: | + import hashlib + import json + import os + import shutil + import subprocess + from pathlib import Path + unit = Path('root-docs/README/install/body-1.md') + proposed = Path('ci/fixtures/python-windows-native-install.md.txt') + shutil.copyfile(proposed, unit) + subprocess.run(['node', 'scripts/build-docs.mjs'], check=True) + subprocess.run(['node', 'scripts/build-docs.mjs', '--check'], check=True) + changed = set(subprocess.check_output(['git', 'diff', '--name-only'], text=True).splitlines()) + names = ['README.md', 'docs/ru/README.ru.md', 'docs/zh/README.zh.md'] + if changed != {str(unit), *names}: + raise SystemExit('documentation proposal changed unrelated files') + output = Path(os.environ['RUNNER_TEMP']) / 'python-gnu-doc-proposal' + output.mkdir() + rows = [] + for name in names: + raw = Path(name).read_bytes() + destination = output / name + destination.parent.mkdir(parents=True, exist_ok=True) + destination.write_bytes(raw) + rows.append({'path': name, 'sha256': hashlib.sha256(raw).hexdigest()}) + record = {'tooling_ref': os.environ['GITHUB_SHA'], 'source_unit_sha256': hashlib.sha256(unit.read_bytes()).hexdigest(), 'files': rows} + (output / 'source-provenance.json').write_text(json.dumps(record, indent=2) + '\n', encoding='utf-8') + - uses: actions/upload-artifact@v4 + with: + name: python-gnu-doc-proposal + path: ${{ runner.temp }}/python-gnu-doc-proposal/ + if-no-files-found: error diff --git a/ci/check_python_gnu_distribution.rb b/ci/check_python_gnu_distribution.rb new file mode 100644 index 0000000..4fe46e7 --- /dev/null +++ b/ci/check_python_gnu_distribution.rb @@ -0,0 +1,65 @@ +require 'yaml' + +# Точные source/CPU/resource gates shared GNU producer. +def validate_python_gnu_contract(native, gnu) + call = native.fetch('jobs').fetch('windows-gnu') + raise 'wrong GNU caller' unless call.fetch('uses') == './.github/workflows/windows-gnu-pyo3-probe.yml' + raise 'GNU source is not forwarded' unless call.fetch('with').fetch('source-ref') == '${{ inputs.source-ref }}' + triggers = gnu['on'] || gnu[true] + raise 'missing GNU source contract' unless triggers.fetch('workflow_call').fetch('inputs').fetch('source-ref').fetch('required') + raise 'duplicate standalone GNU producer' if triggers.key?('pull_request') + raise 'GNU source environment ignores caller' unless gnu.fetch('env').fetch('SOURCE_REF').include?('inputs.source-ref') + job = gnu.fetch('jobs').fetch('compatibility') + actual = job.fetch('strategy').fetch('matrix').fetch('include').map do |item| + [item.fetch('os'), item.fetch('target'), item.fetch('label'), item.fetch('python-arch'), item.fetch('python-version')] + end + expected = [ + ['windows-latest', 'x86_64-pc-windows-gnullvm', 'windows-x86_64', 'x64', '3.13.16'], + ['windows-11-arm', 'aarch64-pc-windows-gnullvm', 'windows-aarch64', 'arm64', '3.13.17'], + ] + raise 'wrong genuine CPU/source-library matrix' unless actual.sort == expected.sort + steps = job.fetch('steps') + required = [ + 'Bind original Python source and dependency identities', + 'Install exact SDK and bind real matching-CPU CPython import libraries', + 'Prepare complete source-bound Python GNU and CPython original grants', + 'Inspect actual PE and run unchanged original native Python tests', + 'Verify complete actual wheel and exact retained-input/source-grant binding', + ] + required.each do |name| + step = steps.find { |item| item['name'] == name } + raise "missing mandatory GNU gate #{name}" unless step && !step.key?('if') && !step['continue-on-error'] + end + uploads = steps.select { |item| item['uses'].to_s.start_with?('actions/upload-artifact@') } + wheels = uploads.select { |item| item.fetch('with').fetch('name').start_with?('wheels-') } + raise 'wrong GNU native release role' unless wheels.length == 1 && wheels[0].fetch('with').fetch('name') == 'wheels-native-${{ matrix.label }}' && wheels[0].fetch('with').fetch('path').end_with?('/*.whl') && !wheels[0].key?('if') && !wheels[0]['continue-on-error'] + verify_index = steps.index { |item| item['name'] == required[-1] } + raise 'wheel upload precedes complete source/grant acceptance' unless verify_index < steps.index(wheels[0]) + true +end + +native = YAML.load_file('.github/workflows/native-release-build.yml') +gnu = YAML.load_file('.github/workflows/windows-gnu-pyo3-probe.yml') +validate_python_gnu_contract(native, gnu) +mutations = [ + ->(n, _) { n.fetch('jobs').fetch('windows-gnu').fetch('with')['source-ref'] = 'unreviewed' }, + ->(_, g) { (g['on'] || g[true]).fetch('workflow_call').fetch('inputs').fetch('source-ref')['required'] = false }, + ->(_, g) { g.fetch('jobs').fetch('compatibility').fetch('strategy').fetch('matrix').fetch('include')[1]['os'] = 'windows-latest' }, + ->(_, g) { g.fetch('jobs').fetch('compatibility').fetch('strategy').fetch('matrix').fetch('include')[0]['python-version'] = '3.13' }, + ->(_, g) { g.fetch('jobs').fetch('compatibility').fetch('steps').find { |s| s['name'] == 'Prepare complete source-bound Python GNU and CPython original grants' }['continue-on-error'] = true }, + ->(_, g) { g.fetch('jobs').fetch('compatibility').fetch('steps').find { |s| s['name'] == 'Verify complete actual wheel and exact retained-input/source-grant binding' }['if'] = 'false' }, + ->(_, g) { g.fetch('jobs').fetch('compatibility').fetch('steps').find { |s| s.dig('with', 'name') == 'wheels-native-${{ matrix.label }}' }['if'] = 'always()' }, +] +mutations.each_with_index do |mutation, index| + n = Marshal.load(Marshal.dump(native)) + g = Marshal.load(Marshal.dump(gnu)) + mutation.call(n, g) + rejected = false + begin + validate_python_gnu_contract(n, g) + rescue RuntimeError, KeyError + rejected = true + end + raise "missing GNU negative regression rejection #{index}" unless rejected +end +puts 'shared Python GNU source/CPU/original-grant/consumer/release gates verified; 7 negative mutations rejected' diff --git a/ci/fixtures/python-windows-native-install.md.txt b/ci/fixtures/python-windows-native-install.md.txt new file mode 100644 index 0000000..fe0095f --- /dev/null +++ b/ci/fixtures/python-windows-native-install.md.txt @@ -0,0 +1,81 @@ +>>>>> lang=en +## Install + +``` +pip install ktav +``` + +The default backend is native and includes our bundled Rust core in a matching platform wheel. No Python runtime dependency is required. Wheels target Python 3.9+ on supported platforms. + +If no wheel matches, install from source: the native extension is compiled with Rust through maturin, so a Rust toolchain and Python development headers are needed. Source installation does not switch to pure mode. + +The prepared 0.8.1 Windows wheels use the actual GNU/LLVM targets `x86_64-pc-windows-gnullvm` and `aarch64-pc-windows-gnullvm`, retaining the standard `win_amd64` and `win_arm64` wheel tags. Installing a matching wheel does not need Rust, LLVM or C build tools. The extension links the installed CPython stable ABI and Windows system/UCRT APIs; the interpreter and its own runtime dependencies are not bundled. Full original Cargo, Rust compiler, MinGW/libunwind and CPython import-interface source/licence materials are retained in `ktav/_third_party/NOTICE.json` inside the wheel. Preserve that resource when redistributing the package. This describes the prepared candidate, not a repair of historical published wheels. + +### Pure Python + +For a universal wheel, install the existing `py3-none-any` artifact explicitly: + +``` +pip install ./ktav-0.8.1-py3-none-any.whl +``` + +Then select the pure API directly: + +``` +from ktav import pure + + +>>>>> lang=ru +## Установка + +``` +pip install ktav +``` + +По умолчанию используется native backend: соответствующий platform wheel содержит наше встроенное Rust-ядро. Зависимости Python для работы не нужны. Поддерживаемые wheels рассчитаны на Python 3.9+. + +Если подходящего wheel нет, установка из исходников компилирует native-расширение на Rust с помощью maturin; нужны Rust toolchain и заголовочные файлы Python. Установка из исходников не переключается на pure mode. + +Подготовленные Windows wheels 0.8.1 используют actual GNU/LLVM targets `x86_64-pc-windows-gnullvm` и `aarch64-pc-windows-gnullvm`, сохраняя стандартные wheel tags `win_amd64` и `win_arm64`. Для установки подходящего wheel не нужны Rust, LLVM или C build tools. Расширение связывается с установленным stable ABI CPython и системными Windows/UCRT API; интерпретатор и его собственные runtime-зависимости в wheel не включены. Полные original Cargo, Rust compiler, MinGW/libunwind и CPython import-interface source/licence materials сохранены в `ktav/_third_party/NOTICE.json` внутри wheel. Сохраняйте этот ресурс при распространении пакета. Это описание подготовленного candidate, а не исправление исторических опубликованных wheels. + +### Чистый Python + +Для универсального wheel явно установите существующий артефакт `py3-none-any`: + +``` +pip install ./ktav-0.8.1-py3-none-any.whl +``` + +Затем явно выберите pure API: + +``` +from ktav import pure + + +>>>>> lang=zh +## 安装 + +``` +pip install ktav +``` + +默认 backend 是 native;匹配的平台 wheel 内含我们打包的 Rust 核心,无需 Python 运行时依赖。受支持的平台 wheel 面向 Python 3.9+。 + +若没有匹配的 wheel,从源码安装会通过 maturin 使用 Rust 编译 native 扩展,因此需要 Rust 工具链和 Python 开发头文件。源码安装不会切换到 pure 模式。 + +准备中的 0.8.1 Windows wheel 使用实际的 GNU/LLVM 目标 `x86_64-pc-windows-gnullvm` 和 `aarch64-pc-windows-gnullvm`,同时保留标准的 `win_amd64`、`win_arm64` wheel 标签。安装匹配的 wheel 无需 Rust、LLVM 或 C 构建工具。扩展链接已安装的 CPython 稳定 ABI 和 Windows 系统/UCRT API;wheel 不包含解释器及其自身的运行时依赖。完整的 Cargo、Rust 编译器、MinGW/libunwind 和 CPython 导入接口原始源码及许可证材料保留在 wheel 内的 `ktav/_third_party/NOTICE.json` 中。重新分发包时应保留该资源。此说明仅适用于准备中的候选发行包,不表示已修复历史发布的 wheel。 + +### 纯 Python + +如需通用 wheel,请显式安装现有的 `py3-none-any` 工件: + +``` +pip install ./ktav-0.8.1-py3-none-any.whl +``` + +然后显式选择 pure API: + +``` +from ktav import pure + + From c4a9b0350983b60e2d335a294255470d6c6ef458 Mon Sep 17 00:00:00 2001 From: Arch-AI-tech Date: Sun, 1 Nov 2026 21:36:46 +0100 Subject: [PATCH 52/54] fix(ci): preserve literal contracts and canonical doc chunks GitHub expanded an inline Ruby comparison's inputs expression before shell execution, turning the expected literal into an empty string. Construct the literal from two Ruby string fragments instead; retain the exact original-source forwarding assertion and all negative gates. Keep the existing multipart install unit completely unchanged. Place new Windows native-runtime documentation in a dedicated canonical single-part EN/RU/ZH unit after install, generated from the reviewed proposal in CI. Preserve exact changed-file/source/per-language hash validation and include all proposed source files in the artifact. No polydoc validator, boundary mandate or assertion is weakened. The new unit and manifest are still only a CI proposal until generated Markdown and source artifacts are independently verified. Native build, resource/input/consumer and release-role code remain unchanged. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/CI.yml | 2 +- .github/workflows/windows-gnu-pyo3-probe.yml | 25 ++++++- .../python-windows-native-install.md.txt | 72 +------------------ 3 files changed, 26 insertions(+), 73 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index e365a94..475d8f6 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -90,7 +90,7 @@ jobs: abort 'missing native workflow contract' unless native_triggers.fetch('workflow_call').fetch('inputs').fetch('source-ref').fetch('required') targets = native_workflow.fetch('jobs').fetch('native').fetch('strategy').fetch('matrix').fetch('include').map { |item| item.fetch('target') } windows_call = native_workflow.fetch('jobs').fetch('windows-gnu') - abort 'wrong native GNU release caller' unless windows_call.fetch('uses') == './.github/workflows/windows-gnu-pyo3-probe.yml' && windows_call.fetch('with').fetch('source-ref') == '${{ inputs.source-ref }}' + abort 'wrong native GNU release caller' unless windows_call.fetch('uses') == './.github/workflows/windows-gnu-pyo3-probe.yml' && windows_call.fetch('with').fetch('source-ref') == ('$' + '{{ inputs.source-ref }}') gnu = YAML.load_file('.github/workflows/windows-gnu-pyo3-probe.yml') gnu_targets = gnu.fetch('jobs').fetch('compatibility').fetch('strategy').fetch('matrix').fetch('include').map { |item| item.fetch('target') } expected = %w[x86_64-unknown-linux-gnu aarch64-unknown-linux-gnu x86_64-apple-darwin aarch64-apple-darwin x86_64-pc-windows-gnullvm aarch64-pc-windows-gnullvm] diff --git a/.github/workflows/windows-gnu-pyo3-probe.yml b/.github/workflows/windows-gnu-pyo3-probe.yml index d0e4225..c96ca84 100644 --- a/.github/workflows/windows-gnu-pyo3-probe.yml +++ b/.github/workflows/windows-gnu-pyo3-probe.yml @@ -444,14 +444,27 @@ jobs: import shutil import subprocess from pathlib import Path - unit = Path('root-docs/README/install/body-1.md') + unit = Path('root-docs/README/windows-native-runtime/body-1.md') proposed = Path('ci/fixtures/python-windows-native-install.md.txt') + unit.parent.mkdir() shutil.copyfile(proposed, unit) + meta = unit.with_name('meta.js') + shutil.copyfile('root-docs/README/license/meta.js', meta) + manifest = Path('root-docs/README/manifest.js') + raw = manifest.read_bytes() + if hashlib.sha1(b'blob ' + str(len(raw)).encode() + bytes([0]) + raw).hexdigest() != '58bd63c8244acb7a6202b0e1b55378ebd102b140': + raise SystemExit('reviewed README manifest differs') + anchor = ' "install",' + chr(10) + if raw.decode('utf-8').count(anchor) != 1: + raise SystemExit('unique original install unit order required') + manifest.write_text(raw.decode('utf-8').replace(anchor, anchor + ' "windows-native-runtime",' + chr(10)), encoding='utf-8') subprocess.run(['node', 'scripts/build-docs.mjs'], check=True) subprocess.run(['node', 'scripts/build-docs.mjs', '--check'], check=True) changed = set(subprocess.check_output(['git', 'diff', '--name-only'], text=True).splitlines()) names = ['README.md', 'docs/ru/README.ru.md', 'docs/zh/README.zh.md'] - if changed != {str(unit), *names}: + changed.update(subprocess.check_output(['git', 'ls-files', '--others', '--exclude-standard', '--', str(unit.parent)], text=True).splitlines()) + sources = [unit, meta, manifest] + if changed != {*(str(path) for path in sources), *names}: raise SystemExit('documentation proposal changed unrelated files') output = Path(os.environ['RUNNER_TEMP']) / 'python-gnu-doc-proposal' output.mkdir() @@ -462,7 +475,13 @@ jobs: destination.parent.mkdir(parents=True, exist_ok=True) destination.write_bytes(raw) rows.append({'path': name, 'sha256': hashlib.sha256(raw).hexdigest()}) - record = {'tooling_ref': os.environ['GITHUB_SHA'], 'source_unit_sha256': hashlib.sha256(unit.read_bytes()).hexdigest(), 'files': rows} + source_rows = [] + for path in sources: + destination = output / path + destination.parent.mkdir(parents=True, exist_ok=True) + destination.write_bytes(path.read_bytes()) + source_rows.append({'path': str(path), 'sha256': hashlib.sha256(path.read_bytes()).hexdigest()}) + record = {'tooling_ref': os.environ['GITHUB_SHA'], 'source_unit_sha256': hashlib.sha256(unit.read_bytes()).hexdigest(), 'source_files': source_rows, 'files': rows} (output / 'source-provenance.json').write_text(json.dumps(record, indent=2) + '\n', encoding='utf-8') - uses: actions/upload-artifact@v4 with: diff --git a/ci/fixtures/python-windows-native-install.md.txt b/ci/fixtures/python-windows-native-install.md.txt index fe0095f..4cc568b 100644 --- a/ci/fixtures/python-windows-native-install.md.txt +++ b/ci/fixtures/python-windows-native-install.md.txt @@ -1,81 +1,15 @@ >>>>> lang=en -## Install - -``` -pip install ktav -``` - -The default backend is native and includes our bundled Rust core in a matching platform wheel. No Python runtime dependency is required. Wheels target Python 3.9+ on supported platforms. - -If no wheel matches, install from source: the native extension is compiled with Rust through maturin, so a Rust toolchain and Python development headers are needed. Source installation does not switch to pure mode. +## Windows native runtime The prepared 0.8.1 Windows wheels use the actual GNU/LLVM targets `x86_64-pc-windows-gnullvm` and `aarch64-pc-windows-gnullvm`, retaining the standard `win_amd64` and `win_arm64` wheel tags. Installing a matching wheel does not need Rust, LLVM or C build tools. The extension links the installed CPython stable ABI and Windows system/UCRT APIs; the interpreter and its own runtime dependencies are not bundled. Full original Cargo, Rust compiler, MinGW/libunwind and CPython import-interface source/licence materials are retained in `ktav/_third_party/NOTICE.json` inside the wheel. Preserve that resource when redistributing the package. This describes the prepared candidate, not a repair of historical published wheels. -### Pure Python - -For a universal wheel, install the existing `py3-none-any` artifact explicitly: - -``` -pip install ./ktav-0.8.1-py3-none-any.whl -``` - -Then select the pure API directly: - -``` -from ktav import pure - - >>>>> lang=ru -## Установка - -``` -pip install ktav -``` - -По умолчанию используется native backend: соответствующий platform wheel содержит наше встроенное Rust-ядро. Зависимости Python для работы не нужны. Поддерживаемые wheels рассчитаны на Python 3.9+. - -Если подходящего wheel нет, установка из исходников компилирует native-расширение на Rust с помощью maturin; нужны Rust toolchain и заголовочные файлы Python. Установка из исходников не переключается на pure mode. +## Нативное расширение Windows Подготовленные Windows wheels 0.8.1 используют actual GNU/LLVM targets `x86_64-pc-windows-gnullvm` и `aarch64-pc-windows-gnullvm`, сохраняя стандартные wheel tags `win_amd64` и `win_arm64`. Для установки подходящего wheel не нужны Rust, LLVM или C build tools. Расширение связывается с установленным stable ABI CPython и системными Windows/UCRT API; интерпретатор и его собственные runtime-зависимости в wheel не включены. Полные original Cargo, Rust compiler, MinGW/libunwind и CPython import-interface source/licence materials сохранены в `ktav/_third_party/NOTICE.json` внутри wheel. Сохраняйте этот ресурс при распространении пакета. Это описание подготовленного candidate, а не исправление исторических опубликованных wheels. -### Чистый Python - -Для универсального wheel явно установите существующий артефакт `py3-none-any`: - -``` -pip install ./ktav-0.8.1-py3-none-any.whl -``` - -Затем явно выберите pure API: - -``` -from ktav import pure - - >>>>> lang=zh -## 安装 - -``` -pip install ktav -``` - -默认 backend 是 native;匹配的平台 wheel 内含我们打包的 Rust 核心,无需 Python 运行时依赖。受支持的平台 wheel 面向 Python 3.9+。 - -若没有匹配的 wheel,从源码安装会通过 maturin 使用 Rust 编译 native 扩展,因此需要 Rust 工具链和 Python 开发头文件。源码安装不会切换到 pure 模式。 +## Windows 原生运行时 准备中的 0.8.1 Windows wheel 使用实际的 GNU/LLVM 目标 `x86_64-pc-windows-gnullvm` 和 `aarch64-pc-windows-gnullvm`,同时保留标准的 `win_amd64`、`win_arm64` wheel 标签。安装匹配的 wheel 无需 Rust、LLVM 或 C 构建工具。扩展链接已安装的 CPython 稳定 ABI 和 Windows 系统/UCRT API;wheel 不包含解释器及其自身的运行时依赖。完整的 Cargo、Rust 编译器、MinGW/libunwind 和 CPython 导入接口原始源码及许可证材料保留在 wheel 内的 `ktav/_third_party/NOTICE.json` 中。重新分发包时应保留该资源。此说明仅适用于准备中的候选发行包,不表示已修复历史发布的 wheel。 -### 纯 Python - -如需通用 wheel,请显式安装现有的 `py3-none-any` 工件: - -``` -pip install ./ktav-0.8.1-py3-none-any.whl -``` - -然后显式选择 pure API: - -``` -from ktav import pure - - From 2d120237fe7ef801cdd591c45230ec843caae62c Mon Sep 17 00:00:00 2001 From: Arch-AI-tech Date: Sun, 1 Nov 2026 21:50:47 +0100 Subject: [PATCH 53/54] docs: describe verified Windows GNU Python wheel resources Commit the canonical single-part Windows native runtime unit, manifest entry and exact CI-generated EN/RU/ZH README bytes. Preserve all original install multipart source chunks and existing generated content. Document actual GNU targets and unchanged wheel tags, normal CPython installation/runtime boundary, no consumer Rust/LLVM/C build tools and complete package-owned original licence/source resources. Keep the prepared-candidate versus historical-publication distinction explicit. Verify source and generated-file SHA256/tooling parent before committing. Remove only the own temporary fixture and docs-proposal generation job; retain actual native producers, all resource/input/consumer/release-role gates and original publication contracts. No local docs generator, build/test, version bump, release, registry publication, merge or deploy. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/windows-gnu-pyo3-probe.yml | 63 ------------------- README.md | 4 ++ docs/ru/README.ru.md | 4 ++ docs/zh/README.zh.md | 4 ++ root-docs/README/manifest.js | 1 + .../README/windows-native-runtime/body-1.md | 0 .../README/windows-native-runtime/meta.js | 7 +++ 7 files changed, 20 insertions(+), 63 deletions(-) rename ci/fixtures/python-windows-native-install.md.txt => root-docs/README/windows-native-runtime/body-1.md (100%) create mode 100644 root-docs/README/windows-native-runtime/meta.js diff --git a/.github/workflows/windows-gnu-pyo3-probe.yml b/.github/workflows/windows-gnu-pyo3-probe.yml index c96ca84..7d4ce4a 100644 --- a/.github/workflows/windows-gnu-pyo3-probe.yml +++ b/.github/workflows/windows-gnu-pyo3-probe.yml @@ -425,66 +425,3 @@ jobs: ${{ runner.temp }}/python-gnu-probe-evidence/*.xml if-no-files-found: warn - docs-proposal: - name: Generate Windows GNU runtime documentation proposal - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 - with: - node-version: '24' - cache: npm - - run: npm ci - - name: Generate and check the reviewed EN RU ZH runtime unit - shell: python - run: | - import hashlib - import json - import os - import shutil - import subprocess - from pathlib import Path - unit = Path('root-docs/README/windows-native-runtime/body-1.md') - proposed = Path('ci/fixtures/python-windows-native-install.md.txt') - unit.parent.mkdir() - shutil.copyfile(proposed, unit) - meta = unit.with_name('meta.js') - shutil.copyfile('root-docs/README/license/meta.js', meta) - manifest = Path('root-docs/README/manifest.js') - raw = manifest.read_bytes() - if hashlib.sha1(b'blob ' + str(len(raw)).encode() + bytes([0]) + raw).hexdigest() != '58bd63c8244acb7a6202b0e1b55378ebd102b140': - raise SystemExit('reviewed README manifest differs') - anchor = ' "install",' + chr(10) - if raw.decode('utf-8').count(anchor) != 1: - raise SystemExit('unique original install unit order required') - manifest.write_text(raw.decode('utf-8').replace(anchor, anchor + ' "windows-native-runtime",' + chr(10)), encoding='utf-8') - subprocess.run(['node', 'scripts/build-docs.mjs'], check=True) - subprocess.run(['node', 'scripts/build-docs.mjs', '--check'], check=True) - changed = set(subprocess.check_output(['git', 'diff', '--name-only'], text=True).splitlines()) - names = ['README.md', 'docs/ru/README.ru.md', 'docs/zh/README.zh.md'] - changed.update(subprocess.check_output(['git', 'ls-files', '--others', '--exclude-standard', '--', str(unit.parent)], text=True).splitlines()) - sources = [unit, meta, manifest] - if changed != {*(str(path) for path in sources), *names}: - raise SystemExit('documentation proposal changed unrelated files') - output = Path(os.environ['RUNNER_TEMP']) / 'python-gnu-doc-proposal' - output.mkdir() - rows = [] - for name in names: - raw = Path(name).read_bytes() - destination = output / name - destination.parent.mkdir(parents=True, exist_ok=True) - destination.write_bytes(raw) - rows.append({'path': name, 'sha256': hashlib.sha256(raw).hexdigest()}) - source_rows = [] - for path in sources: - destination = output / path - destination.parent.mkdir(parents=True, exist_ok=True) - destination.write_bytes(path.read_bytes()) - source_rows.append({'path': str(path), 'sha256': hashlib.sha256(path.read_bytes()).hexdigest()}) - record = {'tooling_ref': os.environ['GITHUB_SHA'], 'source_unit_sha256': hashlib.sha256(unit.read_bytes()).hexdigest(), 'source_files': source_rows, 'files': rows} - (output / 'source-provenance.json').write_text(json.dumps(record, indent=2) + '\n', encoding='utf-8') - - uses: actions/upload-artifact@v4 - with: - name: python-gnu-doc-proposal - path: ${{ runner.temp }}/python-gnu-doc-proposal/ - if-no-files-found: error diff --git a/README.md b/README.md index 2ffc7f9..ecc9c24 100644 --- a/README.md +++ b/README.md @@ -67,6 +67,10 @@ Pure mode implements the full public operation set — `loads`, `loads_strict`, Since 0.8.1 the pure backend also ships as a real install artifact: the universal pure wheel `ktav-0.8.1-py3-none-any.whl` (same distribution name, same version) is published on PyPI alongside the native platform wheels. To install it, download/select the pure wheel (`py3-none-any`) from the release or PyPI files; `pip` prefers a matching platform wheel automatically when one exists. The pure wheel installs with no Rust toolchain, no compiler and no maturin, and provides exactly the `ktav.pure` backend — the native top-level functions then raise a clear `ImportError` pointing at `from ktav import pure`. +## Windows native runtime + +The prepared 0.8.1 Windows wheels use the actual GNU/LLVM targets `x86_64-pc-windows-gnullvm` and `aarch64-pc-windows-gnullvm`, retaining the standard `win_amd64` and `win_arm64` wheel tags. Installing a matching wheel does not need Rust, LLVM or C build tools. The extension links the installed CPython stable ABI and Windows system/UCRT APIs; the interpreter and its own runtime dependencies are not bundled. Full original Cargo, Rust compiler, MinGW/libunwind and CPython import-interface source/licence materials are retained in `ktav/_third_party/NOTICE.json` inside the wheel. Preserve that resource when redistributing the package. This describes the prepared candidate, not a repair of historical published wheels. + ## Quick start ### Parse — read typed fields straight off the dict diff --git a/docs/ru/README.ru.md b/docs/ru/README.ru.md index 4f2b1b9..029b391 100644 --- a/docs/ru/README.ru.md +++ b/docs/ru/README.ru.md @@ -67,6 +67,10 @@ Pure-режим реализует весь публичный набор опе Начиная с 0.8.1 чистый backend поставляется и как реальный артефакт установки: универсальный pure wheel `ktav-0.8.1-py3-none-any.whl` (то же имя дистрибуции, та же версия) публикуется на PyPI рядом с нативными платформенными wheel-ами. Чтобы его поставить, скачайте или выберите pure wheel (`py3-none-any`) среди файлов релиза или на PyPI; `pip` при наличии подходящего платформенного wheel автоматически предпочитает его. Pure wheel ставится без Rust toolchain, компилятора и maturin и предоставляет ровно backend `ktav.pure` — нативные функции верхнего уровня тогда бросают понятный `ImportError` с указанием на `from ktav import pure`. +## Нативное расширение Windows + +Подготовленные Windows wheels 0.8.1 используют actual GNU/LLVM targets `x86_64-pc-windows-gnullvm` и `aarch64-pc-windows-gnullvm`, сохраняя стандартные wheel tags `win_amd64` и `win_arm64`. Для установки подходящего wheel не нужны Rust, LLVM или C build tools. Расширение связывается с установленным stable ABI CPython и системными Windows/UCRT API; интерпретатор и его собственные runtime-зависимости в wheel не включены. Полные original Cargo, Rust compiler, MinGW/libunwind и CPython import-interface source/licence materials сохранены в `ktav/_third_party/NOTICE.json` внутри wheel. Сохраняйте этот ресурс при распространении пакета. Это описание подготовленного candidate, а не исправление исторических опубликованных wheels. + ## Быстрый старт ### Парсинг — типизированно читаем поля прямо из dict diff --git a/docs/zh/README.zh.md b/docs/zh/README.zh.md index 0ba0522..c810749 100644 --- a/docs/zh/README.zh.md +++ b/docs/zh/README.zh.md @@ -65,6 +65,10 @@ formatted = pure.format(open("config.ktav").read()) 从 0.8.1 起,纯 backend 也以真实安装工件的形式发布:通用纯 wheel `ktav-0.8.1-py3-none-any.whl`(同一发行名、同一版本)与各原生平台 wheel 一起发布在 PyPI 上。安装时请从 release 或 PyPI 文件中下载/选择纯 wheel(`py3-none-any`);当存在匹配的平台 wheel 时,`pip` 会自动优先选择。纯 wheel 的安装不需要 Rust 工具链、编译器或 maturin,并且只提供 `ktav.pure` 这一个 backend —— 此时原生的顶层函数会抛出明确的 `ImportError`,指向 `from ktav import pure`。 +## Windows 原生运行时 + +准备中的 0.8.1 Windows wheel 使用实际的 GNU/LLVM 目标 `x86_64-pc-windows-gnullvm` 和 `aarch64-pc-windows-gnullvm`,同时保留标准的 `win_amd64`、`win_arm64` wheel 标签。安装匹配的 wheel 无需 Rust、LLVM 或 C 构建工具。扩展链接已安装的 CPython 稳定 ABI 和 Windows 系统/UCRT API;wheel 不包含解释器及其自身的运行时依赖。完整的 Cargo、Rust 编译器、MinGW/libunwind 和 CPython 导入接口原始源码及许可证材料保留在 wheel 内的 `ktav/_third_party/NOTICE.json` 中。重新分发包时应保留该资源。此说明仅适用于准备中的候选发行包,不表示已修复历史发布的 wheel。 + ## 快速上手 ### 解析 —— 直接从 dict 按类型读取字段 diff --git a/root-docs/README/manifest.js b/root-docs/README/manifest.js index 58bd63c..902aec5 100644 --- a/root-docs/README/manifest.js +++ b/root-docs/README/manifest.js @@ -1,6 +1,7 @@ export default [ "title", "install", + "windows-native-runtime", "quickstart-parse", "quickstart-walk", "quickstart-build", diff --git a/ci/fixtures/python-windows-native-install.md.txt b/root-docs/README/windows-native-runtime/body-1.md similarity index 100% rename from ci/fixtures/python-windows-native-install.md.txt rename to root-docs/README/windows-native-runtime/body-1.md diff --git a/root-docs/README/windows-native-runtime/meta.js b/root-docs/README/windows-native-runtime/meta.js new file mode 100644 index 0000000..eb86416 --- /dev/null +++ b/root-docs/README/windows-native-runtime/meta.js @@ -0,0 +1,7 @@ +export default { + "kind": "frontmatter", + "number": null, + "level": null, + "title": null, + "bodyParts": 1 +} From 18e66c3f3d2ce2bc87ebab88e18a0eb23902d4fe Mon Sep 17 00:00:00 2001 From: Arch-AI-tech Date: Sun, 1 Nov 2026 22:04:59 +0100 Subject: [PATCH 54/54] fix(ci): avoid unpinned maturin release API discovery The final macOS ARM native job failed before compiling a wheel because maturin-action's implicit pyproject range lookup hit getaddrinfo ENOTFOUND api.github.com. The same-head successful macOS x64 job resolved and used maturin 1.16.0. Pin that already-used tool version in both owning native producers. The exact reviewed action implementation skips manifest/API release lookup for an explicit version and downloads its tagged asset directly. Keep all wheel build arguments, Rust targets, source/dependency versions, resource/input/consumer assertions and release gates unchanged. This is not a tool/project/library version upgrade, DNS bypass or a blind CI rerun. No local build/test or publication occurs. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/native-release-build.yml | 1 + .github/workflows/windows-gnu-pyo3-probe.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/workflows/native-release-build.yml b/.github/workflows/native-release-build.yml index baad42f..15ac157 100644 --- a/.github/workflows/native-release-build.yml +++ b/.github/workflows/native-release-build.yml @@ -144,6 +144,7 @@ jobs: - name: Build the native release wheel uses: PyO3/maturin-action@v1 with: + maturin-version: '1.16.0' target: ${{ matrix.target }} args: ${{ runner.os == 'Linux' && '--release --out dist --locked -i python3.12' || '--release --out dist --locked' }} manylinux: ${{ runner.os == 'Linux' && 'auto' || '' }} diff --git a/.github/workflows/windows-gnu-pyo3-probe.yml b/.github/workflows/windows-gnu-pyo3-probe.yml index 7d4ce4a..3adbe37 100644 --- a/.github/workflows/windows-gnu-pyo3-probe.yml +++ b/.github/workflows/windows-gnu-pyo3-probe.yml @@ -240,6 +240,7 @@ jobs: - name: Build original Python stable-ABI wheel for the genuine GNU target uses: PyO3/maturin-action@v1 with: + maturin-version: '1.16.0' target: ${{ matrix.target }} args: --release --locked --out dist -i python rust-toolchain: '1.99.1'