Skip to content

Commit 3be1111

Browse files
committed
build,tools: generate node headers on request
1 parent 738ca51 commit 3be1111

File tree

2 files changed

+106
-72
lines changed

2 files changed

+106
-72
lines changed

Makefile

+29-10
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,28 @@ test/addons/.docbuildstamp: $(DOCBUILDSTAMP_PREREQS) tools/doc/node_modules
379379
[ $$? -eq 0 ] && touch $@; \
380380
fi
381381

382+
# All files that will be included in headers tarball should be listed as deps
383+
# for generating headers. The list is manually synchronized with install.py.
384+
ADDONS_HEADERS_PREREQS := tools/install.py \
385+
config.gypi common.gypi \
386+
$(wildcard deps/openssl/config/*.h) \
387+
$(wildcard deps/openssl/openssl/include/openssl/*.h) \
388+
$(wildcard deps/uv/include/*.h) \
389+
$(wildcard deps/uv/include/*/*.h) \
390+
$(wildcard deps/v8/include/*.h) \
391+
$(wildcard deps/v8/include/*/*.h) \
392+
deps/zlib/zconf.h deps/zlib/zlib.h \
393+
src/node.h src/node_api.h src/js_native_api.h src/js_native_api_types.h \
394+
src/node_api_types.h src/node_buffer.h src/node_object_wrap.h \
395+
src/node_version.h
396+
397+
ADDONS_HEADERS_DIR = out/$(BUILDTYPE)/addons_headers
398+
399+
# Generate node headers which will be used for building addons.
400+
test/addons/.headersbuildstamp: $(ADDONS_HEADERS_PREREQS)
401+
$(PYTHON) tools/install.py install --headers-only --dest-dir '$(ADDONS_HEADERS_DIR)' --prefix '/'
402+
@touch $@
403+
382404
ADDONS_BINDING_GYPS := \
383405
$(filter-out test/addons/??_*/binding.gyp, \
384406
$(wildcard test/addons/*/binding.gyp))
@@ -387,13 +409,11 @@ ADDONS_BINDING_SOURCES := \
387409
$(filter-out test/addons/??_*/*.cc, $(wildcard test/addons/*/*.cc)) \
388410
$(filter-out test/addons/??_*/*.h, $(wildcard test/addons/*/*.h))
389411

390-
ADDONS_PREREQS := config.gypi \
391-
deps/npm/node_modules/node-gyp/package.json tools/build_addons.py \
392-
deps/uv/include/*.h deps/v8/include/*.h \
393-
src/node.h src/node_buffer.h src/node_object_wrap.h src/node_version.h
412+
ADDONS_PREREQS := test/addons/.headersbuildstamp \
413+
deps/npm/node_modules/node-gyp/package.json tools/build_addons.py
394414

395415
define run_build_addons
396-
env $(PYTHON) "$$PWD/tools/build_addons.py" --loglevel=$(LOGLEVEL) $1
416+
env $(PYTHON) "$$PWD/tools/build_addons.py" --loglevel=$(LOGLEVEL) --headers-dir "$(ADDONS_HEADERS_DIR)" $1
397417
touch $2
398418
endef
399419

@@ -426,8 +446,7 @@ JS_NATIVE_API_BINDING_SOURCES := \
426446
# Implicitly depends on $(NODE_EXE), see the build-js-native-api-tests rule for rationale.
427447
test/js-native-api/.buildstamp: $(ADDONS_PREREQS) \
428448
$(JS_NATIVE_API_BINDING_GYPS) $(JS_NATIVE_API_BINDING_SOURCES) \
429-
src/node_api.h src/node_api_types.h src/js_native_api.h \
430-
src/js_native_api_types.h src/js_native_api_v8.h src/js_native_api_v8_internals.h
449+
src/js_native_api_v8.h src/js_native_api_v8_internals.h
431450
@$(call run_build_addons,"$$PWD/test/js-native-api",$@)
432451

433452
.PHONY: build-js-native-api-tests
@@ -451,8 +470,7 @@ NODE_API_BINDING_SOURCES := \
451470
# Implicitly depends on $(NODE_EXE), see the build-node-api-tests rule for rationale.
452471
test/node-api/.buildstamp: $(ADDONS_PREREQS) \
453472
$(NODE_API_BINDING_GYPS) $(NODE_API_BINDING_SOURCES) \
454-
src/node_api.h src/node_api_types.h src/js_native_api.h \
455-
src/js_native_api_types.h src/js_native_api_v8.h src/js_native_api_v8_internals.h
473+
src/js_native_api_v8.h src/js_native_api_v8_internals.h
456474
@$(call run_build_addons,"$$PWD/test/node-api",$@)
457475

458476
.PHONY: build-node-api-tests
@@ -657,9 +675,10 @@ test-addons: test-build test-js-native-api test-node-api
657675
.PHONY: test-addons-clean
658676
.NOTPARALLEL: test-addons-clean
659677
test-addons-clean:
678+
$(RM) -r "$(ADDONS_HEADERS_DIR)"
660679
$(RM) -r test/addons/??_*/
661680
$(RM) -r test/addons/*/build
662-
$(RM) test/addons/.buildstamp test/addons/.docbuildstamp
681+
$(RM) test/addons/.buildstamp test/addons/.docbuildstamp test/addons/.headersbuildstamp
663682
$(MAKE) test-js-native-api-clean
664683
$(MAKE) test-node-api-clean
665684

tools/build_addons.py

+77-62
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,22 @@
1010

1111
ROOT_DIR = os.path.abspath(os.path.join(__file__, '..', '..'))
1212

13-
def install_and_rebuild(args, install_args):
13+
# Run install.py to install headers.
14+
def generate_headers(headers_dir, install_args):
15+
print('Generating headers')
16+
subprocess.check_call([
17+
sys.executable,
18+
os.path.join(ROOT_DIR, 'tools/install.py'),
19+
'install',
20+
'--silent',
21+
'--headers-only',
22+
'--prefix', '/',
23+
'--dest-dir', headers_dir,
24+
] + install_args)
25+
26+
# Rebuild addons in parallel.
27+
def rebuild_addons(args):
28+
headers_dir = os.path.abspath(args.headers_dir)
1429
out_dir = os.path.abspath(args.out_dir)
1530
node_bin = os.path.join(out_dir, 'node')
1631
if args.is_win:
@@ -21,71 +36,57 @@ def install_and_rebuild(args, install_args):
2136
else:
2237
node_gyp = os.path.join(ROOT_DIR, args.node_gyp)
2338

24-
# Create a temporary directory for node headers.
25-
with tempfile.TemporaryDirectory() as headers_dir:
26-
# Run install.py to install headers.
27-
print('Generating headers')
28-
subprocess.check_call([
29-
sys.executable,
30-
os.path.join(ROOT_DIR, 'tools/install.py'),
31-
'install',
32-
'--silent',
33-
'--headers-only',
34-
'--prefix', '/',
35-
'--dest-dir', headers_dir,
36-
] + install_args)
37-
38-
# Copy node.lib.
39-
if args.is_win:
40-
node_lib_dir = os.path.join(headers_dir, 'Release')
41-
os.makedirs(node_lib_dir)
42-
shutil.copy2(os.path.join(args.out_dir, 'node.lib'),
43-
os.path.join(node_lib_dir, 'node.lib'))
39+
# Copy node.lib.
40+
if args.is_win:
41+
node_lib_dir = os.path.join(headers_dir, 'Release')
42+
os.makedirs(node_lib_dir)
43+
shutil.copy2(os.path.join(args.out_dir, 'node.lib'),
44+
os.path.join(node_lib_dir, 'node.lib'))
4445

45-
def rebuild_addon(test_dir):
46-
print('Building addon in', test_dir)
47-
try:
48-
process = subprocess.Popen([
49-
node_bin,
50-
node_gyp,
51-
'rebuild',
52-
'--directory', test_dir,
53-
'--nodedir', headers_dir,
54-
'--python', sys.executable,
55-
'--loglevel', args.loglevel,
56-
], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
46+
def node_gyp_rebuild(test_dir):
47+
print('Building addon in', test_dir)
48+
try:
49+
process = subprocess.Popen([
50+
node_bin,
51+
node_gyp,
52+
'rebuild',
53+
'--directory', test_dir,
54+
'--nodedir', headers_dir,
55+
'--python', sys.executable,
56+
'--loglevel', args.loglevel,
57+
], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
5758

58-
# We buffer the output and print it out once the process is done in order
59-
# to avoid interleaved output from multiple builds running at once.
60-
return_code = process.wait()
61-
stdout, stderr = process.communicate()
62-
if return_code != 0:
63-
print(f'Failed to build addon in {test_dir}:')
64-
if stdout:
65-
print(stdout.decode())
66-
if stderr:
67-
print(stderr.decode())
59+
# We buffer the output and print it out once the process is done in order
60+
# to avoid interleaved output from multiple builds running at once.
61+
return_code = process.wait()
62+
stdout, stderr = process.communicate()
63+
if return_code != 0:
64+
print(f'Failed to build addon in {test_dir}:')
65+
if stdout:
66+
print(stdout.decode())
67+
if stderr:
68+
print(stderr.decode())
6869

69-
except Exception as e:
70-
print(f'Unexpected error when building addon in {test_dir}. Error: {e}')
70+
except Exception as e:
71+
print(f'Unexpected error when building addon in {test_dir}. Error: {e}')
7172

72-
test_dirs = []
73-
skip_tests = args.skip_tests.split(',')
74-
only_tests = args.only_tests.split(',') if args.only_tests else None
75-
for child_dir in os.listdir(args.target):
76-
full_path = os.path.join(args.target, child_dir)
77-
if not os.path.isdir(full_path):
78-
continue
79-
if 'binding.gyp' not in os.listdir(full_path):
80-
continue
81-
if child_dir in skip_tests:
82-
continue
83-
if only_tests and child_dir not in only_tests:
84-
continue
85-
test_dirs.append(full_path)
73+
test_dirs = []
74+
skip_tests = args.skip_tests.split(',')
75+
only_tests = args.only_tests.split(',') if args.only_tests else None
76+
for child_dir in os.listdir(args.target):
77+
full_path = os.path.join(args.target, child_dir)
78+
if not os.path.isdir(full_path):
79+
continue
80+
if 'binding.gyp' not in os.listdir(full_path):
81+
continue
82+
if child_dir in skip_tests:
83+
continue
84+
if only_tests and child_dir not in only_tests:
85+
continue
86+
test_dirs.append(full_path)
8687

87-
with ThreadPoolExecutor() as executor:
88-
executor.map(rebuild_addon, test_dirs)
88+
with ThreadPoolExecutor() as executor:
89+
executor.map(node_gyp_rebuild, test_dirs)
8990

9091
def main():
9192
if sys.platform == 'cygwin':
@@ -94,6 +95,10 @@ def main():
9495
parser = argparse.ArgumentParser(
9596
description='Install headers and rebuild child directories')
9697
parser.add_argument('target', help='target directory to build addons')
98+
parser.add_argument('--headers-dir',
99+
help='path to node headers directory, if not specified '
100+
'new headers will be generated for building',
101+
default=None)
97102
parser.add_argument('--out-dir', help='path to the output directory',
98103
default='out/Release')
99104
parser.add_argument('--loglevel', help='loglevel of node-gyp',
@@ -108,7 +113,17 @@ def main():
108113
action='store_true', default=(sys.platform == 'win32'))
109114
args, unknown_args = parser.parse_known_args()
110115

111-
install_and_rebuild(args, unknown_args)
116+
if args.headers_dir:
117+
rebuild_addons(args)
118+
else:
119+
# When --headers-dir is not specified, generate headers into a temp dir and
120+
# build with the new headers.
121+
try:
122+
args.headers_dir = tempfile.mkdtemp()
123+
generate_headers(args.headers_dir, unknown_args)
124+
rebuild_addons(args)
125+
finally:
126+
shutil.rmtree(args.headers_dir)
112127

113128
if __name__ == '__main__':
114129
main()

0 commit comments

Comments
 (0)