Files
Willi Ballenthin ec11c61cc7 Add per-DLL declaration catalogs to sigdb
The signature database answers only permissive lookups: a name can
match through DLL aliases, name prefixes or another DLL's declaration.
That is useful to format handler arguments, but it cannot tell which
functions a module really declares or whether the generic executor can
run a declaration.

SignatureSource now has iter_functions, which lists the declarations of
one DLL for the architecture in name order, with no aliases, name
prefixes or declarations of other DLLs. SignatureDatabase.get_functions
maps these declarations to their signatures with source precedence and
keeps the map until a source is added. Win32MetadataSource keeps a
per-DLL index so that a listing does not scan the whole table. FuncSig.supports_emulation
tells whether the integer-slot executor can run a declaration. An
explicit empty arch list now means no architecture in lookup as well.
The unified API commit uses these methods to build synthetic export
tables and to gate generic dispatch.

The win32metadata archive is now win32json_signatures.json.gz, so that
its name states its source next to phnt_signatures.json.gz. A source
checkout must run `just gen-signatures` or reinstall to create it.
2026-10-11 19:50:30 +01:00

72 lines
2.4 KiB
Python

"""
Build hooks for speakeasy.
All project metadata lives in ``pyproject.toml``; this file only exists to
regenerate the API signature databases from the ``deps/win32json`` and
``deps/phnt`` submodules whenever a wheel or sdist is built, so that the
generated ``speakeasy/resources/win32/*.json.gz`` files ship in every
distribution without being committed to the repository.
"""
import os
import subprocess
import sys
from setuptools import setup
from setuptools.command.build_py import build_py as _build_py
from setuptools.command.sdist import sdist as _sdist
HERE = os.path.dirname(os.path.abspath(__file__))
RESOURCES = os.path.join(HERE, "speakeasy", "resources", "win32")
# (generator script, submodule marker path, output file)
DATABASES = [
(
os.path.join(HERE, "scripts", "gen_win32_signatures.py"),
os.path.join(HERE, "deps", "win32json", "api"),
os.path.join(RESOURCES, "win32json_signatures.json.gz"),
),
(
os.path.join(HERE, "scripts", "gen_phnt_signatures.py"),
os.path.join(HERE, "deps", "phnt", "ntpsapi.h"),
os.path.join(RESOURCES, "phnt_signatures.json.gz"),
),
]
def _generate_signatures(announce=None):
for generator, marker, output in DATABASES:
submodule = os.path.relpath(os.path.dirname(marker) if marker.endswith(".h") else marker, HERE)
submodule = submodule.split(os.sep + "api")[0]
if not os.path.exists(marker):
if os.path.exists(output):
if announce:
announce(f"{submodule} missing; reusing existing {os.path.relpath(output, HERE)}", level=2)
continue
raise SystemExit(
f"{submodule} is not checked out and no signature database exists; "
f"run `git submodule update --init {submodule}` before building"
)
if announce:
announce(f"generating API signature database from {submodule}", level=2)
subprocess.check_call([sys.executable, generator, "--output", output])
class build_py(_build_py):
"""Regenerate the signature database before collecting package data."""
def run(self) -> None:
_generate_signatures(self.announce)
super().run()
class sdist(_sdist):
"""Regenerate the signature database so the generated files are included in the sdist."""
def run(self) -> None:
_generate_signatures(self.announce)
super().run()
setup(cmdclass={"build_py": build_py, "sdist": sdist})