mirror of
https://github.com/nodejs/node.git
synced 2024-12-01 16:10:02 +01:00
b7bcf3e121
Refs: 5495678083
PR-URL: https://github.com/nodejs/node/pull/54077
Reviewed-By: Jiawen Geng <technicalcute@gmail.com>
Reviewed-By: Richard Lau <rlau@redhat.com>
Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
Reviewed-By: Marco Ippolito <marcoippolito54@gmail.com>
421 lines
15 KiB
Python
Executable File
421 lines
15 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
|
|
import argparse
|
|
import ast
|
|
import errno
|
|
import os
|
|
import shutil
|
|
import sys
|
|
import re
|
|
|
|
def abspath(*args):
|
|
path = os.path.join(*args)
|
|
return os.path.abspath(path)
|
|
|
|
def is_child_dir(child, parent):
|
|
p = os.path.abspath(parent)
|
|
c = os.path.abspath(child)
|
|
return c.startswith(p) and c != p
|
|
|
|
def try_unlink(path):
|
|
try:
|
|
os.unlink(path)
|
|
except OSError as e:
|
|
if e.errno != errno.ENOENT: raise
|
|
|
|
def try_symlink(options, source_path, link_path):
|
|
if not options.silent:
|
|
print('symlinking %s -> %s' % (source_path, link_path))
|
|
try_unlink(link_path)
|
|
try_mkdir_r(os.path.dirname(link_path))
|
|
os.symlink(source_path, link_path)
|
|
|
|
def try_mkdir_r(path):
|
|
try:
|
|
os.makedirs(path)
|
|
except OSError as e:
|
|
if e.errno != errno.EEXIST: raise
|
|
|
|
def try_rmdir_r(options, path):
|
|
path = abspath(path)
|
|
while is_child_dir(path, options.install_path):
|
|
try:
|
|
os.rmdir(path)
|
|
except OSError as e:
|
|
if e.errno == errno.ENOTEMPTY: return
|
|
if e.errno == errno.ENOENT: return
|
|
raise
|
|
path = abspath(path, '..')
|
|
|
|
def mkpaths(options, path, dest):
|
|
if dest.endswith('/') or dest.endswith('\\'):
|
|
target_path = abspath(options.install_path, dest, os.path.basename(path))
|
|
else:
|
|
target_path = abspath(options.install_path, dest)
|
|
if os.path.isabs(path):
|
|
source_path = path
|
|
else:
|
|
source_path = abspath(options.root_dir, path)
|
|
return source_path, target_path
|
|
|
|
def try_copy(options, path, dest):
|
|
source_path, target_path = mkpaths(options, path, dest)
|
|
if not options.silent:
|
|
print('installing %s' % target_path)
|
|
try_mkdir_r(os.path.dirname(target_path))
|
|
try_unlink(target_path) # prevent ETXTBSY errors
|
|
return shutil.copy2(source_path, target_path)
|
|
|
|
def try_remove(options, path, dest):
|
|
source_path, target_path = mkpaths(options, path, dest)
|
|
if not options.silent:
|
|
print('removing %s' % target_path)
|
|
try_unlink(target_path)
|
|
try_rmdir_r(options, os.path.dirname(target_path))
|
|
|
|
def install(options, paths, dest):
|
|
for path in paths:
|
|
try_copy(options, path, dest)
|
|
|
|
def uninstall(options, paths, dest):
|
|
for path in paths:
|
|
try_remove(options, path, dest)
|
|
|
|
def package_files(options, action, name, bins):
|
|
target_path = os.path.join('lib/node_modules', name)
|
|
|
|
# don't install npm if the target path is a symlink, it probably means
|
|
# that a dev version of npm is installed there
|
|
if os.path.islink(abspath(options.install_path, target_path)): return
|
|
|
|
# npm has a *lot* of files and it'd be a pain to maintain a fixed list here
|
|
# so we walk its source directory instead...
|
|
root = os.path.join('deps', name)
|
|
for dirname, subdirs, basenames in os.walk(root, topdown=True):
|
|
subdirs[:] = [subdir for subdir in subdirs if subdir != 'test']
|
|
paths = [os.path.join(dirname, basename) for basename in basenames]
|
|
action(options, paths,
|
|
os.path.join(target_path, dirname[len(root) + 1:]) + os.path.sep)
|
|
|
|
# create/remove symlinks
|
|
for bin_name, bin_target in bins.items():
|
|
link_path = abspath(options.install_path, os.path.join('bin', bin_name))
|
|
if action == uninstall:
|
|
action(options, [link_path], os.path.join('bin', bin_name))
|
|
elif action == install:
|
|
try_symlink(options, os.path.join('../lib/node_modules', name, bin_target), link_path)
|
|
else:
|
|
assert 0 # unhandled action type
|
|
|
|
def npm_files(options, action):
|
|
package_files(options, action, 'npm', {
|
|
'npm': 'bin/npm-cli.js',
|
|
'npx': 'bin/npx-cli.js',
|
|
})
|
|
|
|
def corepack_files(options, action):
|
|
package_files(options, action, 'corepack', {
|
|
'corepack': 'dist/corepack.js',
|
|
# Not the default just yet:
|
|
# 'yarn': 'dist/yarn.js',
|
|
# 'yarnpkg': 'dist/yarn.js',
|
|
# 'pnpm': 'dist/pnpm.js',
|
|
# 'pnpx': 'dist/pnpx.js',
|
|
})
|
|
|
|
# On z/OS, we install node-gyp for convenience, as some vendors don't have
|
|
# external access and may want to build native addons.
|
|
if sys.platform == 'zos':
|
|
link_path = abspath(options.install_path, 'bin/node-gyp')
|
|
if action == uninstall:
|
|
action(options, [link_path], 'bin/node-gyp')
|
|
elif action == install:
|
|
try_symlink(options, '../lib/node_modules/npm/node_modules/node-gyp/bin/node-gyp.js', link_path)
|
|
else:
|
|
assert 0 # unhandled action type
|
|
|
|
def subdir_files(options, path, dest, action):
|
|
source_path, _ = mkpaths(options, path, dest)
|
|
ret = {}
|
|
for dirpath, dirnames, filenames in os.walk(source_path):
|
|
files_in_path = [os.path.join(os.path.relpath(dirpath, options.root_dir), f) for f in filenames if f.endswith('.h')]
|
|
ret[os.path.join(dest, os.path.relpath(dirpath, source_path))] = files_in_path
|
|
for subdir, files_in_path in ret.items():
|
|
action(options, files_in_path, subdir + os.path.sep)
|
|
|
|
def files(options, action):
|
|
node_bin = 'node'
|
|
if options.is_win:
|
|
node_bin += '.exe'
|
|
action(options, [os.path.join(options.build_dir, node_bin)], os.path.join('bin', node_bin))
|
|
|
|
if 'true' == options.variables.get('node_shared'):
|
|
if options.is_win:
|
|
action(options, [os.path.join(options.build_dir, 'libnode.dll')], 'bin/libnode.dll')
|
|
action(options, [os.path.join(options.build_dir, 'libnode.lib')], 'lib/libnode.lib')
|
|
elif sys.platform == 'zos':
|
|
# GYP will output to lib.target; see _InstallableTargetInstallPath
|
|
# function in tools/gyp/pylib/gyp/generator/make.py
|
|
output_prefix = os.path.join(options.build_dir, 'lib.target')
|
|
|
|
output_lib = 'libnode.' + options.variables.get('shlib_suffix')
|
|
action(options, [os.path.join(output_prefix, output_lib)], os.path.join('lib', output_lib))
|
|
|
|
# create libnode.x that references libnode.so (C++ addons compat)
|
|
os.system(os.path.dirname(os.path.realpath(__file__)) +
|
|
'/zos/modifysidedeck.sh ' +
|
|
abspath(options.install_path, 'lib', output_lib) + ' ' +
|
|
abspath(options.install_path, 'lib/libnode.x') + ' libnode.so')
|
|
|
|
# install libnode.version.so
|
|
so_name = 'libnode.' + re.sub(r'\.x$', '.so', options.variables.get('shlib_suffix'))
|
|
action(options, [os.path.join(output_prefix, so_name)], options.variables.get('libdir') + '/' + so_name)
|
|
|
|
# create symlink of libnode.so -> libnode.version.so (C++ addons compat)
|
|
link_path = abspath(options.install_path, 'lib/libnode.so')
|
|
try_symlink(options, so_name, link_path)
|
|
else:
|
|
output_lib = 'libnode.' + options.variables.get('shlib_suffix')
|
|
action(options, [os.path.join(options.build_dir, output_lib)],
|
|
os.path.join(options.variables.get('libdir'), output_lib))
|
|
|
|
action(options, [os.path.join(options.v8_dir, 'tools/gdbinit')], 'share/doc/node/')
|
|
action(options, [os.path.join(options.v8_dir, 'tools/lldb_commands.py')], 'share/doc/node/')
|
|
|
|
if 'openbsd' in sys.platform:
|
|
action(options, ['doc/node.1'], 'man/man1/')
|
|
else:
|
|
action(options, ['doc/node.1'], 'share/man/man1/')
|
|
|
|
if 'true' == options.variables.get('node_install_npm'):
|
|
npm_files(options, action)
|
|
|
|
if 'true' == options.variables.get('node_install_corepack'):
|
|
corepack_files(options, action)
|
|
|
|
headers(options, action)
|
|
|
|
def headers(options, action):
|
|
def wanted_v8_headers(options, files_arg, dest):
|
|
v8_headers = [
|
|
# The internal cppgc headers are depended on by the public
|
|
# ones, so they need to be included as well.
|
|
'include/cppgc/internal/api-constants.h',
|
|
'include/cppgc/internal/atomic-entry-flag.h',
|
|
'include/cppgc/internal/base-page-handle.h',
|
|
'include/cppgc/internal/caged-heap-local-data.h',
|
|
'include/cppgc/internal/caged-heap.h',
|
|
'include/cppgc/internal/compiler-specific.h',
|
|
'include/cppgc/internal/finalizer-trait.h',
|
|
'include/cppgc/internal/gc-info.h',
|
|
'include/cppgc/internal/logging.h',
|
|
'include/cppgc/internal/member-storage.h',
|
|
'include/cppgc/internal/name-trait.h',
|
|
'include/cppgc/internal/persistent-node.h',
|
|
'include/cppgc/internal/pointer-policies.h',
|
|
'include/cppgc/internal/write-barrier.h',
|
|
# cppgc headers
|
|
'include/cppgc/allocation.h',
|
|
'include/cppgc/common.h',
|
|
'include/cppgc/cross-thread-persistent.h',
|
|
'include/cppgc/custom-space.h',
|
|
'include/cppgc/default-platform.h',
|
|
'include/cppgc/ephemeron-pair.h',
|
|
'include/cppgc/explicit-management.h',
|
|
'include/cppgc/garbage-collected.h',
|
|
'include/cppgc/heap-consistency.h',
|
|
'include/cppgc/heap-handle.h',
|
|
'include/cppgc/heap-state.h',
|
|
'include/cppgc/heap-statistics.h',
|
|
'include/cppgc/heap.h',
|
|
'include/cppgc/liveness-broker.h',
|
|
'include/cppgc/macros.h',
|
|
'include/cppgc/member.h',
|
|
'include/cppgc/name-provider.h',
|
|
'include/cppgc/object-size-trait.h',
|
|
'include/cppgc/persistent.h',
|
|
'include/cppgc/platform.h',
|
|
'include/cppgc/prefinalizer.h',
|
|
'include/cppgc/process-heap-statistics.h',
|
|
'include/cppgc/sentinel-pointer.h',
|
|
'include/cppgc/source-location.h',
|
|
'include/cppgc/testing.h',
|
|
'include/cppgc/trace-trait.h',
|
|
'include/cppgc/type-traits.h',
|
|
'include/cppgc/visitor.h',
|
|
# libplatform headers
|
|
'include/libplatform/libplatform-export.h',
|
|
'include/libplatform/libplatform.h',
|
|
'include/libplatform/v8-tracing.h',
|
|
# v8 headers
|
|
'include/v8-array-buffer.h',
|
|
'include/v8-callbacks.h',
|
|
'include/v8-container.h',
|
|
'include/v8-context.h',
|
|
'include/v8-cppgc.h',
|
|
'include/v8-data.h',
|
|
'include/v8-date.h',
|
|
'include/v8-debug.h',
|
|
'include/v8-embedder-heap.h',
|
|
'include/v8-embedder-state-scope.h',
|
|
'include/v8-exception.h',
|
|
'include/v8-extension.h',
|
|
'include/v8-external.h',
|
|
'include/v8-forward.h',
|
|
'include/v8-function-callback.h',
|
|
'include/v8-function.h',
|
|
'include/v8-handle-base.h',
|
|
'include/v8-initialization.h',
|
|
'include/v8-internal.h',
|
|
'include/v8-isolate.h',
|
|
'include/v8-json.h',
|
|
'include/v8-local-handle.h',
|
|
'include/v8-locker.h',
|
|
'include/v8-maybe.h',
|
|
'include/v8-memory-span.h',
|
|
'include/v8-message.h',
|
|
'include/v8-microtask-queue.h',
|
|
'include/v8-microtask.h',
|
|
'include/v8-object.h',
|
|
'include/v8-persistent-handle.h',
|
|
'include/v8-platform.h',
|
|
'include/v8-primitive-object.h',
|
|
'include/v8-primitive.h',
|
|
'include/v8-profiler.h',
|
|
'include/v8-promise.h',
|
|
'include/v8-proxy.h',
|
|
'include/v8-regexp.h',
|
|
'include/v8-sandbox.h',
|
|
'include/v8-script.h',
|
|
'include/v8-snapshot.h',
|
|
'include/v8-source-location.h',
|
|
'include/v8-statistics.h',
|
|
'include/v8-template.h',
|
|
'include/v8-traced-handle.h',
|
|
'include/v8-typed-array.h',
|
|
'include/v8-unwinder.h',
|
|
'include/v8-value-serializer.h',
|
|
'include/v8-value.h',
|
|
'include/v8-version.h',
|
|
'include/v8-wasm.h',
|
|
'include/v8-weak-callback-info.h',
|
|
'include/v8.h',
|
|
'include/v8config.h',
|
|
]
|
|
if sys.platform == 'win32':
|
|
# Native win32 python uses \ for path separator.
|
|
v8_headers = [os.path.normpath(path) for path in v8_headers]
|
|
if os.path.isabs(options.v8_dir):
|
|
rel_v8_dir = os.path.relpath(options.v8_dir, options.root_dir)
|
|
else:
|
|
rel_v8_dir = options.v8_dir
|
|
files_arg = [name for name in files_arg if os.path.relpath(name, rel_v8_dir) in v8_headers]
|
|
action(options, files_arg, dest)
|
|
|
|
def wanted_zoslib_headers(options, files_arg, dest):
|
|
import glob
|
|
zoslib_headers = glob.glob(zoslibinc + '/*.h')
|
|
files_arg = [name for name in files_arg if name in zoslib_headers]
|
|
action(options, files_arg, dest)
|
|
|
|
action(options, [
|
|
options.config_gypi_path,
|
|
'common.gypi',
|
|
'src/node.h',
|
|
'src/node_api.h',
|
|
'src/js_native_api.h',
|
|
'src/js_native_api_types.h',
|
|
'src/node_api_types.h',
|
|
'src/node_buffer.h',
|
|
'src/node_object_wrap.h',
|
|
'src/node_version.h',
|
|
], 'include/node/')
|
|
|
|
# Add the expfile that is created on AIX
|
|
if sys.platform.startswith('aix') or sys.platform == "os400":
|
|
action(options, ['out/Release/node.exp'], 'include/node/')
|
|
|
|
subdir_files(options, os.path.join(options.v8_dir, 'include'), 'include/node/', wanted_v8_headers)
|
|
|
|
if 'false' == options.variables.get('node_shared_libuv'):
|
|
subdir_files(options, 'deps/uv/include', 'include/node/', action)
|
|
|
|
if 'true' == options.variables.get('node_use_openssl') and \
|
|
'false' == options.variables.get('node_shared_openssl'):
|
|
subdir_files(options, 'deps/openssl/openssl/include/openssl', 'include/node/openssl/', action)
|
|
subdir_files(options, 'deps/openssl/config/archs', 'include/node/openssl/archs', action)
|
|
subdir_files(options, 'deps/openssl/config', 'include/node/openssl', action)
|
|
|
|
if 'false' == options.variables.get('node_shared_zlib'):
|
|
action(options, [
|
|
'deps/zlib/zconf.h',
|
|
'deps/zlib/zlib.h',
|
|
], 'include/node/')
|
|
|
|
if sys.platform == 'zos':
|
|
zoslibinc = os.environ.get('ZOSLIB_INCLUDES')
|
|
if not zoslibinc:
|
|
raise RuntimeError('Environment variable ZOSLIB_INCLUDES is not set\n')
|
|
if not os.path.isfile(zoslibinc + '/zos-base.h'):
|
|
raise RuntimeError('ZOSLIB_INCLUDES is not set to a valid location\n')
|
|
subdir_files(options, zoslibinc, 'include/node/zoslib/', wanted_zoslib_headers)
|
|
|
|
def run(options):
|
|
if options.headers_only:
|
|
if options.command == 'install':
|
|
headers(options, install)
|
|
return
|
|
if options.command == 'uninstall':
|
|
headers(options, uninstall)
|
|
return
|
|
else:
|
|
if options.command == 'install':
|
|
files(options, install)
|
|
return
|
|
if options.command == 'uninstall':
|
|
files(options, uninstall)
|
|
return
|
|
|
|
raise RuntimeError('Bad command: %s\n' % options.command)
|
|
|
|
def parse_options(args):
|
|
parser = argparse.ArgumentParser(
|
|
description='Install headers and binaries into filesystem')
|
|
parser.add_argument('command', choices=['install', 'uninstall'])
|
|
parser.add_argument('--dest-dir', help='custom install prefix for packagers, i.e. DESTDIR',
|
|
default=os.getcwd())
|
|
parser.add_argument('--prefix', help='custom install prefix, i.e. PREFIX',
|
|
default='/usr/local')
|
|
parser.add_argument('--headers-only', help='only install headers',
|
|
action='store_true', default=False)
|
|
parser.add_argument('--root-dir', help='the root directory of source code',
|
|
default=os.getcwd())
|
|
parser.add_argument('--build-dir', help='the location of built binaries',
|
|
default='out/Release')
|
|
parser.add_argument('--v8-dir', help='the location of V8',
|
|
default='deps/v8')
|
|
parser.add_argument('--config-gypi-path', help='the location of config.gypi',
|
|
default='config.gypi')
|
|
parser.add_argument('--is-win', help='build for Windows target',
|
|
action='store_true',
|
|
default=(sys.platform in ['win32', 'cygwin']))
|
|
parser.add_argument('--silent', help='do not output log',
|
|
action='store_true', default=False)
|
|
options = parser.parse_args(args)
|
|
|
|
# |dest_dir| is a custom install prefix for packagers (think DESTDIR)
|
|
# |prefix| is a custom install prefix (think PREFIX)
|
|
# Difference is that dest_dir won't be included in shebang lines etc.
|
|
# |install_path| thus becomes the base target directory.
|
|
options.install_path = os.path.join(options.dest_dir + options.prefix)
|
|
|
|
# Read variables from the config.gypi.
|
|
with open(options.config_gypi_path) as f:
|
|
config = ast.literal_eval(f.read())
|
|
options.variables = config['variables']
|
|
|
|
return options
|
|
|
|
if __name__ == '__main__':
|
|
run(parse_options(sys.argv[1:]))
|