Skip to content

Commit

Permalink
build,tools: make addons tests work with GN
Browse files Browse the repository at this point in the history
  • Loading branch information
zcbenz committed Nov 16, 2023
1 parent dfbca33 commit 0940864
Show file tree
Hide file tree
Showing 10 changed files with 477 additions and 327 deletions.
20 changes: 6 additions & 14 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -187,11 +187,11 @@ config.gypi: configure configure.py src/node_version.h

.PHONY: install
install: all ## Installs node into $PREFIX (default=/usr/local).
$(PYTHON) tools/install.py $@ '$(DESTDIR)' '$(PREFIX)'
$(PYTHON) tools/install.py $@ --dest-dir '$(DESTDIR)' --prefix '$(PREFIX)'

.PHONY: uninstall
uninstall: ## Uninstalls node from $PREFIX (default=/usr/local).
$(PYTHON) tools/install.py $@ '$(DESTDIR)' '$(PREFIX)'
$(PYTHON) tools/install.py $@ --dest-dir '$(DESTDIR)' --prefix '$(PREFIX)'

.PHONY: clean
.NOTPARALLEL: clean
Expand Down Expand Up @@ -388,15 +388,12 @@ ADDONS_BINDING_SOURCES := \
$(filter-out test/addons/??_*/*.h, $(wildcard test/addons/*/*.h))

ADDONS_PREREQS := config.gypi \
deps/npm/node_modules/node-gyp/package.json tools/build-addons.mjs \
deps/npm/node_modules/node-gyp/package.json tools/build_addons.py \
deps/uv/include/*.h deps/v8/include/*.h \
src/node.h src/node_buffer.h src/node_object_wrap.h src/node_version.h

define run_build_addons
env npm_config_loglevel=$(LOGLEVEL) npm_config_nodedir="$$PWD" \
npm_config_python="$(PYTHON)" $(NODE) "$$PWD/tools/build-addons.mjs" \
"$$PWD/deps/npm/node_modules/node-gyp/bin/node-gyp.js" \
$1
env $(PYTHON) "$$PWD/tools/build_addons.py" --loglevel=$(LOGLEVEL) $1
touch $2
endef

Expand Down Expand Up @@ -1216,15 +1213,10 @@ $(TARBALL)-headers: release-only
--tag=$(TAG) \
--release-urlbase=$(RELEASE_URLBASE) \
$(CONFIG_FLAGS) $(BUILD_RELEASE_FLAGS)
HEADERS_ONLY=1 $(PYTHON) tools/install.py install '$(TARNAME)' '/'
find $(TARNAME)/ -type l | xargs $(RM)
tar -cf $(TARNAME)-headers.tar $(TARNAME)
$(RM) -r $(TARNAME)
gzip -c -f -9 $(TARNAME)-headers.tar > $(TARNAME)-headers.tar.gz
$(PYTHON) tools/create_headers_tarball.py --target '$(TARNAME)-headers.tar.gz' --prefix '/'
ifeq ($(XZ), 1)
xz -c -f -$(XZ_COMPRESSION) $(TARNAME)-headers.tar > $(TARNAME)-headers.tar.xz
$(PYTHON) tools/create_headers_tarball.py --target '$(TARNAME)-headers.tar.xz' --compression-level $(XZ_COMPRESSION) --prefix '/'
endif
$(RM) $(TARNAME)-headers.tar

.PHONY: tar-headers
tar-headers: $(TARBALL)-headers ## Build the node header tarball.
Expand Down
5 changes: 5 additions & 0 deletions deps/openssl/unofficial.gni
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,11 @@ template("openssl_gn_build") {
configs += [ ":openssl_internal_config" ]
public_configs = [ ":openssl_external_config" ]

if (is_posix) {
configs -= [ "//build/config/gcc:symbol_visibility_hidden" ]
configs += [ "//build/config/gcc:symbol_visibility_default" ]
}

config_path_name = ""
if (is_win) {
if (target_cpu == "x86") {
Expand Down
5 changes: 5 additions & 0 deletions deps/uv/unofficial.gni
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,11 @@ template("uv_gn_build") {
configs += [ ":uv_internal_config" ]
public_configs = [ ":uv_external_config" ]

if (is_posix) {
configs -= [ "//build/config/gcc:symbol_visibility_hidden" ]
configs += [ "//build/config/gcc:symbol_visibility_default" ]
}

if (is_win) {
libs = [
"advapi32.lib",
Expand Down
1 change: 0 additions & 1 deletion doc/contributing/collaborator-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,6 @@ There are some other files that touch the build chain. Changes in the following
files also qualify as affecting the `node` binary:

* `tools/*.py`
* `tools/build-addons.mjs`
* `*.gyp`
* `*.gypi`
* `configure`
Expand Down
63 changes: 0 additions & 63 deletions tools/build-addons.mjs

This file was deleted.

114 changes: 114 additions & 0 deletions tools/build_addons.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
#!/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__, '..', '..'))

def install_and_rebuild(args, install_args):
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)

# Create a temporary directory for node headers.
with tempfile.TemporaryDirectory() as headers_dir:
# Run install.py to install headers.
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)

# Copy node.lib.
if args.is_win:
node_lib_dir = os.path.join(headers_dir, 'Release')
os.makedirs(node_lib_dir)
shutil.copy2(os.path.join(args.out_dir, 'node.lib'),
os.path.join(node_lib_dir, 'node.lib'))

def rebuild_addon(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())

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:
executor.map(rebuild_addon, test_dirs)

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('--out-dir', help='path to the output directory',
default='out/Release')
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'))
args, unknown_args = parser.parse_known_args()

install_and_rebuild(args, unknown_args)

if __name__ == '__main__':
main()
65 changes: 65 additions & 0 deletions tools/create_headers_tarball.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#!/usr/bin/env python3

import argparse
import lzma
import os
import shutil
import sys
import subprocess
import tarfile
import tempfile

def strip_tarball_suffix(filename):
suffixs = ['-headers.tar.gz', '-headers.tar.xz']
for suffix in suffixs:
if filename.endswith(suffix):
return filename[:-len(suffix)]
raise ValueError('The tarball name must ends with -headers.tar.gz/xz.')

def run_install_py(flags, temp_dir):
install_py = os.path.join(os.path.dirname(__file__), 'install.py')
subprocess.check_call([
sys.executable,
install_py,
'install',
'--silent',
'--headers-only',
'--dest-dir', temp_dir,
'--prefix', '/',
] + flags)

def create_tarball(temp_dir, tarball, compression_level):
if tarball.endswith('.gz'):
tar = tarfile.open(tarball, 'w:gz', compresslevel=int(compression_level))
elif tarball.endswith('.xz'):
if compression_level.endswith('e'):
preset = int(compression_level[:-1]) | lzma.PRESET_EXTREME
else:
preset = int(compression_level)
tar = tarfile.open(tarball, 'w:xz', preset=preset)
else:
raise ValueError('Invalid tarball extension. Only .tar.gz and .tar.xz are supported.')
with tar:
tar.add(temp_dir, arcname=os.path.basename(temp_dir))

def main():
parser = argparse.ArgumentParser(
description='Install headers and create tarball')
parser.add_argument('--target',
required=True,
help='Target path of the generated tarball')
parser.add_argument('--compression-level',
default='9',
help='Compression level for gzip or xz (default: 9)')
args, unknown_args = parser.parse_known_args()

temp_dir = tempfile.mkdtemp()
try:
workspace = os.path.join(temp_dir, strip_tarball_suffix(args.target))
run_install_py(unknown_args, workspace)
create_tarball(workspace, args.target, args.compression_level)
finally:
shutil.rmtree(temp_dir)

if __name__ == '__main__':
main()
Loading

0 comments on commit 0940864

Please sign in to comment.