mirror of
https://github.com/mandiant/speakeasy.git
synced 2026-10-12 02:18:47 +00:00
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.
72 lines
2.4 KiB
Python
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})
|