| #!/usr/bin/env python3 | |
| # -*- coding: utf-8 -*- | |
| # Copyright 2019 The Chromium OS Authors. All rights reserved. | |
| # Use of this source code is governed by a BSD-style license that can be | |
| # found in the LICENSE file. | |
| """Run node with the right settings.""" | |
| from __future__ import print_function | |
| import glob | |
| import os | |
| import platform | |
| import shutil | |
| import sys | |
| import libdot | |
| # The hash of the node_modules that we maintain. | |
| # Allow a long line for easy automated updating. | |
| # pylint: disable=line-too-long | |
| NODE_MODULES_HASH = '2cd2dd365999ae139b6b0fb62a5a09e2a7fb5ab1c0926cf1968a1dec9b74fea5' | |
| # pylint: enable=line-too-long | |
| # In sync with Chromium's DEPS file because it's easier to use something that | |
| # already exists than maintain our own. Look for 'node_linux64' here: | |
| # https://chromium.googlesource.com/chromium/src/+/master/DEPS | |
| NODE_VER = '12.14.1' | |
| # Run `./node_sync_with_chromium` to update these hashes. | |
| NODE_LINUX_HASH = '4572d3801500bcbebafadf203056d6263c840cda' | |
| NODE_MAC_HASH = '6b9eefd982e255d639234acfa1d772c34a2529cb' | |
| NODE_WIN_HASH = 'a25b445d6e02de0fdbf426d5cfd193702351989e' | |
| # Bucket maintained by Chromium. | |
| # gsutil ls gs://chromium-nodejs/ | |
| NODE_BASE_URI = 'https://storage.googleapis.com/chromium-nodejs' | |
| # Bucket maintained by us. | |
| NODE_MODULES_GS_FRAGMENT = 'chromeos-localmirror/secureshell/distfiles' | |
| NODE_MODULES_GS_URI = 'gs://%s' % (NODE_MODULES_GS_FRAGMENT,) | |
| NODE_MODULES_BASE_URI = ('https://storage.googleapis.com/%s' | |
| % (NODE_MODULES_GS_FRAGMENT,)) | |
| # The node_modules & node/npm paths. | |
| NODE_MODULES_DIR = os.path.join(libdot.LIBAPPS_DIR, 'node_modules') | |
| NODE_BIN_DIR = os.path.join(NODE_MODULES_DIR, '.bin') | |
| NODE = os.path.join(NODE_BIN_DIR, 'node') | |
| NPM = os.path.join(NODE_BIN_DIR, 'npm') | |
| # Use a dotdir as npm expects to manage everything under node_modules/. | |
| NODE_DIR = os.path.join(NODE_MODULES_DIR, '.node') | |
| def run(cmd, **kwargs): | |
| """Run the node |cmd|. | |
| This assumes |cmd| is a node program. Different OS's might need to invoke | |
| node differently, so wrap the calls here. | |
| """ | |
| # We need the node bin dir to be at the start of the $PATH as some packages | |
| # will try to run other npm packages directly. | |
| extra_env = kwargs.setdefault('extra_env', {}) | |
| assert 'PATH' not in extra_env | |
| extra_env['PATH'] = os.pathsep.join([NODE_BIN_DIR, os.getenv('PATH')]) | |
| return libdot.run( | |
| cmd[1:], | |
| cmd_prefix=[NODE, os.path.join(NODE_BIN_DIR, cmd[0])], | |
| log_prefix=[cmd[0]], | |
| **kwargs) | |
| def update(): | |
| """Download & update our copy of node.""" | |
| osname = platform.system() | |
| if osname == 'Linux': | |
| node_hash = NODE_LINUX_HASH | |
| elif osname == 'Darwin': | |
| node_hash = NODE_MAC_HASH | |
| elif osname == 'Windows': | |
| node_hash = NODE_WIN_HASH | |
| else: | |
| raise RuntimeError('Unknown OS %s' % (osname,)) | |
| # In case of an upgrade, nuke existing dir. | |
| hash_file = os.path.join(NODE_DIR, node_hash) | |
| if not os.path.exists(hash_file): | |
| shutil.rmtree(NODE_DIR, ignore_errors=True) | |
| node = NODE | |
| if osname == 'Windows': | |
| node += '.exe' | |
| if not os.path.exists(node): | |
| os.makedirs(NODE_BIN_DIR, exist_ok=True) | |
| os.makedirs(NODE_DIR, exist_ok=True) | |
| # Download & unpack the archive. | |
| uri = '/'.join((NODE_BASE_URI, NODE_VER, node_hash)) | |
| output = os.path.join(NODE_DIR, node_hash) | |
| libdot.fetch(uri, output) | |
| if osname == 'Windows': | |
| libdot.unlink(node) | |
| os.rename(output, node) | |
| else: | |
| libdot.unpack(output, cwd=NODE_DIR) | |
| libdot.unlink(output) | |
| # Create canonical symlinks for node & npm. | |
| paths = glob.glob(os.path.join(NODE_DIR, '*', 'bin', 'node')) | |
| #relpath = os.path.relpath(paths[0], NODE_BIN_DIR) | |
| #os.symlink(relpath, NODE) | |
| libdot.symlink(paths[0], NODE) | |
| paths = glob.glob(os.path.join(NODE_DIR, '*', '*', 'node_modules', | |
| 'npm', 'bin', 'npm-cli.js')) | |
| #relpath = os.path.relpath(paths[0], NODE_BIN_DIR) | |
| #os.symlink(relpath, NPM) | |
| libdot.symlink(paths[0], NPM) | |
| # Mark the hash of this checkout. | |
| libdot.touch(hash_file) | |
| def modules_update(): | |
| """Download & update our copy of node_modules.""" | |
| hash_file = os.path.join(NODE_MODULES_DIR, '.hash') | |
| old_hash = None | |
| try: | |
| with open(hash_file, 'r', encoding='utf-8') as fp: | |
| old_hash = fp.read().strip() | |
| except FileNotFoundError: | |
| pass | |
| # In case of an upgrade, nuke existing dir. | |
| if old_hash != NODE_MODULES_HASH: | |
| shutil.rmtree(NODE_MODULES_DIR, ignore_errors=True) | |
| if not os.path.exists(hash_file): | |
| # Download & unpack the archive. | |
| tar = 'node_modules-%s.tar.xz' % (NODE_MODULES_HASH,) | |
| uri = '/'.join((NODE_MODULES_BASE_URI, tar)) | |
| output = os.path.join(libdot.LIBAPPS_DIR, tar) | |
| libdot.fetch(uri, output) | |
| libdot.unpack(output, cwd=libdot.LIBAPPS_DIR) | |
| libdot.unlink(output) | |
| # Mark the hash of this checkout. | |
| with open(hash_file, 'w', encoding='utf-8') as fp: | |
| fp.write(NODE_MODULES_HASH) | |
| def main(argv): | |
| """The main func!""" | |
| libdot.setup_logging() | |
| libdot.node_and_npm_setup() | |
| os.execv(NODE, ['node'] + argv) | |
| if __name__ == '__main__': | |
| sys.exit(main(sys.argv[1:])) |