2023-11-16 07:30:16 +00:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
|
|
|
|
import argparse
|
|
|
|
import os
|
|
|
|
import shutil
|
|
|
|
import subprocess
|
|
|
|
import sys
|
|
|
|
import tempfile
|
|
|
|
from concurrent.futures import ThreadPoolExecutor
|
|
|
|
|
|
|
|
ROOT_DIR = os.path.abspath(os.path.join(__file__, '..', '..'))
|
|
|
|
|
|
|
|
# Run install.py to install headers.
|
|
|
|
def generate_headers(headers_dir, install_args):
|
|
|
|
print('Generating headers')
|
|
|
|
subprocess.check_call([
|
|
|
|
sys.executable,
|
|
|
|
os.path.join(ROOT_DIR, 'tools/install.py'),
|
|
|
|
'install',
|
|
|
|
'--silent',
|
|
|
|
'--headers-only',
|
|
|
|
'--prefix', '/',
|
|
|
|
'--dest-dir', headers_dir,
|
|
|
|
] + install_args)
|
|
|
|
|
|
|
|
# Rebuild addons in parallel.
|
|
|
|
def rebuild_addons(args):
|
|
|
|
headers_dir = os.path.abspath(args.headers_dir)
|
|
|
|
out_dir = os.path.abspath(args.out_dir)
|
|
|
|
node_bin = os.path.join(out_dir, 'node')
|
|
|
|
if args.is_win:
|
|
|
|
node_bin += '.exe'
|
|
|
|
|
|
|
|
if os.path.isabs(args.node_gyp):
|
|
|
|
node_gyp = args.node_gyp
|
|
|
|
else:
|
|
|
|
node_gyp = os.path.join(ROOT_DIR, args.node_gyp)
|
|
|
|
|
|
|
|
# Copy node.lib.
|
|
|
|
if args.is_win:
|
2024-04-26 11:36:13 +00:00
|
|
|
node_lib_dir = os.path.join(headers_dir, args.config)
|
2023-11-16 07:30:16 +00:00
|
|
|
os.makedirs(node_lib_dir)
|
|
|
|
shutil.copy2(os.path.join(args.out_dir, 'node.lib'),
|
|
|
|
os.path.join(node_lib_dir, 'node.lib'))
|
|
|
|
|
|
|
|
def node_gyp_rebuild(test_dir):
|
|
|
|
print('Building addon in', test_dir)
|
|
|
|
try:
|
|
|
|
process = subprocess.Popen([
|
|
|
|
node_bin,
|
|
|
|
node_gyp,
|
|
|
|
'rebuild',
|
|
|
|
'--directory', test_dir,
|
|
|
|
'--nodedir', headers_dir,
|
|
|
|
'--python', sys.executable,
|
|
|
|
'--loglevel', args.loglevel,
|
|
|
|
], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
|
|
|
|
|
|
# We buffer the output and print it out once the process is done in order
|
|
|
|
# to avoid interleaved output from multiple builds running at once.
|
|
|
|
return_code = process.wait()
|
|
|
|
stdout, stderr = process.communicate()
|
|
|
|
if return_code != 0:
|
|
|
|
print(f'Failed to build addon in {test_dir}:')
|
|
|
|
if stdout:
|
|
|
|
print(stdout.decode())
|
|
|
|
if stderr:
|
|
|
|
print(stderr.decode())
|
2024-07-29 11:19:52 +00:00
|
|
|
return return_code
|
2023-11-16 07:30:16 +00:00
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
print(f'Unexpected error when building addon in {test_dir}. Error: {e}')
|
|
|
|
|
|
|
|
test_dirs = []
|
|
|
|
skip_tests = args.skip_tests.split(',')
|
|
|
|
only_tests = args.only_tests.split(',') if args.only_tests else None
|
|
|
|
for child_dir in os.listdir(args.target):
|
|
|
|
full_path = os.path.join(args.target, child_dir)
|
|
|
|
if not os.path.isdir(full_path):
|
|
|
|
continue
|
|
|
|
if 'binding.gyp' not in os.listdir(full_path):
|
|
|
|
continue
|
|
|
|
if child_dir in skip_tests:
|
|
|
|
continue
|
|
|
|
if only_tests and child_dir not in only_tests:
|
|
|
|
continue
|
|
|
|
test_dirs.append(full_path)
|
|
|
|
|
|
|
|
with ThreadPoolExecutor() as executor:
|
2024-07-29 11:19:52 +00:00
|
|
|
codes = executor.map(node_gyp_rebuild, test_dirs)
|
|
|
|
return 0 if all(code == 0 for code in codes) else 1
|
2023-11-16 07:30:16 +00:00
|
|
|
|
|
|
|
def get_default_out_dir(args):
|
2024-04-26 11:36:13 +00:00
|
|
|
default_out_dir = os.path.join('out', args.config)
|
2023-11-16 07:30:16 +00:00
|
|
|
if not args.is_win:
|
|
|
|
# POSIX platforms only have one out dir.
|
|
|
|
return default_out_dir
|
|
|
|
# On Windows depending on the args of GYP and configure script, the out dir
|
2024-04-26 11:36:13 +00:00
|
|
|
# could be 'out/Release', 'out/Debug' or just 'Release' or 'Debug'.
|
2023-11-16 07:30:16 +00:00
|
|
|
if os.path.exists(default_out_dir):
|
|
|
|
return default_out_dir
|
2024-04-26 11:36:13 +00:00
|
|
|
if os.path.exists(args.config):
|
|
|
|
return args.config
|
|
|
|
raise RuntimeError('Cannot find out dir, did you run build?')
|
2023-11-16 07:30:16 +00:00
|
|
|
|
|
|
|
def main():
|
|
|
|
if sys.platform == 'cygwin':
|
|
|
|
raise RuntimeError('This script does not support running with cygwin python.')
|
|
|
|
|
|
|
|
parser = argparse.ArgumentParser(
|
|
|
|
description='Install headers and rebuild child directories')
|
|
|
|
parser.add_argument('target', help='target directory to build addons')
|
|
|
|
parser.add_argument('--headers-dir',
|
|
|
|
help='path to node headers directory, if not specified '
|
|
|
|
'new headers will be generated for building',
|
|
|
|
default=None)
|
|
|
|
parser.add_argument('--out-dir', help='path to the output directory',
|
|
|
|
default=None)
|
|
|
|
parser.add_argument('--loglevel', help='loglevel of node-gyp',
|
|
|
|
default='silent')
|
|
|
|
parser.add_argument('--skip-tests', help='skip building tests',
|
|
|
|
default='')
|
|
|
|
parser.add_argument('--only-tests', help='only build tests in the list',
|
|
|
|
default='')
|
|
|
|
parser.add_argument('--node-gyp', help='path to node-gyp script',
|
|
|
|
default='deps/npm/node_modules/node-gyp/bin/node-gyp.js')
|
|
|
|
parser.add_argument('--is-win', help='build for Windows target',
|
|
|
|
action='store_true', default=(sys.platform == 'win32'))
|
2024-04-26 11:36:13 +00:00
|
|
|
parser.add_argument('--config', help='build config (Release or Debug)',
|
|
|
|
default='Release')
|
2023-11-16 07:30:16 +00:00
|
|
|
args, unknown_args = parser.parse_known_args()
|
|
|
|
|
|
|
|
if not args.out_dir:
|
|
|
|
args.out_dir = get_default_out_dir(args)
|
|
|
|
|
2024-07-29 11:19:52 +00:00
|
|
|
exit_code = 1
|
2023-11-16 07:30:16 +00:00
|
|
|
if args.headers_dir:
|
2024-07-29 11:19:52 +00:00
|
|
|
exit_code = rebuild_addons(args)
|
2023-11-16 07:30:16 +00:00
|
|
|
else:
|
|
|
|
# When --headers-dir is not specified, generate headers into a temp dir and
|
|
|
|
# build with the new headers.
|
|
|
|
try:
|
|
|
|
args.headers_dir = tempfile.mkdtemp()
|
|
|
|
generate_headers(args.headers_dir, unknown_args)
|
2024-07-29 11:19:52 +00:00
|
|
|
exit_code = rebuild_addons(args)
|
2023-11-16 07:30:16 +00:00
|
|
|
finally:
|
|
|
|
shutil.rmtree(args.headers_dir)
|
2024-07-29 11:19:52 +00:00
|
|
|
return exit_code
|
2023-11-16 07:30:16 +00:00
|
|
|
|
|
|
|
if __name__ == '__main__':
|
2024-07-29 11:19:52 +00:00
|
|
|
sys.exit(main())
|