diff --git a/.github/label-pr-config.yml b/.github/label-pr-config.yml index 0b94dd5755d7d6..1e6d0d28ea18d8 100644 --- a/.github/label-pr-config.yml +++ b/.github/label-pr-config.yml @@ -64,7 +64,7 @@ subSystemLabels: /^tools\/(?:certdata|mkssldef|mk-ca-bundle)/: tools, openssl, tls /^tools\/msvs\//: tools, windows, install, needs-ci /^tools\/[^/]+\.bat$/: tools, windows, needs-ci - /^tools\/make-v8/: tools, v8 engine, needs-ci + /^tools\/build_v8/: tools, v8 engine, needs-ci /^tools\/v8_gypfiles/: tools, v8 engine, needs-ci /^tools\/snapshot/: needs-ci /^tools\/build-addons.mjs/: needs-ci diff --git a/Makefile b/Makefile index b1f07c7c3a0158..f2fc717ab21c3b 100644 --- a/Makefile +++ b/Makefile @@ -307,8 +307,7 @@ endif # Rebuilds deps/v8 as a git tree, pulls its third-party dependencies, and # builds it. v8: ## Build deps/v8. - export PATH="$(NO_BIN_OVERRIDE_PATH)" && \ - tools/make-v8.sh $(V8_ARCH).$(BUILDTYPE_LOWER) $(V8_BUILD_OPTIONS) + $(PYTHON) tools/build_v8.py $(V8_ARCH).$(BUILDTYPE_LOWER) --v8-build-options "$(V8_BUILD_OPTIONS)" .PHONY: jstest jstest: build-addons build-js-native-api-tests build-node-api-tests build-sqlite-tests ## Run addon tests and JS tests. diff --git a/doc/contributing/maintaining/maintaining-V8.md b/doc/contributing/maintaining/maintaining-V8.md index 740af7f228f694..eb033701583981 100644 --- a/doc/contributing/maintaining/maintaining-V8.md +++ b/doc/contributing/maintaining/maintaining-V8.md @@ -180,7 +180,7 @@ See [`git-node-v8-backport`][] for more documentation and additional options. _vY.x-staging_ branch and notify the `@nodejs/v8` team. * Run the Node.js [V8 CI][] in addition to the [Node.js CI][]. The CI uses the `test-v8` target in the `Makefile`, which uses - `tools/make-v8.sh` to reconstruct a git tree in the `deps/v8` directory to + `tools/build_v8.py` to reconstruct a git tree in the `deps/v8` directory to run V8 tests.[^1] Here are the steps for the bug mentioned above: diff --git a/tools/build_v8.py b/tools/build_v8.py new file mode 100644 index 00000000000000..e4142bede7dfd4 --- /dev/null +++ b/tools/build_v8.py @@ -0,0 +1,138 @@ +#!/usr/bin/env python3 + +import os +import sys +import subprocess +import platform +import shlex +import shutil +from pathlib import Path +from argparse import ArgumentParser + +from v8.fetch_deps import FetchDeps +from v8.node_common import UninitGit +from utils import IsWindows + +v8_arch_map = { + "amd64": "x64", + "x86_64": "x64", + "i386": "x86", + "i686": "x86", + "arm64": "arm64", + "aarch64": "arm64", +} + +class V8Builder: + """Handles the building of V8 for different architectures.""" + + def __init__(self, build_arch_type, jobs=None, v8_build_options=None): + self.build_arch_type = build_arch_type + self.jobs = jobs + self.v8_build_options = v8_build_options + self.arch = platform.machine() + self.v8_dir = Path('deps/v8').resolve() + + def fetch_dependencies(self): + """Initialize repository and fetch dependencies.""" + UninitGit(str(self.v8_dir)) + self.depot_tools = FetchDeps(str(self.v8_dir)) + + def get_ninja_jobs_arg(self): + """Return jobs argument for ninja if specified.""" + return ['-j', str(self.jobs)] if self.jobs else [] + + def create_symlink(self, env_var, default_bin, build_tools): + """Create compiler symlink if a custom compiler is specified.""" + custom_compiler = os.environ.get(env_var) + + if not custom_compiler or custom_compiler == default_bin: + return + + bin_path = shutil.which(custom_compiler) + if not bin_path or 'ccache' in bin_path: + return + + link_path = Path(build_tools) / default_bin + try: + if link_path.exists() or link_path.is_symlink(): + link_path.unlink() + link_path.symlink_to(bin_path) + except OSError as e: + print(f"Failed to create symlink: {e}") + + def build_special_arch(self): + """Build for s390x or ppc64le architectures.""" + target_arch = 'ppc64' if self.arch == 'ppc64le' else self.arch + build_tools = '/home/iojs/build-tools' + + env = os.environ.copy() + env['BUILD_TOOLS'] = build_tools + env['LD_LIBRARY_PATH'] = f"{build_tools}:{env.get('LD_LIBRARY_PATH', '')}" + env['PATH'] = f"{build_tools}:{env['PATH']}" + env['PKG_CONFIG_PATH'] = f"{build_tools}/pkg-config" + + self.create_symlink('CC', 'gcc', build_tools) + self.create_symlink('CXX', 'g++', build_tools) + + cc_wrapper = 'cc_wrapper="ccache"' if 'CXX' in env and 'ccache' in env['CXX'] else "" + + gn_args = ( + f"is_component_build=false is_debug=false use_goma=false " + f"goma_dir=\"None\" use_custom_libcxx=false v8_target_cpu=\"{target_arch}\" " + f"target_cpu=\"{target_arch}\" v8_enable_backtrace=true {cc_wrapper}" + ) + + out_dir = f"out.gn/{self.build_arch_type}" + subprocess.run(['gn', 'gen', '-v', out_dir, f"--args={gn_args}"], env=env, check=True) + + ninja_cmd = ['ninja', '-v', '-C', out_dir] + ninja_cmd.extend(self.get_ninja_jobs_arg()) + ninja_cmd.extend(['d8', 'cctest', 'inspector-test']) + subprocess.run(ninja_cmd, env=env, check=True) + + def build_standard_arch(self): + """Build for standard architectures using depot_tools.""" + env = os.environ.copy() + env['PATH'] = f"{self.depot_tools}:{env.get('PATH', '')}" + + v8gen_cmd = [sys.executable, 'tools/dev/v8gen.py', '-vv', self.build_arch_type] + if self.v8_build_options: + v8gen_cmd.extend(shlex.split(self.v8_build_options)) + if IsWindows(): + env["DEPOT_TOOLS_WIN_TOOLCHAIN"] = "0" + + subprocess.run(v8gen_cmd, env=env, check=True) + + out_dir = f"out.gn/{self.build_arch_type}" + ninja_cmd = ['ninja', '-C', out_dir] + ninja_cmd.extend(self.get_ninja_jobs_arg()) + ninja_cmd.extend(['d8', 'cctest', 'inspector-test']) + subprocess.run(ninja_cmd, env=env, check=True) + + def build(self): + """Main build function that orchestrates the build process.""" + os.chdir(self.v8_dir) + self.fetch_dependencies() + + if self.arch in ['s390x', 'ppc64le']: + self.build_special_arch() + else: + self.build_standard_arch() + +def main(): + """Parse arguments and initiate the V8 build.""" + parser = ArgumentParser(description='Build V8 with specified configuration') + parser.add_argument('build_arch_type', nargs='?', default=f"{v8_arch_map.get(platform.machine(), 'x64')}.release", + help='Build architecture type (e.g., x64.release)') + parser.add_argument('-j', '--jobs', type=int, + help='Number of jobs to run simultaneously') + parser.add_argument('--v8-build-options', type=str, default=None, + help='Additional V8 build options as a quoted string') + + args = parser.parse_args() + + builder = V8Builder(args.build_arch_type, args.jobs, args.v8_build_options) + builder.build() + +if __name__ == '__main__': + main() diff --git a/tools/make-v8.sh b/tools/make-v8.sh deleted file mode 100755 index 8d8f090f0fe794..00000000000000 --- a/tools/make-v8.sh +++ /dev/null @@ -1,60 +0,0 @@ -#!/bin/sh - -set -xe - -BUILD_ARCH_TYPE=$1 -V8_BUILD_OPTIONS=$2 - -cd deps/v8 || exit -find . -type d -name .git -print0 | xargs -0 rm -rf -../../tools/v8/fetch_deps.py . - -JOBS_ARG= -if [ "${JOBS}" ]; then - JOBS_ARG="-j ${JOBS}" -fi - -ARCH=$(arch) -if [ "$ARCH" = "s390x" ] || [ "$ARCH" = "ppc64le" ]; then - TARGET_ARCH=$ARCH - if [ "$ARCH" = "ppc64le" ]; then - TARGET_ARCH="ppc64" - fi - # set paths manually for now to use locally installed gn - export BUILD_TOOLS=/home/iojs/build-tools - export LD_LIBRARY_PATH="$BUILD_TOOLS:$LD_LIBRARY_PATH" - rm -f "$BUILD_TOOLS/g++" - rm -f "$BUILD_TOOLS/gcc" - # V8's build config looks for binaries called `gcc` and `g++` if not using - # clang. Ensure that `gcc` and `g++` point to the compilers we want to - # invoke, creating symbolic links placed at the front of PATH, if needed. - # Avoid linking to ccache symbolic links as ccache decides which binary - # to run based on the name of the link (i.e. `gcc`/`g++` in our case). - # shellcheck disable=SC2154 - if [ "$CC" != "" ] && [ "$CC" != "gcc" ]; then - CC_PATH=$(command -v "$CC" gcc | grep -v ccache | head -n 1) - ln -s "$CC_PATH" "$BUILD_TOOLS/gcc" - fi - # shellcheck disable=SC2154 - if [ "$CXX" != "" ] && [ "$CXX" != "g++" ]; then - CXX_PATH=$(command -v "$CXX" g++ | grep -v ccache | head -n 1) - ln -s "$CXX_PATH" "$BUILD_TOOLS/g++" - fi - export PATH="$BUILD_TOOLS:$PATH" - # Propagate ccache to gn. - case "$CXX" in - *ccache*) CC_WRAPPER="cc_wrapper=\"ccache\"" ;; - *) ;; - esac - - g++ --version - gcc --version - export PKG_CONFIG_PATH=$BUILD_TOOLS/pkg-config - gn gen -v "out.gn/$BUILD_ARCH_TYPE" --args="is_component_build=false is_debug=false use_goma=false goma_dir=\"None\" use_custom_libcxx=false v8_target_cpu=\"$TARGET_ARCH\" target_cpu=\"$TARGET_ARCH\" v8_enable_backtrace=true $CC_WRAPPER" - ninja -v -C "out.gn/$BUILD_ARCH_TYPE" "${JOBS_ARG}" d8 cctest inspector-test -else - DEPOT_TOOLS_DIR="$(cd depot_tools && pwd)" - # shellcheck disable=SC2086 - PATH="$DEPOT_TOOLS_DIR":$PATH tools/dev/v8gen.py "$BUILD_ARCH_TYPE" $V8_BUILD_OPTIONS - PATH="$DEPOT_TOOLS_DIR":$PATH ninja -C "out.gn/$BUILD_ARCH_TYPE/" "${JOBS_ARG}" d8 cctest inspector-test -fi diff --git a/tools/test-v8.bat b/tools/test-v8.bat index 64265157f00d80..539a8bed8f60b0 100644 --- a/tools/test-v8.bat +++ b/tools/test-v8.bat @@ -6,8 +6,8 @@ set path=%DEPOT_TOOLS_PATH%;%path% pushd . set ERROR_STATUS=0 -echo calling: tools\make-v8.sh -sh tools\make-v8.sh +echo calling: tools\build_v8.py +python3 tools\build_v8.py if errorlevel 1 set ERROR_STATUS=1&goto test-v8-exit cd %~dp0 cd deps\v8 diff --git a/tools/v8/fetch_deps.py b/tools/v8/fetch_deps.py index b7e628608ad419..2e19f858d6540c 100755 --- a/tools/v8/fetch_deps.py +++ b/tools/v8/fetch_deps.py @@ -16,7 +16,7 @@ import subprocess import sys -import node_common +from . import node_common GCLIENT_SOLUTION = [ { "name" : "v8", @@ -77,9 +77,15 @@ def FetchDeps(v8_path): env = os.environ.copy() # gclient needs to have depot_tools in the PATH. env["PATH"] = depot_tools + os.pathsep + env["PATH"] - gclient = os.path.join(depot_tools, "gclient.py") + + if os.name == 'nt': + gclient = [os.path.join(depot_tools, "gclient.bat")] + env["DEPOT_TOOLS_WIN_TOOLCHAIN"] = "0" + else: + gclient = [sys.executable, os.path.join(depot_tools, "gclient.py")] + spec = "solutions = %s" % GCLIENT_SOLUTION - subprocess.check_call([sys.executable, gclient, "sync", "--spec", spec], + subprocess.check_call([*gclient, "sync", "--spec", spec], cwd=os.path.join(v8_path, os.path.pardir), env=env) except: diff --git a/tools/v8/node_common.py b/tools/v8/node_common.py index f873065c1dfe81..5642137ba82ad0 100755 --- a/tools/v8/node_common.py +++ b/tools/v8/node_common.py @@ -3,11 +3,7 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -# for py2/py3 compatibility -from __future__ import print_function - import os -import pipes import shutil import stat import subprocess @@ -27,10 +23,8 @@ def _Get(v8_path): pass if fetch_if_not_exist: print("Checking out depot_tools.") - # shell=True needed on Windows to resolve git.bat. - subprocess.check_call("git clone {} {}".format( - pipes.quote(DEPOT_TOOLS_URL), - pipes.quote(depot_tools)), shell=True) + + subprocess.check_call(['git', 'clone', DEPOT_TOOLS_URL, depot_tools]) # Using check_output to hide warning messages. subprocess.check_output( [sys.executable, gclient_path, "metrics", "--opt-out"],