node/deps/libffi/preprocess_asm.py
cjihrig d0fa608c07 lib,src,test,doc: add node:ffi module
PR-URL: https://github.com/nodejs/node/pull/62072
Co-authored-by: Paolo Insogna <paolo@cowtech.it>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Bryan English <bryan@bryanenglish.com>
2026-04-14 10:44:30 +01:00

134 lines
4.4 KiB
Python

#!/usr/bin/env python3
import argparse
import os
from pathlib import Path
import re
import shlex
import shutil
import subprocess
import sys
def split_command(value):
if not value:
return None
value = value.strip()
if not value:
return None
return shlex.split(value, posix=(os.name != 'nt'))
def find_compiler():
for var in ('CC', 'CXX'):
command = split_command(os.environ.get(var))
if command and shutil.which(command[0]):
return command
for name in ('cl.exe', 'cl', 'clang-cl.exe', 'clang-cl', 'cc', 'clang', 'gcc'):
path = shutil.which(name)
if path:
return [path]
raise RuntimeError('Unable to locate a compiler for preprocessing assembly')
def find_armasm64():
"""Find armasm64.exe in the PATH or common Visual Studio locations."""
# First check PATH
path = shutil.which('armasm64.exe')
if path:
return path
# Check common VS locations via environment
vc_install_dir = os.environ.get('VCINSTALLDIR', '')
if vc_install_dir:
candidate = os.path.join(vc_install_dir, 'bin', 'Hostx64', 'arm64', 'armasm64.exe')
if os.path.exists(candidate):
return candidate
candidate = os.path.join(vc_install_dir, 'bin', 'Hostarm64', 'arm64', 'armasm64.exe')
if os.path.exists(candidate):
return candidate
raise RuntimeError('Unable to locate armasm64.exe')
def normalize_path(value):
return str(value).strip().strip('"')
def unique_paths(paths):
seen = set()
result = []
for path in paths:
if path in seen:
continue
seen.add(path)
result.append(path)
return result
def preprocess(args):
compiler = find_compiler()
input_path = normalize_path(args.input)
output = Path(normalize_path(args.output))
include_dirs = [normalize_path(include_dir) for include_dir in args.include_dir]
include_dirs.append(str(output.parent))
include_dirs = unique_paths(include_dirs)
output.parent.mkdir(parents=True, exist_ok=True)
if os.name == 'nt' and Path(compiler[0]).name.lower() in ('cl.exe', 'cl', 'clang-cl.exe', 'clang-cl'):
command = compiler + ['/nologo', '/EP', '/TC']
command += [f'/I{include_dir}' for include_dir in include_dirs]
command += [f'/D{define}' for define in args.define]
command += [input_path]
else:
command = compiler + ['-E', '-P', '-x', 'c']
command += [f'-I{include_dir}' for include_dir in include_dirs]
command += [f'-D{define}' for define in args.define]
command += [input_path]
result = subprocess.run(command, capture_output=True, text=True)
if result.returncode != 0:
sys.stderr.write(result.stderr)
raise RuntimeError(f'Preprocessing failed: {" ".join(command)}')
# Strip all preprocessor directives that assemblers can't handle
# (e.g., armasm64.exe doesn't accept any # directives)
# Remove lines starting with # (preprocessor output like #line, # 1 "file", etc.)
lines = result.stdout.splitlines(keepends=True)
cleaned_lines = [line for line in lines if not line.lstrip().startswith('#')]
cleaned = ''.join(cleaned_lines)
output.write_text(cleaned, encoding='utf-8')
# If --assemble is specified, also run the assembler to produce an object file
if args.assemble:
assemble_output = Path(normalize_path(args.assemble))
assemble_output.parent.mkdir(parents=True, exist_ok=True)
armasm64 = find_armasm64()
asm_command = [armasm64, '-nologo', '-g', str(output), '-o', str(assemble_output)]
asm_result = subprocess.run(asm_command, capture_output=True, text=True)
if asm_result.returncode != 0:
sys.stderr.write(asm_result.stderr)
sys.stderr.write(asm_result.stdout)
raise RuntimeError(f'Assembly failed: {" ".join(asm_command)}')
def main(argv=None):
parser = argparse.ArgumentParser(description='Preprocess libffi assembly source')
parser.add_argument('--input', required=True)
parser.add_argument('--output', required=True)
parser.add_argument('--include-dir', action='append', default=[])
parser.add_argument('--define', action='append', default=[])
parser.add_argument('--assemble', help='Also assemble the output to this object file')
args = parser.parse_args(argv)
preprocess(args)
return 0
if __name__ == '__main__':
raise SystemExit(main())