node/deps/libffi/generate-headers.py
Node.js GitHub Bot d9707af728
deps: update libffi to 3.6.0
PR-URL: https://github.com/nodejs/node/pull/64040
Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
Reviewed-By: René <contact.9a5d6388@renegade334.me.uk>
Reviewed-By: Paolo Insogna <paolo@cowtech.it>
2026-06-22 08:55:29 +02:00

278 lines
7.9 KiB
Python

#!/usr/bin/env python3
import argparse
import os
import platform
import sys
from pathlib import Path
LIBFFI_VERSION = '3.6.0'
LIBFFI_VERSION_NUMBER = '30600'
def normalize_arch(target_arch):
aliases = {
'x86': 'ia32',
'x86_64': 'x64',
'arm64': 'arm64',
'aarch64': 'arm64',
}
return aliases.get(target_arch, target_arch)
def get_target(os_name, target_arch):
target_arch = normalize_arch(target_arch)
if target_arch == 'arm':
return ('ARM_WIN32' if os_name == 'win' else 'ARM', 'arm')
if target_arch == 'arm64':
return ('ARM_WIN64' if os_name == 'win' else 'AARCH64', 'aarch64')
if target_arch == 'ia32':
if os_name in ('freebsd', 'openbsd'):
return ('X86_FREEBSD', 'x86')
if os_name in ('ios', 'mac'):
return ('X86_DARWIN', 'x86')
if os_name == 'win':
return ('X86_WIN32', 'x86')
return ('X86', 'x86')
if target_arch == 'x64':
return ('X86_WIN64' if os_name == 'win' else 'X86_64', 'x86')
if target_arch == 'riscv64':
return ('RISCV', 'riscv')
if target_arch == 'loong64':
return ('LOONGARCH64', 'loongarch')
if target_arch in ('mips', 'mipsel', 'mips64el'):
if os_name in ('freebsd', 'linux', 'openbsd'):
return ('MIPS', 'mips')
if target_arch == 'ppc64':
if os_name == 'aix':
return ('POWERPC_AIX', 'powerpc')
if os_name == 'mac':
return ('POWERPC_DARWIN', 'powerpc')
if os_name in ('freebsd', 'openbsd'):
return ('POWERPC_FREEBSD', 'powerpc')
if os_name == 'linux':
return ('POWERPC', 'powerpc')
raise ValueError(f'Unsupported libffi target {os_name}/{target_arch}.')
def has_long_double(os_name, target_arch):
target_arch = normalize_arch(target_arch)
if os_name == 'win':
return '0'
if os_name == 'mac' and target_arch == 'arm64':
return '0'
if target_arch == 'arm':
return '0'
return '1'
def uses_exec_trampoline_table(os_name, target_arch):
target_arch = normalize_arch(target_arch)
return os_name == 'mac' and target_arch in ('arm', 'arm64')
def render_ffi_header(base_dir, os_name, target_arch, target):
template = (base_dir / 'include' / 'ffi.h.in').read_text(encoding='utf-8')
replacements = {
'@FFI_EXEC_TRAMPOLINE_TABLE@':
'1' if uses_exec_trampoline_table(os_name, target_arch) else '0',
'@FFI_VERSION_NUMBER@': LIBFFI_VERSION_NUMBER,
'@FFI_VERSION_STRING@': LIBFFI_VERSION,
'@HAVE_LONG_DOUBLE@': has_long_double(os_name, target_arch),
'@TARGET@': target,
'@VERSION@': LIBFFI_VERSION,
}
for source, replacement in replacements.items():
template = template.replace(source, replacement)
return template
def render_fficonfig(os_name, target_arch):
target_arch = normalize_arch(target_arch)
lines = [
'/* Auto-generated by generate-headers.py. */',
'#ifndef LIBFFI_FFICONFIG_H_',
'#define LIBFFI_FFICONFIG_H_',
'',
'#define HAVE_ALLOCA 1',
'#define HAVE_MEMCPY 1',
'#define HAVE_MEMORY_H 1',
'#define HAVE_STDINT_H 1',
'#define HAVE_STRING_H 1',
'#define STDC_HEADERS 1',
]
if os_name != 'win':
lines.extend([
'#define HAVE_AS_CFI_PSEUDO_OP 1',
'#define HAVE_HIDDEN_VISIBILITY_ATTRIBUTE 1',
'#define HAVE_MKOSTEMP 1',
'#define HAVE_RO_EH_FRAME 1',
'#define EH_FRAME_FLAGS "a"',
])
if target_arch in ('ia32', 'x64') and os_name != 'win':
lines.append('#define HAVE_AS_X86_PCREL 1')
if uses_exec_trampoline_table(os_name, target_arch):
lines.append('#define FFI_EXEC_TRAMPOLINE_TABLE 1')
elif os_name in ('freebsd', 'mac', 'openbsd'):
lines.append('#define FFI_MMAP_EXEC_WRIT 1')
if os_name != 'win':
lines.extend([
'#define HAVE_DLFCN_H 1',
'#define HAVE_MMAP 1',
'#define HAVE_MMAP_ANON 1',
'#define HAVE_MMAP_FILE 1',
'#define HAVE_SYS_MMAN_H 1',
'#define HAVE_UNISTD_H 1',
])
if os_name in ('freebsd', 'ios', 'mac', 'openbsd'):
lines.extend([
'#define HAVE_MMAP_DEV_ZERO 1',
])
if os_name == 'mac' and target_arch == 'arm64':
lines.extend([
'#if defined(__arm64e__)',
'#define HAVE_ARM64E_PTRAUTH 1',
'#endif',
])
lines.extend([
'',
'#ifdef HAVE_HIDDEN_VISIBILITY_ATTRIBUTE',
'#ifdef LIBFFI_ASM',
'#ifdef __APPLE__',
'#define FFI_HIDDEN(name) .private_extern name',
'#else',
'#define FFI_HIDDEN(name) .hidden name',
'#endif',
'#else',
'#define FFI_HIDDEN __attribute__ ((visibility ("hidden")))',
'#endif',
'#else',
'#ifdef LIBFFI_ASM',
'#define FFI_HIDDEN(name)',
'#else',
'#define FFI_HIDDEN',
'#endif',
'#endif',
'',
'#endif /* LIBFFI_FFICONFIG_H_ */',
'',
])
return '\n'.join(lines)
def generate_headers(output_dir, target_arch, os_name):
base_dir = Path(__file__).resolve().parent
output_dir = Path(str(output_dir).strip().strip('"'))
output_dir.mkdir(parents=True, exist_ok=True)
target, target_dir = get_target(os_name, target_arch)
ffitarget_src = base_dir / 'src' / target_dir / 'ffitarget.h'
if not ffitarget_src.exists():
raise FileNotFoundError(f'Missing libffi target header: {ffitarget_src}')
(output_dir / 'ffi.h').write_text(
render_ffi_header(base_dir, os_name, target_arch, target),
encoding='utf-8')
(output_dir / 'fficonfig.h').write_text(
render_fficonfig(os_name, target_arch),
encoding='utf-8')
(output_dir / 'ffitarget.h').write_text(
ffitarget_src.read_text(encoding='utf-8'),
encoding='utf-8')
def detect_os_name():
if sys.platform.startswith('win'):
return 'win'
if sys.platform == 'darwin':
return 'mac'
if sys.platform.startswith('linux'):
return 'linux'
if sys.platform.startswith('freebsd'):
return 'freebsd'
raise ValueError(f'Unsupported host platform {sys.platform!r}')
def detect_target_arch():
candidates = [
os.environ.get('TARGET_ARCH'),
os.environ.get('npm_config_arch'),
os.environ.get('VSCMD_ARG_TGT_ARCH'),
os.environ.get('Platform'),
os.environ.get('PROCESSOR_ARCHITECTURE'),
platform.machine(),
]
aliases = {
'amd64': 'x64',
'x86_64': 'x64',
'x64': 'x64',
'win32': 'x64',
'i386': 'ia32',
'i686': 'ia32',
'ia32': 'ia32',
'x86': 'ia32',
'arm64': 'arm64',
'aarch64': 'arm64',
'arm': 'arm',
'riscv64': 'riscv64',
'loong64': 'loong64',
'ppc64': 'ppc64',
'mips': 'mips',
'mipsel': 'mipsel',
'mips64el': 'mips64el',
}
for candidate in candidates:
if not candidate:
continue
normalized = aliases.get(candidate.lower())
if normalized is not None:
return normalized
raise ValueError('Unable to determine target architecture for libffi headers')
def main(argv=None):
parser = argparse.ArgumentParser(description='Generate libffi headers')
parser.add_argument('--output-dir', required=True)
parser.add_argument('--target-arch')
parser.add_argument('--os')
args = parser.parse_args(argv)
try:
generate_headers(args.output_dir,
args.target_arch or detect_target_arch(),
args.os or detect_os_name())
except Exception as exc: # pylint: disable=broad-except
print(exc, file=sys.stderr)
return 1
return 0
if __name__ == '__main__':
sys.exit(main())