| """Build an experimental just-in-time compiler for CPython.""" | |
| import argparse | |
| import pathlib | |
| import shlex | |
| import sys | |
| import typing | |
| import _targets | |
| def _write_target_dispatcher( | |
| output: pathlib.Path, | |
| targets: typing.Iterable[_targets._Target[typing.Any, typing.Any]], | |
| comment: str, | |
| header_prefix: str, | |
| ) -> None: | |
| lines = [f"// {comment}\n"] | |
| guard = "#if" | |
| for target in targets: | |
| lines.append(f"{guard} {target.condition}\n") | |
| lines.append(f'#include "{header_prefix}-{target.triple}.h"\n') | |
| guard = "#elif" | |
| lines.append("#else\n") | |
| lines.append('#error "unexpected target"\n') | |
| lines.append("#endif\n") | |
| body = "".join(lines) | |
| # Don't touch the file if it hasn't changed (so we don't trigger a rebuild): | |
| if not output.is_file() or output.read_text() != body: | |
| output.write_text(body) | |
| if __name__ == "__main__": | |
| comment = f"$ {shlex.join([pathlib.Path(sys.executable).name] + sys.argv)}" | |
| parser = argparse.ArgumentParser(description=__doc__) | |
| parser.add_argument( | |
| "target", | |
| nargs="+", | |
| type=_targets.get_target, | |
| help="a PEP 11 target triple to compile for", | |
| ) | |
| parser.add_argument( | |
| "-d", "--debug", action="store_true", help="compile for a debug build of Python" | |
| ) | |
| parser.add_argument( | |
| "-f", "--force", action="store_true", help="force the entire JIT to be rebuilt" | |
| ) | |
| parser.add_argument( | |
| "-o", | |
| "--output-dir", | |
| help="where to output generated files", | |
| required=True, | |
| type=lambda p: pathlib.Path(p).resolve(), | |
| ) | |
| parser.add_argument( | |
| "-p", | |
| "--pyconfig-dir", | |
| help="where to find pyconfig.h", | |
| required=True, | |
| type=lambda p: pathlib.Path(p).resolve(), | |
| ) | |
| parser.add_argument( | |
| "-v", "--verbose", action="store_true", help="echo commands as they are run" | |
| ) | |
| parser.add_argument( | |
| "--cflags", help="additional flags to pass to the compiler", default="" | |
| ) | |
| parser.add_argument("--llvm-version", help="LLVM version to use") | |
| parser.add_argument( | |
| "--llvm-tools-install-dir", help="Installation location of LLVM tools" | |
| ) | |
| args = parser.parse_args() | |
| for target in args.target: | |
| target.debug = args.debug | |
| target.force = args.force | |
| target.verbose = args.verbose | |
| target.cflags = args.cflags | |
| target.pyconfig_dir = args.pyconfig_dir | |
| if args.llvm_version: | |
| target.llvm_version = args.llvm_version | |
| if args.llvm_tools_install_dir: | |
| target.llvm_tools_install_dir = args.llvm_tools_install_dir | |
| # Build this target's stencils, shim object, and target-specific | |
| # unwind metadata before writing the generic dispatcher headers below. | |
| target.build( | |
| comment=comment, | |
| force=args.force, | |
| jit_stencils=args.output_dir / f"jit_stencils-{target.triple}.h", | |
| jit_shim_object=args.output_dir / f"jit_shim-{target.triple}.o", | |
| jit_unwind_info=args.output_dir / f"jit_unwind_info-{target.triple}.h", | |
| ) | |
| # Write the target dispatcher that includes the right stencil header for | |
| # the platform compiling Python/jit.c. | |
| _write_target_dispatcher( | |
| args.output_dir / "jit_stencils.h", | |
| args.target, | |
| comment, | |
| "jit_stencils", | |
| ) | |
| # Write the matching dispatcher for generated JIT unwind constants. | |
| _write_target_dispatcher( | |
| args.output_dir / "jit_unwind_info.h", | |
| args.target, | |
| comment, | |
| "jit_unwind_info", | |
| ) |