1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
|
#!/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:
node_lib_dir = os.path.join(headers_dir, args.config)
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())
return return_code
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:
codes = executor.map(node_gyp_rebuild, test_dirs)
return 0 if all(code == 0 for code in codes) else 1
def get_default_out_dir(args):
default_out_dir = os.path.join('out', args.config)
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
# could be 'out/Release', 'out/Debug' or just 'Release' or 'Debug'.
if os.path.exists(default_out_dir):
return default_out_dir
if os.path.exists(args.config):
return args.config
raise RuntimeError('Cannot find out dir, did you run build?')
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'))
parser.add_argument('--config', help='build config (Release or Debug)',
default='Release')
args, unknown_args = parser.parse_known_args()
if not args.out_dir:
args.out_dir = get_default_out_dir(args)
exit_code = 1
if args.headers_dir:
exit_code = rebuild_addons(args)
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)
exit_code = rebuild_addons(args)
finally:
shutil.rmtree(args.headers_dir)
return exit_code
if __name__ == '__main__':
sys.exit(main())
|