Upload Kmake

This commit is contained in:
Gorochu
2026-05-26 23:36:42 -07:00
parent ba051b2f74
commit 555ec72358
41615 changed files with 13344630 additions and 1 deletions

View File

@ -0,0 +1,150 @@
#!/usr/bin/env python3
# Copyright 2025 the V8 project authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can
# be found in the LICENSE file.
import argparse
import json
import os
from pathlib import Path
import re
import subprocess
import sys
TEST_ROOT = Path(__file__).absolute().parent.parent.parent / 'test'
TEST_SUITES = [
'mjsunit', 'debugger', 'inspector', 'message', 'js-perf-test', 'webkit'
]
TEST_EXTENSIONS = ('.js', '.mjs')
HAS_FLAGS_RE = re.compile("//\\s*Flags:.*\n")
EXTRACT_FLAGS_RE = re.compile("//\\s*Flags:\\s+((--[A-z0-9-_=]+\\s*)+).*")
FEATURE_FLAG_PREFIXES = {
'js': ('--js-', '--js_'),
'harmony': ('--harmony-', '--harmony_'),
'wasm': ('--experimental-wasm-', '--experimental_wasm_')
}
NEG_FEATURE_FLAG_PREFIXES = {
'js': ('--no-js-', '--no_js_'),
'harmony': ('--no-harmony-', '--no_harmony_'),
'wasm': ('--no-experimental-wasm-', '--no_experimental_wasm_')
}
STAGING_FLAGS = {
'js': '--js-staging',
'harmony': '--harmony',
'wasm': '--wasm-staging'
}
SHIPPING_FLAGS = {
'js': '--js-shipping',
'harmony': '--harmony-shipping',
'wasm': '' # Wasm doesn't have a shipping flag
}
parser = argparse.ArgumentParser()
parser.add_argument(
'--outdir', required=True, type=Path, help='build directory with d8')
args = parser.parse_args()
D8 = os.path.join(args.outdir, 'd8')
def raw_flags_to_cli_flags(raw_flags_for_lang):
return {
kind:
set([f'--{flag}' for flag in raw_flags] +
[f"--{flag.replace('_', '-')}" for flag in raw_flags])
for kind, raw_flags in raw_flags_for_lang.items()
}
def get_feature_flags():
p = subprocess.run([D8, '--print-feature-flags-json'],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
raw_flags = json.loads(p.stdout)
return {key: raw_flags_to_cli_flags(val) for key, val in raw_flags.items()}
def clean_up_feature_flags(contents, all_flags_by_lang):
# Returns a (bool, string) pair, where the bool says whether the contents
# changed due to a flag being cleaned up.
if not re.search(HAS_FLAGS_RE, contents):
return (False, contents)
dirty = False
cleaned_contents = []
emitted_staging_flags = set()
for line in contents.splitlines():
match = re.search(EXTRACT_FLAGS_RE, line)
if match:
inprogress_flags = set(match.group(1).split())
original_flags = inprogress_flags.copy()
for (lang, lang_flags) in all_flags_by_lang.items():
# Remove staged flags.
inprogress_flags_no_staged = inprogress_flags - lang_flags['staged']
# If any staged flags were removed, emit the staging flag instead. Only
# do so once per file.
if (inprogress_flags_no_staged
!= inprogress_flags) and (lang not in emitted_staging_flags):
inprogress_flags_no_staged.add(STAGING_FLAGS[lang])
emitted_staging_flags.add(lang)
inprogress_flags = inprogress_flags_no_staged
# Remove flags that are shipping.
inprogress_flags -= lang_flags['shipping']
# Heuristically remove flags that look like feature flags (prefixed by
# one of the known prefixes) that don't exist anymore.
for flag in inprogress_flags.copy():
if flag == STAGING_FLAGS[lang] or flag == SHIPPING_FLAGS[lang]:
continue
if flag.startswith(FEATURE_FLAG_PREFIXES[lang]) and (
flag not in lang_flags['in-progress']):
inprogress_flags.remove(flag)
elif flag.startswith(NEG_FEATURE_FLAG_PREFIXES[lang]) and (
f"--{flag[5:]}" not in lang_flags['in-progress']):
inprogress_flags.remove(flag)
if inprogress_flags != original_flags:
dirty = True
if len(inprogress_flags) > 0:
cleaned_contents.append(f"// Flags: {' '.join(inprogress_flags)}")
else:
cleaned_contents.append(line)
else:
cleaned_contents.append(line)
if not dirty:
return (False, contents)
else:
return (True, "\n".join(cleaned_contents) + "\n")
if __name__ == '__main__':
flags = get_feature_flags()
# Walk test files.
for suite_dir in TEST_SUITES:
suite_path = TEST_ROOT / suite_dir
for dirname, dirs, files in os.walk(suite_path, followlinks=True):
for filename in files:
if not filename.endswith(TEST_EXTENSIONS):
continue
abspath = Path(dirname) / filename
encoding = 'utf-8'
with open(abspath, 'rb') as fh:
# We still have some test files that aren't valid UTF-8. But we also
# can't unconditionally use ISO-8859-1, because we also have tests
# that can only be encoded in UTF-8.
try:
contents = fh.read().decode(encoding)
except:
encoding = 'ISO-8859-1'
contents = fh.read().decode(encoding)
contents_changed, contents = clean_up_feature_flags(contents, flags)
if contents_changed:
with open(abspath, 'w', encoding=encoding) as fh:
fh.write(contents)
print(f"Changed flags in {abspath}")
sys.exit(0)

120
deps/v8/tools/dev/gen-static-roots.py vendored Executable file
View File

@ -0,0 +1,120 @@
#!/usr/bin/env python3
# Copyright 2022 the V8 project authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can
# be found in the LICENSE file.
import subprocess
import argparse
import os
import filecmp
import tempfile
import shutil
import platform
import re
import sys
from pathlib import Path
# List of all supported build configurations for static roots
STATIC_ROOT_CONFIGURATIONS = {
"ptr-cmpr-wasm-intl": {
"target": "src/roots/static-roots.h",
"gn_args": {
"v8_enable_static_roots": "false",
"v8_enable_static_roots_generation": "true",
"v8_enable_pointer_compression": "true",
"v8_enable_shared_ro_heap": "true",
"v8_enable_pointer_compression_shared_cage": "true",
"v8_enable_webassembly": "true",
"v8_enable_i18n_support": "true",
"dcheck_always_on": "false"
}
},
}
# Parse args
parser = argparse.ArgumentParser(description='Generates static-roots.h.')
parser.add_argument(
'--configuration',
choices=STATIC_ROOT_CONFIGURATIONS.keys(),
action='extend',
default='ptr-cmpr-wasm-intl',
nargs='*',
help="""Build configuration. Refers to a set of configurations with
identical static-roots.h. Currently there is only one supported configuration.
Future configurations will need to generate multiple target files.""")
parser.add_argument(
'--out',
default=Path('out/static-roots'),
required=False,
type=Path,
help='target build directory')
args = parser.parse_args()
# Some helpers
def run(cmd, **kwargs):
print(f"# CMD: {cmd}")
return subprocess.run(cmd, **kwargs, check=True)
def machine_target():
raw = platform.machine()
raw_lower = raw.lower()
if raw_lower == "x86_64" or raw_lower == "amd64":
return "x64"
if raw_lower == "aarch64":
return "arm64"
return raw
V8_PATH = Path(__file__).parents[2]
GM = V8_PATH / 'tools' / 'dev' / 'gm.py'
changed = False
for target in [args.configuration]:
out_dir = args.out / f"{target}"
if not out_dir.exists():
out_dir.mkdir(parents=True, exist_ok=True)
build_dir = out_dir / f"{machine_target()}.release"
config = STATIC_ROOT_CONFIGURATIONS[target]
# Let gm create the default config
e = dict(os.environ)
e['V8_GM_OUTDIR'] = f"{out_dir}"
run([f"{sys.executable}", f"{GM}", f"{machine_target()}.release.gn_args"],
env=e)
# Patch the default config according to our needs
print("# Updating gn args")
gn_args_template = config["gn_args"].copy()
gn_args = (build_dir / "args.gn").open("r").read().splitlines()
with (build_dir / "args.gn").open("w") as f:
for line in filter(bool, gn_args):
result = re.search(r"^([^ ]+) = (.+)$", line)
if not result or len(result.groups()) != 2:
print(f"Error parsing {build_dir / 'args.gn'} at line '{line}'")
exit(255)
if result.group(1) in gn_args_template:
line = f"{result.group(1)} = {gn_args_template[result.group(1)]}"
gn_args_template.pop(result.group(1))
f.write(f"{line}\n")
for extra, val in gn_args_template.items():
f.write(f"{extra} = {val}\n")
# Build mksnapshot
run([f"{sys.executable}", f"{GM}", f"{machine_target()}.release.mksnapshot"],
env=e)
# Generate static roots file and check if it changed
out_file = Path(tempfile.gettempdir()) / f"static-roots-{target}.h"
run([f"{build_dir / 'mksnapshot'}", "--static-roots-src", f"{out_file}"])
target_file = V8_PATH / config["target"]
if not filecmp.cmp(out_file, target_file):
shutil.move(out_file, target_file)
changed = True
if changed:
exit(1)

94
deps/v8/tools/dev/gen-tags.py vendored Executable file
View File

@ -0,0 +1,94 @@
#!/usr/bin/env python3
# Copyright 2017 the V8 project authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""\
Convenience script for generating arch-specific ctags file.
This script MUST be executed at the top directory.
Usage:
$ tools/dev/gen-tags.py [<arch>...]
The example usage is as follows:
$ tools/dev/gen-tags.py x64
If no <arch> is given, it generates tags file for all arches:
$ tools/dev/gen-tags.py
"""
# for py2/py3 compatibility
from __future__ import print_function
import os
import subprocess
import sys
# All arches that this script understands.
ARCHES = ["ia32", "x64", "arm", "arm64", "mips64"]
def PrintHelpAndExit():
print(__doc__)
sys.exit(0)
def _Call(cmd, silent=False):
if not silent: print("# %s" % cmd)
return subprocess.call(cmd, shell=True)
def ParseArguments(argv):
if not "tools/dev" in argv[0]:
PrintHelpAndExit()
argv = argv[1:]
# If no argument is given, then generate ctags for all arches.
if len(argv) == 0:
return ARCHES
user_arches = []
for argstring in argv:
if argstring in ("-h", "--help", "help"):
PrintHelpAndExit()
if argstring not in ARCHES:
print("Invalid argument: %s" % argstring)
sys.exit(1)
user_arches.append(argstring)
return user_arches
def Exclude(fullpath, exclude_arches):
for arch in exclude_arches:
if ("/%s/" % arch) in fullpath: return True
return False
def Main(argv):
user_arches = []
user_arches = ParseArguments(argv)
exclude_arches = list(ARCHES)
for user_arch in user_arches:
exclude_arches.remove(user_arch)
paths = ["include", "src", "test"]
exts = [".h", ".cc", ".c"]
gtags_filename = "gtags.files"
with open(gtags_filename, "w") as gtags:
for path in paths:
for root, dirs, files in os.walk(path):
for file in files:
if not file.endswith(tuple(exts)): continue
fullpath = os.path.join(root, file)
if Exclude(fullpath, exclude_arches): continue
gtags.write(fullpath + os.linesep)
_Call("ctags --fields=+l -L " + gtags_filename)
if __name__ == "__main__":
sys.exit(Main(sys.argv))

785
deps/v8/tools/dev/gm.py vendored Executable file
View File

@ -0,0 +1,785 @@
#!/usr/bin/env python3
# Copyright 2017 the V8 project authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""\
Convenience wrapper for compiling V8 with gn/ninja and running tests.
Sets up build output directories if they don't exist.
Produces simulator builds for non-Intel target architectures.
Uses reclient by default if it is detected (at output directory setup time).
Expects to be run from the root of a V8 checkout.
Usage:
gm.py [<arch>].[<mode>[-<suffix>]].[<target>] [testname...] [--flag]
All arguments are optional. Most combinations should work, e.g.:
gm.py ia32.debug x64.release x64.release-my-custom-opts d8
gm.py android_arm.release.check --progress=verbose
gm.py x64 mjsunit/foo cctest/test-bar/*
For a less automated experience, pass an existing output directory (which
must contain an existing args.gn), e.g.:
gm.py out/foo unittests
Flags are passed unchanged to the test runner. They must start with -- and must
not contain spaces.
"""
# See HELP below for additional documentation.
from __future__ import print_function
import errno
import os
import platform
import re
import subprocess
import sys
import shutil
import time
from enum import IntEnum
from pathlib import Path
USE_PTY = "linux" in sys.platform
if USE_PTY:
import pty
BUILD_TARGETS_TEST = [
"d8", "bigint_shell", "cctest", "inspector-test", "v8_unittests",
"wasm_api_tests"
]
BUILD_TARGETS_ALL = ["all"]
# All arches that this script understands.
ARCHES = [
"ia32", "x64", "arm", "arm64", "mips64el", "ppc64", "riscv32", "riscv64",
"s390x", "android_arm", "android_arm64", "loong64", "fuchsia_x64",
"fuchsia_arm64", "android_riscv64"
]
# Arches that get built/run when you don't specify any.
DEFAULT_ARCHES = ["ia32", "x64", "arm", "arm64"]
SANDBOX_SUPPORTED_ARCHES = ["x64", "arm64"]
# Modes that this script understands.
MODES = {
"release": "release",
"rel": "release",
"debug": "debug",
"dbg": "debug",
"optdebug": "optdebug",
"opt": "optdebug"
}
# Modes that get built/run when you don't specify any.
DEFAULT_MODES = ["release", "debug"]
# Build targets that can be manually specified.
TARGETS = [
"d8", "cctest", "v8_unittests", "v8_fuzzers", "wasm_api_tests", "wee8",
"mkgrokdump", "mksnapshot", "generate-bytecode-expectations",
"inspector-test", "bigint_shell", "wami", "gn_args"
]
# Build targets that get built when you don't specify any (and specified tests
# don't imply any other targets).
DEFAULT_TARGETS = ["d8"]
# Tests that run-tests.py would run by default that can be run with
# BUILD_TARGETS_TESTS.
DEFAULT_TESTS = ["cctest", "debugger", "intl", "message", "mjsunit",
"unittests"]
# These can be suffixed to any <arch>.<mode> combo, or used standalone,
# or used as global modifiers (affecting all <arch>.<mode> combos).
ACTIONS = {
"all": {
"targets": BUILD_TARGETS_ALL,
"tests": [],
"clean": False
},
"tests": {
"targets": BUILD_TARGETS_TEST,
"tests": [],
"clean": False
},
"check": {
"targets": BUILD_TARGETS_TEST,
"tests": DEFAULT_TESTS,
"clean": False
},
"checkall": {
"targets": BUILD_TARGETS_ALL,
"tests": ["ALL"],
"clean": False
},
"clean": {
"targets": [],
"tests": [],
"clean": True
},
}
HELP = """<arch> can be any of: %(arches)s
<mode> can be any of: %(modes)s
<target> can be any of:
- %(targets)s (build respective binary)
- all (build all binaries)
- tests (build test binaries)
- check (build test binaries, run most tests)
- checkall (build all binaries, run more tests)
""" % {
"arches": " ".join(ARCHES),
"modes": " ".join(MODES.keys()),
"targets": ", ".join(TARGETS)
}
TESTSUITES_TARGETS = {
"benchmarks": "d8",
"bigint": "bigint_shell",
"cctest": "cctest",
"debugger": "d8",
"fuzzer": "v8_fuzzers",
"inspector": "inspector-test",
"intl": "d8",
"message": "d8",
"mjsunit": "d8",
"mozilla": "d8",
"test262": "d8",
"unittests": "v8_unittests",
"wasm-api-tests": "wasm_api_tests",
"wasm-js": "d8",
"wasm-spec-tests": "d8",
"webkit": "d8"
}
out_dir_override = os.getenv("V8_GM_OUTDIR")
if out_dir_override and Path(out_dir_override).is_file:
OUTDIR = Path(out_dir_override)
else:
OUTDIR = Path("out")
V8_DIR = Path(__file__).resolve().parent.parent.parent
GCLIENT_FILE_PATH = V8_DIR.parent / ".gclient"
RECLIENT_CERT_CACHE = V8_DIR / ".#gm_reclient_cert_cache"
BUILD_DISTRIBUTION_RE = re.compile(r"\nuse_(remoteexec|goma) = (false|true)")
GOMA_DIR_LINE = re.compile(r"\ngoma_dir = \"[^\"]+\"")
DEPRECATED_RBE_CFG_RE = re.compile(r"\nrbe_cfg_dir = \"[^\"]+\"")
RECLIENT_CFG_RE = re.compile(r"\nreclient_cfg_dir = \"[^\"]+\"")
class Reclient(IntEnum):
NONE = 0
GOOGLE = 1
CUSTOM = 2
def get_v8_solution(solutions):
for solution in solutions:
if (solution["name"] == "v8" or
solution["url"] == "https://chromium.googlesource.com/v8/v8.git"):
return solution
return None
# Note: this function is reused by update-compile-commands.py. When renaming
# this, please update that file too!
def detect_reclient():
if not GCLIENT_FILE_PATH.exists():
return Reclient.NONE
with open(GCLIENT_FILE_PATH) as f:
content = f.read()
try:
config_dict = {}
exec(content, config_dict)
except SyntaxError as e:
print("# Can't detect reclient due to .gclient syntax errors.")
return Reclient.NONE
v8_solution = get_v8_solution(config_dict["solutions"])
if not v8_solution:
print("# Can't detect reclient due to missing v8 gclient solution.")
return Reclient.NONE
custom_vars = v8_solution.get("custom_vars", {})
if custom_vars.get("rbe_instance"):
return Reclient.CUSTOM
if custom_vars.get("download_remoteexec_cfg"):
return Reclient.GOOGLE
return Reclient.NONE
# Note: this function is reused by update-compile-commands.py. When renaming
# this, please update that file too!
def detect_reclient_cert():
now = int(time.time())
# We cache the cert expiration time in a file, because that's much faster
# to read than invoking `gcertstatus`.
if RECLIENT_CERT_CACHE.exists():
with open(RECLIENT_CERT_CACHE, 'r') as f:
cached_time = int(f.read())
if now < cached_time:
return True
cmd = ["gcertstatus", "-nocheck_ssh", "-format=simple"]
ret = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
if ret.returncode != 0:
return False
# Request fresh cert if less than an hour remains. Reproxy will refuse to
# start when the certificate is close to expiring.
MARGIN = 3600
lifetime = int(ret.stdout.decode("utf-8").strip().split(':')[1]) - MARGIN
if lifetime < 0:
return False
with open(RECLIENT_CERT_CACHE, 'w') as f:
f.write(str(now + lifetime))
return True
RECLIENT_MODE = detect_reclient()
if platform.system() == "Linux":
RECLIENT_CFG_REL = "../../buildtools/reclient_cfgs/linux"
else:
RECLIENT_CFG_REL = "../../buildtools/reclient_cfgs"
BUILD_DISTRIBUTION_LINE = ""
if RECLIENT_MODE:
BUILD_DISTRIBUTION_LINE = "\nuse_remoteexec = true"
if RECLIENT_MODE == Reclient.CUSTOM:
BUILD_DISTRIBUTION_LINE += f"\nreclient_cfg_dir = \"{RECLIENT_CFG_REL}\""
RELEASE_ARGS_TEMPLATE = f"""\
is_component_build = false
is_debug = false
%s{BUILD_DISTRIBUTION_LINE}
v8_enable_backtrace = true
v8_enable_disassembler = true
v8_enable_object_print = true
v8_enable_verify_heap = true
dcheck_always_on = false
"""
DEBUG_ARGS_TEMPLATE = f"""\
is_component_build = true
is_debug = true
symbol_level = 2
%s{BUILD_DISTRIBUTION_LINE}
v8_enable_backtrace = true
v8_enable_fast_mksnapshot = true
v8_enable_slow_dchecks = true
v8_optimized_debug = false
"""
OPTDEBUG_ARGS_TEMPLATE = f"""\
is_component_build = true
is_debug = true
symbol_level = 1
%s{BUILD_DISTRIBUTION_LINE}
v8_enable_backtrace = true
v8_enable_fast_mksnapshot = true
v8_enable_verify_heap = true
v8_optimized_debug = true
"""
ARGS_TEMPLATES = {
"release": RELEASE_ARGS_TEMPLATE,
"debug": DEBUG_ARGS_TEMPLATE,
"optdebug": OPTDEBUG_ARGS_TEMPLATE
}
def print_help_and_exit():
print(__doc__)
print(HELP)
sys.exit(0)
# Used by `tools/bash-completion.sh`
def print_completions_and_exit():
for a in ARCHES:
print(str(a))
for m in set(MODES.values()):
print(f"{a}.{m}")
for t in TARGETS:
print(f"{a}.{m}.{t}")
for k in ACTIONS.keys():
print(f"{a}.{m}.{k}")
for t in TARGETS:
print(str(t))
for m in set(MODES.values()):
print(str(m))
sys.exit(0)
def _call(cmd, silent=False):
if not silent:
print(f"# {cmd}")
return subprocess.call(cmd, shell=True)
def _call_with_output_no_terminal(cmd):
return subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True)
def _call_with_output(cmd):
print(f"# {cmd}")
# The following trickery is required so that the 'cmd' thinks it's running
# in a real terminal, while this script gets to intercept its output.
parent, child = pty.openpty()
p = subprocess.Popen(cmd, shell=True, stdin=child, stdout=child, stderr=child)
os.close(child)
output = []
try:
while True:
try:
data = os.read(parent, 512).decode('utf-8')
except OSError as e:
if e.errno != errno.EIO: raise
break # EIO means EOF on some systems
else:
if not data: # EOF
break
print(data, end="")
sys.stdout.flush()
output.append(data)
finally:
os.close(parent)
while p.poll() is None:
print(".", end="")
time.sleep(0.1)
return p.returncode, "".join(output)
def _write(filename, content, log=True):
if log:
print(f"# echo > {filename} << EOF\n{content}EOF")
with filename.open("w") as f:
f.write(content)
def _notify(summary, body):
if (shutil.which('notify-send') is not None and
os.environ.get("DISPLAY") is not None):
_call(f"notify-send '{summary}' '{body}'", silent=True)
else:
print(f"{summary} - {body}")
def _get_machine():
return platform.machine()
def get_path(arch, mode):
return OUTDIR / f"{arch}.{mode}"
def prepare_mksnapshot_cmdline(orig_cmdline, path):
mksnapshot_bin = path / "mksnapshot"
result = f"gdb --args {mksnapshot_bin} "
for w in orig_cmdline.split(" "):
if w.startswith("gen/") or w.startswith("snapshot_blob"):
result += f"{str(path / w)} "
elif w.startswith("../../"):
result += f"{w[6:]} "
else:
result += f"{w} "
return result
def prepare_torque_cmdline(orig_cmdline: str, path):
torque_bin = path / "torque"
args = orig_cmdline.replace("-v8-root ../..", "-v8-root .")
args = args.replace("gen/torque-generated", f"{path}/gen/torque-generated")
return f"gdb --args {torque_bin} {args}"
# Only has a path, assumes that the path (and args.gn in it) already exists.
class RawConfig:
def __init__(self, path, targets, tests=[], clean=False, testrunner_args=[]):
self.path = path
self.targets = set(targets)
self.tests = set(tests)
self.testrunner_args = testrunner_args
self.clean = clean
def extend(self, targets, tests=[], clean=False):
self.targets.update(targets)
self.tests.update(tests)
self.clean |= clean
def update_build_distribution_args(self):
args_gn = self.path / "args.gn"
assert args_gn.exists()
with open(args_gn) as f:
gn_args = f.read()
# Remove custom reclient config path (it will be added again as part of
# the config line below if needed).
new_gn_args = DEPRECATED_RBE_CFG_RE.sub("", gn_args)
new_gn_args = RECLIENT_CFG_RE.sub("", new_gn_args)
new_gn_args = BUILD_DISTRIBUTION_RE.sub(BUILD_DISTRIBUTION_LINE,
new_gn_args)
# Remove stale goma_dir to silence GN warnings about unused options.
new_gn_args = GOMA_DIR_LINE.sub("", new_gn_args)
if gn_args != new_gn_args:
print(f"# Updated gn args:{BUILD_DISTRIBUTION_LINE}")
_write(args_gn, new_gn_args, log=False)
def build(self):
self.update_build_distribution_args()
# If the target is to just build args.gn then we are done here; otherwise
# drop that target because it's not something ninja can build.
if 'gn_args' in self.targets:
self.targets.remove('gn_args')
if len(self.targets) == 0:
return 0
build_ninja = self.path / "build.ninja"
if not build_ninja.exists():
code = _call(f"gn gen {self.path}")
if code != 0:
return code
elif self.clean:
code = _call(f"gn clean {self.path}")
if code != 0:
return code
targets = " ".join(self.targets)
# The implementation of mksnapshot failure detection relies on
# the "pty" module and GDB presence, so skip it on non-Linux.
if not USE_PTY:
return _call(f"autoninja -C {self.path} {targets}")
return_code, output = _call_with_output(
f"autoninja -C {self.path} {targets}")
if return_code != 0 and "FAILED:" in output:
if "snapshot_blob" in output:
if "gen-static-roots.py" in output:
_notify("V8 build requires your attention",
"Please re-generate static roots...")
return return_code
csa_trap = re.compile("Specify option( --csa-trap-on-node=[^ ]*)")
match = csa_trap.search(output)
extra_opt = match.group(1) if match else ""
cmdline = re.compile("python3 ../../tools/run.py ./mksnapshot (.*)")
orig_cmdline = cmdline.search(output).group(1).strip()
cmdline = (
prepare_mksnapshot_cmdline(orig_cmdline, self.path) + extra_opt)
_notify("V8 build requires your attention",
"Detected mksnapshot failure, re-running in GDB...")
_call(cmdline)
elif "run.py ./torque" in output and not ": Torque Error: " in output:
# Torque failed/crashed without printing an error message.
cmdline = re.compile("python3 ../../tools/run.py ./torque (.*)")
orig_cmdline = cmdline.search(output).group(1).strip()
cmdline = f"gdb --args "
cmdline = prepare_torque_cmdline(orig_cmdline, self.path)
_notify("V8 build requires your attention",
"Detecting torque failure, re-running in GDB...")
_call(cmdline)
return return_code
def run_tests(self):
if not self.tests:
return 0
if "ALL" in self.tests:
tests = ""
else:
tests = " ".join(self.tests)
run_tests = Path("tools") / "run-tests.py"
test_runner_args = " ".join(self.testrunner_args)
return _call(
f'"{sys.executable }" {run_tests} --outdir={self.path} {tests} {test_runner_args}'
)
# Contrary to RawConfig, takes arch and mode, and sets everything up
# automatically.
# Note: This class is imported by update-compile-commands.py. When renaming
# anything here, please update that script too!
class ManagedConfig(RawConfig):
def __init__(self,
arch,
mode,
targets,
tests=[],
clean=False,
testrunner_args=[]):
super().__init__(
get_path(arch, mode), targets, tests, clean, testrunner_args)
self.arch = arch
self.mode = mode
def get_target_cpu(self):
cpu = "x86"
if self.arch == "android_arm":
cpu = "arm"
elif self.arch == "android_arm64" or self.arch == "fuchsia_arm64":
cpu = "arm64"
elif self.arch == "android_riscv64":
cpu = "riscv64"
elif self.arch == "arm64" and _get_machine() in ("aarch64", "arm64"):
# arm64 build host:
cpu = "arm64"
elif self.arch == "arm" and _get_machine() in ("aarch64", "arm64"):
cpu = "arm"
elif self.arch == "loong64" and _get_machine() == "loongarch64":
cpu = "loong64"
elif self.arch == "mips64el" and _get_machine() == "mips64":
cpu = "mips64el"
elif "64" in self.arch or self.arch == "s390x":
# Native x64 or simulator build.
cpu = "x64"
return [f"target_cpu = \"{cpu}\""]
def get_v8_target_cpu(self):
if self.arch == "android_arm":
v8_cpu = "arm"
elif self.arch == "android_arm64" or self.arch == "fuchsia_arm64":
v8_cpu = "arm64"
elif self.arch == "android_riscv64":
v8_cpu = "riscv64"
elif self.arch in ("arm", "arm64", "mips64el", "ppc64", "riscv64",
"riscv32", "s390x", "loong64"):
v8_cpu = self.arch
else:
return []
return [f"v8_target_cpu = \"{v8_cpu}\""]
def get_target_os(self):
if self.arch in ("android_arm", "android_arm64", "android_riscv64"):
return ["target_os = \"android\""]
elif self.arch in ("fuchsia_x64", "fuchsia_arm64"):
return ["target_os = \"fuchsia\""]
return []
def get_specialized_compiler(self):
if _get_machine() in ("aarch64", "mips64", "loongarch64"):
# We have no prebuilt Clang for arm64, mips64 or loongarch64 on Linux,
# so use the system Clang instead.
return ["clang_base_path = \"/usr\"", "clang_use_chrome_plugins = false"]
return []
def get_sandbox_flag(self):
if self.arch in SANDBOX_SUPPORTED_ARCHES:
return ["v8_enable_sandbox = true"]
return []
def get_gn_args(self):
# Use only substring before first '-' as the actual mode
mode = re.match("([^-]+)", self.mode).group(1)
template = ARGS_TEMPLATES[mode]
arch_specific = (
self.get_target_cpu() + self.get_v8_target_cpu() +
self.get_target_os() + self.get_specialized_compiler() +
self.get_sandbox_flag())
return template % "\n".join(arch_specific)
def build(self):
path = self.path
args_gn = path / "args.gn"
if not path.exists():
print(f"# mkdir -p {path}")
path.mkdir(parents=True)
if not args_gn.exists():
_write(args_gn, self.get_gn_args())
return super().build()
def run_tests(self):
host_arch = _get_machine()
if host_arch == "arm64":
if platform.system() == "Darwin" and self.arch != "arm64":
# MacOS-arm64 doesn't provide a good experience when users
# accidentally try to run x64 tests.
print(f"Running {self.arch} tests on Mac-arm64 isn't going to work.")
return 1
if platform.system() == "Linux" and "arm" not in self.arch:
# Assume that an arm64 Linux machine may have been set up to run
# arm32 binaries and Android on-device tests; refuse everything else.
print(f"Running {self.arch} tests on Linux-arm64 isn't going to work.")
return 1
# Special handling for "mkgrokdump": if it was built, run it.
if ("mkgrokdump" in self.targets and self.mode == "release" and
((host_arch == "x86_64" and self.arch == "x64") or
(host_arch == "arm64" and self.arch == "arm64"))):
mkgrokdump_bin = self.path / "mkgrokdump"
_call(f"{mkgrokdump_bin} > tools/v8heapconst.py")
return super().run_tests()
def get_test_binary(argstring):
for suite in TESTSUITES_TARGETS:
if argstring.startswith(suite): return TESTSUITES_TARGETS[suite]
return None
class ArgumentParser(object):
def __init__(self):
self.global_targets = set()
self.global_tests = set()
self.global_actions = set()
self.configs = {}
self.testrunner_args = []
def populate_configs(self, arches, modes, targets, tests, clean):
for a in arches:
for m in modes:
path = get_path(a, m)
if path not in self.configs:
self.configs[path] = ManagedConfig(a, m, targets, tests, clean,
self.testrunner_args)
else:
self.configs[path].extend(targets, tests)
def process_global_actions(self):
have_configs = len(self.configs) > 0
for action in self.global_actions:
impact = ACTIONS[action]
if (have_configs):
for c in self.configs:
self.configs[c].extend(**impact)
else:
self.populate_configs(DEFAULT_ARCHES, DEFAULT_MODES, **impact)
def maybe_parse_builddir(self, argstring):
outdir_prefix = str(OUTDIR) + os.path.sep
# {argstring} must have the shape "out/x", and the 'x' part must be
# at least one character.
if not argstring.startswith(outdir_prefix):
return False
if len(argstring) <= len(outdir_prefix):
return False
# "out/foo.d8" -> path="out/foo", targets=["d8"]
# "out/d8.cctest" -> path="out/d8", targets=["cctest"]
# "out/x.y.d8.cctest" -> path="out/x.y", targets=["d8", "cctest"]
words = argstring.split('.')
path_end = len(words)
targets = []
tests = []
clean = False
while path_end > 1:
w = words[path_end - 1]
maybe_target = get_test_binary(w)
if w in TARGETS:
targets.append(w)
elif maybe_target is not None:
targets.append(maybe_target)
tests.append(w)
elif w == 'clean':
clean = True
else:
break
path_end -= 1
targets = targets or DEFAULT_TARGETS
path = Path('.'.join(words[:path_end]))
args_gn = path / "args.gn"
# Only accept existing build output directories, otherwise fall back
# to regular parsing.
if not args_gn.is_file():
return False
if path not in self.configs:
self.configs[path] = RawConfig(path, targets, tests, clean,
self.testrunner_args)
else:
self.configs[path].extend(targets, tests, clean)
return True
def parse_arg(self, argstring):
if argstring in ("-h", "--help", "help"):
print_help_and_exit()
if argstring == "--print-completions":
print_completions_and_exit()
arches = []
modes = []
targets = []
actions = []
tests = []
clean = False
# Special handling for "mkgrokdump": build it for (arm64|x64).release.
if argstring == "mkgrokdump":
arch = "x64"
if _get_machine() in ("aarch64", "arm64"):
arch = "arm64"
self.populate_configs([arch], ["release"], ["mkgrokdump"], [], False)
return
if argstring.startswith("--"):
# Pass all other flags to test runner.
self.testrunner_args.append(argstring)
return
# Specifying a directory like "out/foo" enters "manual mode".
if self.maybe_parse_builddir(argstring):
return
# Specifying a single unit test looks like "unittests/Foo.Bar", test262
# tests have names like "S15.4.4.7_A4_T1", don't split these.
if (argstring.startswith("unittests/") or
argstring.startswith("test262/") or argstring.startswith("fuzzer/") or
argstring.startswith("wasm-api-tests/")):
words = [argstring]
else:
# Assume it's a word like "x64.release" -> split at the dot.
words = argstring.split('.')
if len(words) == 1:
word = words[0]
if word in ACTIONS:
self.global_actions.add(word)
return
if word in TARGETS:
self.global_targets.add(word)
return
maybe_target = get_test_binary(word)
if maybe_target is not None:
self.global_tests.add(word)
self.global_targets.add(maybe_target)
return
for word in words:
if word in ARCHES:
arches.append(word)
elif word in MODES:
modes.append(MODES[word])
elif word in TARGETS:
targets.append(word)
elif word in ACTIONS:
actions.append(word)
else:
for mode in MODES.keys():
if word.startswith(mode + "-"):
prefix = word[:len(mode)]
suffix = word[len(mode) + 1:]
modes.append(MODES[prefix] + "-" + suffix)
break
else:
print(f"Didn't understand: {word}")
sys.exit(1)
# Process actions.
for action in actions:
impact = ACTIONS[action]
targets += impact["targets"]
tests += impact["tests"]
clean |= impact["clean"]
# Fill in defaults for things that weren't specified.
arches = arches or DEFAULT_ARCHES
modes = modes or DEFAULT_MODES
targets = targets or DEFAULT_TARGETS
# Produce configs.
self.populate_configs(arches, modes, targets, tests, clean)
def parse_arguments(self, argv):
if len(argv) == 0:
print_help_and_exit()
for argstring in argv:
self.parse_arg(argstring)
self.process_global_actions()
for c in self.configs:
self.configs[c].extend(self.global_targets, self.global_tests)
return self.configs
def main(argv):
parser = ArgumentParser()
configs = parser.parse_arguments(argv[1:])
return_code = 0
# If we have Reclient with the Google configuration, check for current
# certificate.
if (RECLIENT_MODE == Reclient.GOOGLE and not detect_reclient_cert()):
print("# gcert")
subprocess.check_call("gcert", shell=True)
for c in configs:
return_code += configs[c].build()
if return_code == 0:
for c in configs:
return_code += configs[c].run_tests()
if return_code == 0:
_notify('Done!', 'V8 compilation finished successfully.')
else:
_notify('Error!', 'V8 compilation finished with errors.')
return return_code
if __name__ == "__main__":
sys.exit(main(sys.argv))

251
deps/v8/tools/dev/setup-reclient.py vendored Executable file
View File

@ -0,0 +1,251 @@
#!/usr/bin/env python3
# Copyright 2024 the V8 project authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""\
Convenience script to set up reclient (aka "remoteexec").
"""
from enum import IntEnum
from pathlib import Path
import os
import platform
import re
import subprocess
V8_DIR = Path(__file__).resolve().parent.parent.parent
GCLIENT_FILE_PATH = V8_DIR.parent / ".gclient"
RBE_INSTANCE = "projects/rbe-chromium-untrusted/instances/default_instance"
class MachineType(IntEnum):
EXTERNAL = 0
GOOGLE_DESKTOP = 1
GOOGLE_LAPTOP = 2
MACHINE_TYPE_NAME = {
MachineType.EXTERNAL: "External",
MachineType.GOOGLE_DESKTOP: "Google Desktop",
MachineType.GOOGLE_LAPTOP: "Google Laptop",
}
def DetectShell():
# We could just read the SHELL environment variable, but that can be
# inaccurate when users use multiple shells. So instead, detect the
# shell that this script was called from.
pid = subprocess.check_output(f"ps -p {os.getpid()} -o ppid=", shell=True)
pid = pid.decode("utf-8").strip()
if platform.system() == "Linux":
shell = subprocess.check_output(["readlink", "-f", f"/proc/{pid}/exe"])
return shell.decode("utf-8").strip()
elif platform.system() == "Darwin":
shell = subprocess.check_output(
f"lsof -p {pid} | grep txt | head -1", shell=True)
return shell.decode("utf-8").strip().split(" ")[-1]
else:
raise Exception(f"Unimplemented platform: {platform.system()}")
SHELL = DetectShell()
def InstallGCloudPublic():
existing = subprocess.call("which gcloud", shell=True)
if existing == 0:
print("GCloud is already installed, great.")
return False
print("Downloading and running GCloud installer. "
"Please let it add the CLI tools to your PATH when prompted.")
subprocess.check_call(
"curl -o gcloud-install.sh https://sdk.cloud.google.com", shell=True)
subprocess.check_call("bash gcloud-install.sh", shell=True)
subprocess.check_call("rm gcloud-install.sh", shell=True)
return True
def UpdateGCloud():
try:
# Use "SHELL -i" to consider any updated .bashrc or .zshrc.
# Use "SHELL -l" to consider any updated .bash_profile or .zsh_profile.
gcloud_version = subprocess.check_output(
f"{SHELL} -il -c 'gcloud version 2>/dev/null; exit 0'", shell=True)
gcloud_version = gcloud_version.decode("utf-8").strip()
gcloud_version = int(
re.search(r"Google Cloud SDK (\d+)\.\d+\.\d+", gcloud_version).group(1))
print(f"Detected gcloud version: {gcloud_version}")
except Exception as e:
print(f"{e}")
print("Error running 'gcloud version'. "
"Please fix your gcloud installation manually.")
return False
if gcloud_version < 455:
print("Found old gcloud, updating.")
subprocess.check_call(
f"{SHELL} -il -c 'gcloud components update; exit 0'", shell=True)
return True
def UpdateGclientFile(mode):
if not GCLIENT_FILE_PATH.exists():
print(f".gclient not found, bug in this script?")
return False
with open(GCLIENT_FILE_PATH) as f:
content = f.read()
try:
config_dict = {}
# .gclient is a Python file, so execute it to understand it.
exec(content, config_dict)
except SyntaxError:
print(f"Error parsing .gclient: {content}")
return False
v8_solution_found = False
for solution in config_dict["solutions"]:
if (solution["name"] == "v8" or
solution["url"] == "https://chromium.googlesource.com/v8/v8.git"):
v8_solution_found = True
if "custom_vars" in solution:
custom_vars = solution["custom_vars"]
if custom_vars.get("download_remoteexec_cfg", False) == True:
if (mode == MachineType.GOOGLE_DESKTOP or
custom_vars.get("rbe_instance", "") == RBE_INSTANCE):
# Nothing to do.
return True
else:
custom_vars = solution["custom_vars"] = {}
custom_vars["download_remoteexec_cfg"] = True
if mode == MachineType.EXTERNAL:
custom_vars["rbe_instance"] = RBE_INSTANCE
if not v8_solution_found:
print("Error: v8 solution not found in .gclient")
return False
# Imitate the format that depot_tools/fetch.py produces.
# It's not quite JSON and it's not what Python's "pprint" would do.
def _format_literal(lit):
if isinstance(lit, str):
return f'"{lit}"'
if isinstance(lit, list):
body = ", ".join(_format_literal(i) for i in lit)
return f"[{body}]"
if isinstance(lit, dict):
if not lit.items():
return "{}"
body = "{\n"
for key, value in lit.items():
body += f' "{key}": {_format_literal(value)},\n'
body += " }"
return body
return f"{lit!r}"
new_content = "solutions = [\n"
for s in config_dict["solutions"]:
new_content += " {\n"
for key, value in s.items():
new_content += f' "{key}": {_format_literal(value)},\n'
new_content += " },\n"
new_content += "]\n"
for entry, value in config_dict.items():
if entry == "solutions" or entry.startswith("__"):
continue
new_content += f"{entry} = {_format_literal(value)}\n"
with open(GCLIENT_FILE_PATH, "w") as f:
f.write(new_content)
return True
def PromptLogin(mode):
domain = "chromium.org"
if mode in (MachineType.GOOGLE_DESKTOP, MachineType.GOOGLE_LAPTOP):
domain = "google.com"
print(f"Please log in with your {domain} account. "
"Press <Return> to continue...")
input()
def CipdLogin(mode):
PromptLogin(mode)
subprocess.check_call("cipd auth-login", shell=True)
subprocess.check_call("gclient sync -D", shell=True)
def GCloudLogin(mode):
PromptLogin(mode)
# Use "SHELL -il" to consider any updated .bashrc and .bash_profile.
subprocess.check_call(
f"{SHELL} -il -c 'gcloud auth login --update-adc; exit 0'", shell=True)
def SetupLinux():
mode = MachineType.EXTERNAL
LSB_RELEASE = Path("/etc/lsb-release")
if LSB_RELEASE.exists():
with open(LSB_RELEASE) as f:
lsb_release = f.read()
if "GOOGLE_ROLE=desktop" in lsb_release:
mode = MachineType.GOOGLE_DESKTOP
elif "GOOGLE_ROLE=laptop" in lsb_release:
mode = MachineType.GOOGLE_LAPTOP
print(f"Detected machine type: {MACHINE_TYPE_NAME[mode]}")
if not UpdateGclientFile(mode):
return
CipdLogin(mode)
# Google desktops: gcert will be sufficient for authentication.
# Google laptops: install gcloud via apt.
if mode == MachineType.GOOGLE_LAPTOP:
print("Installing gcloud, please authenticate for 'sudo':")
subprocess.check_call("sudo apt install -y google-cloud-cli", shell=True)
if not UpdateGCloud():
return
GCloudLogin(mode)
# Non-Google machines: install gcloud the public way.
elif mode == MachineType.EXTERNAL:
gcloud_installed = InstallGCloudPublic()
if not UpdateGCloud():
return
GCloudLogin(mode)
if gcloud_installed:
print("GCloud SDK was installed, remember to 'source ~/.bashrc' to "
"make your updated PATH available to the current shell.")
print("All done, enjoy fast builds!")
def SetupMac():
mode = MachineType.EXTERNAL
if Path("/usr/local/google").exists():
print(
"Looks like this could be a Google corp machine, is that correct? "
"[Y/n] ",
end="")
while True:
answer = input()
if answer == "" or answer.lower() == "y":
mode = MachineType.GOOGLE_LAPTOP
break
if answer.lower() == "n":
break
print(f"Answer '{answer}' not understood, please say 'y' or 'n'.")
print(f"Machine type: {MACHINE_TYPE_NAME[mode]}")
if not UpdateGclientFile(mode):
return
CipdLogin(mode)
InstallGCloudPublic()
if not UpdateGCloud():
return
GCloudLogin(mode)
print("All done, enjoy fast builds!")
if __name__ == "__main__":
if platform.system() == "Linux":
SetupLinux()
elif platform.system() == "Darwin":
SetupMac()
else:
print("Platforms other than Linux and Mac are not yet implemented, sorry")

116
deps/v8/tools/dev/update-compile-commands.py vendored Executable file
View File

@ -0,0 +1,116 @@
#!/usr/bin/env python3
# Copyright 2020 the V8 project authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""\
Creates a "compile_commands.json" file for V8, for the needs of clangd and
similar code indexers. Also updates generated C++ sources, and compiles the
Torque Language Server, for a complete code indexing experience.
"""
import json
import os
import subprocess
import sys
import platform
DEFAULT_ARCH = "x64"
if platform.machine() == "arm64":
DEFAULT_ARCH = "arm64"
PYLIB_PATH = 'tools/clang/pylib'
GM_PATH = 'tools/dev'
PYLIB_CHECK = os.path.join(PYLIB_PATH, 'clang', 'compile_db.py')
GM_CHECK = os.path.join(GM_PATH, 'gm.py')
def CheckRelativeImport(path):
if not os.path.exists(path):
print(f"Error: Please run this script from the root of a V8 checkout. "
"{path} must be a valid relative path.")
sys.exit(1)
CheckRelativeImport(PYLIB_CHECK)
CheckRelativeImport(GM_CHECK)
sys.path.insert(0, PYLIB_PATH)
from clang import compile_db
sys.path.insert(0, GM_PATH)
import gm
def _Call(cmd, silent=False):
if not silent:
print(f"# {cmd}")
return subprocess.call(cmd, shell=True)
def _Write(filename, content):
with open(filename, "w") as f:
f.write(content)
def PrepareBuildDir(arch, mode):
build_dir = os.path.join("out", f"{arch}.{mode}")
if not os.path.exists(build_dir):
print(f"# mkdir -p {build_dir}")
os.makedirs(build_dir)
args_gn = os.path.join(build_dir, "args.gn")
if not os.path.exists(args_gn):
conf = gm.ManagedConfig(arch, mode, [])
_Write(args_gn, conf.get_gn_args())
build_ninja = os.path.join(build_dir, "build.ninja")
if not os.path.exists(build_ninja):
code = _Call(f"gn gen {build_dir}")
if code != 0: raise Exception("gn gen failed")
else:
_Call(f"autoninja -C {build_dir} build.ninja")
return build_dir
def AddTargetsForArch(arch, combined):
build_dir = PrepareBuildDir(arch, "debug")
commands = compile_db.ProcessCompileDatabase(
compile_db.GenerateWithNinja(build_dir, ["all"]), [])
added = 0
for c in commands:
key = c["file"]
if key not in combined:
combined[key] = c
added += 1
print(f"{arch}: added {added} compile commands")
def UpdateCompileCommands():
print(">>> Updating compile_commands.json...")
combined = {}
AddTargetsForArch("x64", combined)
AddTargetsForArch("arm64", combined)
if DEFAULT_ARCH != "arm64":
# Mac arm64 doesn't like 32bit platforms:
AddTargetsForArch("ia32", combined)
AddTargetsForArch("arm", combined)
commands = []
for key in combined:
commands.append(combined[key])
_Write("compile_commands.json", json.dumps(commands, indent=2))
def CompileLanguageServer():
print(">>> Compiling Torque Language Server...")
PrepareBuildDir(DEFAULT_ARCH, "release")
_Call(f"autoninja -C out/{DEFAULT_ARCH}.release torque-language-server")
def GenerateCCFiles():
print(">>> Generating generated C++ source files...")
# This must be called after UpdateCompileCommands().
assert os.path.exists(f"out/{DEFAULT_ARCH}.debug/build.ninja")
_Call(f"autoninja -C out/{DEFAULT_ARCH}.debug v8_generated_cc_files")
def PrepareReclient():
reclient_mode = gm.detect_reclient()
if reclient_mode == gm.Reclient.GOOGLE and not gm.detect_reclient_cert():
print("# gcert")
subprocess.check_call("gcert", shell=True)
if __name__ == "__main__":
PrepareReclient()
CompileLanguageServer()
UpdateCompileCommands()
GenerateCCFiles()

173
deps/v8/tools/dev/update-vscode.sh vendored Executable file
View File

@ -0,0 +1,173 @@
#!/bin/bash
# Copyright 2020 the V8 project authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
# The purpose of this script is to make it easy to download/update
# Visual Studio Code on Linux distributions where for whatever reason there
# is no good way to do so via the package manager.
# Version of this script: 2022.11.12
# Basic checking of arguments: want at least one, and it's not --help.
VERSION="$1"
[ -z "$VERSION" -o \
"$VERSION" == "-h" -o \
"$VERSION" == "--help" -o \
"$VERSION" == "help" ] && {
echo "Usage: $0 <version>"
echo "<version> may be --auto for auto-detecting the latest available."
exit 1
}
die() {
echo "Error: $1"
exit 1
}
if [ "$VERSION" == "--auto" -o "$VERSION" == "auto" ]; then
echo "Searching online for latest available version..."
# Where to find the latest available version.
AVAILABLE_PACKAGES_URL="https://packages.microsoft.com/repos/vscode/dists/stable/main/binary-amd64/Packages.gz"
VERSION=$(curl "$AVAILABLE_PACKAGES_URL" --silent \
| gunzip \
| gawk '
BEGIN { engaged = 0 }
# Look at blocks starting with "Package: code".
/^Package: code$/ { engaged = 1 }
# Stop looking at the empty line indicating the end of a block.
/^$/ { engaged = 0 }
# In interesting blocks, print the relevant part of the
# "Version: " line.
match($0, /^Version: ([0-9.]*)/, groups) {
if (engaged == 1) print groups[1]
}
' - \
| sort -rV \
| head -1)
if [ -z "$VERSION" ]; then
die "Detecting latest version failed, please specify it manually."
else
echo "Latest version found: $VERSION"
fi
fi
# Constant definitions for local paths. Edit these to your liking.
VSCODE_DIR="$HOME/vscode"
BACKUP_DIR="$HOME/vscode.prev"
DOWNLOADS_DIR="$HOME/Downloads"
DOWNLOAD_FILE="$DOWNLOADS_DIR/vscode-$VERSION.tar.gz"
DESKTOP_FILE_DIR="$HOME/.local/share/applications"
DESKTOP_FILE="$DESKTOP_FILE_DIR/code.desktop"
# Constant definitions for remote/upstream things. Might need to be updated
# when upstream changes things.
# Where to find the version inside VS Code's installation directory.
PACKAGE_JSON="$VSCODE_DIR/resources/app/package.json"
ICON="$VSCODE_DIR/resources/app/resources/linux/code.png"
# Where to download the archive.
DOWNLOAD_URL="https://update.code.visualstudio.com/$VERSION/linux-x64/stable"
CODE_BIN="$VSCODE_DIR/bin/code"
# Check for "code" in $PATH; create a symlink if we can find a good place.
SYMLINK=$(which code)
if [ -z "$SYMLINK" ]; then
IFS=':' read -ra PATH_ARRAY <<< "$PATH"
for P in "${PATH_ARRAY[@]}"; do
if [ "$P" == "$HOME/bin" -o \
"$P" == "$HOME/local/bin" -o \
"$P" == "$HOME/.local/bin" ]; then
LOCALBIN="$P"
break
fi
done
if [ -n "$LOCALBIN" ]; then
echo "Adding symlink to $LOCALBIN..."
if [ ! -d "$LOCALBIN" ]; then
mkdir -p "$LOCALBIN" || die "Failed to create $LOCALBIN."
fi
ln -s "$CODE_BIN" "$LOCALBIN/code" || die "Failed to create symlink."
else
echo "Please put a symlink to $CODE_BIN somewhere on your \$PATH."
fi
fi
if [ ! -r "$DESKTOP_FILE" ]; then
echo "Creating .desktop file..."
mkdir -p "$DESKTOP_FILE_DIR" || die "Failed to create .desktop directory."
cat <<EOF > "$DESKTOP_FILE"
#!/usr/bin/env xdg-open
[Desktop Entry]
Name=Visual Studio Code
Comment=Code Editing. Redefined.
GenericName=Text Editor
Exec=$CODE_BIN --unity-launch %F
Icon=$ICON
Type=Application
StartupNotify=false
StartupWMClass=Code
Categories=Utility;TextEditor;Development;IDE;
MimeType=text/plain;inode/directory;
Actions=new-empty-window;
Keywords=vscode;
X-Desktop-File-Install-Version=0.24
[Desktop Action new-empty-window]
Name=New Empty Window
Exec=$CODE_BIN --new-window %F
Icon=$ICON
EOF
chmod +x "$DESKTOP_FILE" || die "Failed to make .desktop file executable."
fi
# Find currently installed version.
if [ -d "$VSCODE_DIR" ]; then
if [ ! -r "$PACKAGE_JSON" ]; then
die "$PACKAGE_JSON file not found, this script must be updated."
fi
INSTALLED=$(grep '"version":' "$PACKAGE_JSON" \
| sed 's/[^0-9]*\([0-9.]*\).*/\1/')
echo "Detected installed version: $INSTALLED"
if [ "$VERSION" == "$INSTALLED" ] ; then
echo "You already have that version."
exit 0
else
echo "Updating from $INSTALLED to $VERSION..."
fi
fi
if [ ! -r "$DOWNLOAD_FILE" ]; then
echo "Downloading..."
if [ ! -d "$DOWNLOADS_DIR" ]; then
mkdir -p "$DOWNLOADS_DIR" || die "Failed to create $DOWNLOADS_DIR."
fi
wget "$DOWNLOAD_URL" -O "$DOWNLOAD_FILE" || die "Downloading failed."
else
echo "$DOWNLOAD_FILE already exists; delete it to force re-download."
fi
echo "Extracting..."
TAR_DIR=$(tar -tf "$DOWNLOAD_FILE" | head -1)
[ -z "$TAR_DIR" ] && die "Couldn't read archive."
TMP_DIR=$(mktemp -d)
tar -C "$TMP_DIR" -xf "$DOWNLOAD_FILE" || {
rm -rf "$TMP_DIR"
die "Extracting failed."
}
if [ -d "$BACKUP_DIR" ]; then
echo "Deleting previous backup..."
rm -rf "$BACKUP_DIR"
fi
if [ -d "$VSCODE_DIR" ]; then
echo "Moving previous installation..."
mv "$VSCODE_DIR" "$BACKUP_DIR"
fi
echo "Installing new version..."
mv "$TMP_DIR/$TAR_DIR" "$VSCODE_DIR"
rmdir "$TMP_DIR"
echo "All done, enjoy coding!"

261
deps/v8/tools/dev/v8gen.py vendored Executable file
View File

@ -0,0 +1,261 @@
#!/usr/bin/env python3
# Copyright 2016 the V8 project authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Script to generate V8's gn arguments based on common developer defaults
or builder configurations.
This script can be added to the PATH and be used on other checkouts. It always
runs for the checkout nesting the CWD.
Configurations of this script live in infra/mb/mb_config.pyl.
Available actions are: {gen,list}. Omitting the action defaults to "gen".
-------------------------------------------------------------------------------
Examples:
# Generate the ia32.release config in out.gn/ia32.release.
v8gen.py ia32.release
# Generate into out.gn/foo.
v8gen.py gen -b ia32.release foo
# Pass additional gn arguments after -- (don't use spaces within gn args).
v8gen.py ia32.optdebug -- v8_enable_slow_dchecks=true
# Generate gn arguments of 'V8 Linux64 - builder' from 'client.v8'.
v8gen.py -m client.v8 -b 'V8 Linux64 - builder'
# Show available configurations.
v8gen.py list
-------------------------------------------------------------------------------
"""
# for py2/py3 compatibility
from __future__ import print_function
import argparse
import os
import re
import subprocess
import sys
CONFIG = os.path.join('infra', 'mb', 'mb_config.pyl')
OUT_DIR = 'out.gn'
TOOLS_PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(os.path.join(TOOLS_PATH, 'mb'))
import mb
def _sanitize_nonalpha(text):
return re.sub(r'[^a-zA-Z0-9.]', '_', text)
class GenerateGnArgs(object):
def __init__(self, args):
# Split args into this script's arguments and gn args passed to the
# wrapped gn.
index = args.index('--') if '--' in args else len(args)
self._options = self._parse_arguments(args[:index])
self._gn_args = args[index + 1:]
def _parse_arguments(self, args):
self.parser = argparse.ArgumentParser(
description=__doc__,
formatter_class=argparse.RawTextHelpFormatter,
)
def add_common_options(p):
p.add_argument(
'-m', '--master', default='developer_default',
help='config group or master from mb_config.pyl - default: '
'developer_default')
p.add_argument(
'-v', '--verbosity', action='count',
help='print wrapped commands (use -vv to print output of wrapped '
'commands)')
subps = self.parser.add_subparsers()
# Command: gen.
gen_cmd = subps.add_parser(
'gen', help='generate a new set of build files (default)')
gen_cmd.set_defaults(func=self.cmd_gen)
add_common_options(gen_cmd)
gen_cmd.add_argument(
'outdir', nargs='?',
help='optional gn output directory')
gen_cmd.add_argument(
'-b', '--builder',
help='build configuration or builder name from mb_config.pyl, e.g. '
'x64.release')
gen_cmd.add_argument(
'-p', '--pedantic', action='store_true',
help='run gn over command-line gn args to catch errors early')
# Command: list.
list_cmd = subps.add_parser(
'list', help='list available configurations')
list_cmd.set_defaults(func=self.cmd_list)
add_common_options(list_cmd)
# Default to "gen" unless global help is requested.
if not args or args[0] not in list(subps.choices) + ['-h', '--help']:
args = ['gen'] + args
return self.parser.parse_args(args)
def cmd_gen(self):
if not self._options.outdir and not self._options.builder:
self.parser.error('please specify either an output directory or '
'a builder/config name (-b), e.g. x64.release')
if not self._options.outdir:
# Derive output directory from builder name.
self._options.outdir = _sanitize_nonalpha(self._options.builder)
else:
# Also, if this should work on windows, we might need to use \ where
# outdir is used as path, while using / if it's used in a gn context.
if self._options.outdir.startswith('/'):
self.parser.error(
'only output directories relative to %s are supported' % OUT_DIR)
if not self._options.builder:
# Derive builder from output directory.
self._options.builder = self._options.outdir
# Check for builder/config in mb config.
if self._options.builder not in self._mbw.builder_groups[self._options.master]:
print('%s does not exist in %s for %s' % (
self._options.builder, CONFIG, self._options.master))
return 1
# TODO(machenbach): Check if the requested configurations has switched to
# gn at all.
# The directories are separated with slashes in a gn context (platform
# independent).
gn_outdir = '/'.join([OUT_DIR, self._options.outdir])
# Call MB to generate the basic configuration.
self._call_cmd([
sys.executable,
'-u', os.path.join('tools', 'mb', 'mb.py'),
'gen',
'-f', CONFIG,
'-m', self._options.master,
'-b', self._options.builder,
gn_outdir,
])
# Handle extra gn arguments.
gn_args_path = os.path.join(OUT_DIR, self._options.outdir, 'args.gn')
# Append command-line args.
modified = self._append_gn_args(
'command-line', gn_args_path, '\n'.join(self._gn_args))
# Regenerate ninja files to check for errors in the additional gn args.
if modified and self._options.pedantic:
self._call_cmd(['gn', 'gen', gn_outdir])
return 0
def cmd_list(self):
print('\n'.join(sorted(self._mbw.builder_groups[self._options.master])))
return 0
def verbose_print_1(self, text):
if self._options.verbosity and self._options.verbosity >= 1:
print('#' * 80)
print(text)
def verbose_print_2(self, text):
if self._options.verbosity and self._options.verbosity >= 2:
indent = ' ' * 2
for l in text.splitlines():
if type(l) == bytes:
l = l.decode()
print(indent + l)
def _call_cmd(self, args):
self.verbose_print_1(' '.join(args))
try:
output = subprocess.check_output(
args=args,
stderr=subprocess.STDOUT,
)
self.verbose_print_2(output)
except subprocess.CalledProcessError as e:
self.verbose_print_2(e.output)
raise
def _find_work_dir(self, path):
"""Find the closest v8 root to `path`."""
if os.path.exists(os.path.join(path, 'tools', 'dev', 'v8gen.py')):
# Approximate the v8 root dir by a folder where this script exists
# in the expected place.
return path
elif os.path.dirname(path) == path:
raise Exception(
'This appears to not be called from a recent v8 checkout')
else:
return self._find_work_dir(os.path.dirname(path))
def _append_gn_args(self, type, gn_args_path, more_gn_args):
"""Append extra gn arguments to the generated args.gn file."""
if not more_gn_args:
return False
self.verbose_print_1('Appending """\n%s\n""" to %s.' % (
more_gn_args, os.path.abspath(gn_args_path)))
with open(gn_args_path, 'a') as f:
f.write('\n# Additional %s args:\n' % type)
f.write(more_gn_args)
f.write('\n')
# Artificially increment modification time as our modifications happen too
# fast. This makes sure that gn is properly rebuilding the ninja files.
mtime = os.path.getmtime(gn_args_path) + 1
with open(gn_args_path, 'a'):
os.utime(gn_args_path, (mtime, mtime))
return True
def main(self):
# Always operate relative to the base directory for better relative-path
# handling. This script can be used in any v8 checkout.
workdir = self._find_work_dir(os.getcwd())
if workdir != os.getcwd():
self.verbose_print_1('cd ' + workdir)
os.chdir(workdir)
# Initialize MB as a library.
self._mbw = mb.MetaBuildWrapper()
# TODO(machenbach): Factor out common methods independent of mb arguments.
self._mbw.ParseArgs(['lookup', '-f', CONFIG])
self._mbw.ReadConfigFile()
if not self._options.master in self._mbw.builder_groups:
print('%s not found in %s\n' % (self._options.master, CONFIG))
print('Choose one of:\n%s\n' % (
'\n'.join(sorted(self._mbw.builder_groups.keys()))))
return 1
return self._options.func()
if __name__ == "__main__":
gen = GenerateGnArgs(sys.argv[1:])
try:
sys.exit(gen.main())
except Exception:
if not gen._options.verbosity or gen._options.verbosity < 2:
print ('\nHint: You can raise verbosity (-vv) to see the output of '
'failed commands.\n')
raise