v8 147.3.0

Rust bindings to V8
Documentation
# Copyright 2025 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

import collections
import contextlib
import io
import pathlib

from compiler import Compiler
import config
from graph import IncludeDir
from graph import Target

_HEADER = """# Copyright 2025 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

# AUTOGENERATED FILE. DO NOT EDIT.
# To regenerate, see instructions at build/modules/modularize/README.md

"""
_RULE_MAP = {
    IncludeDir.Builtin: 'builtin_module',
    IncludeDir.Framework: 'framework_module',
    IncludeDir.LibCxx: 'libcxx_module',
    IncludeDir.Sysroot: 'sysroot_module',
    IncludeDir.SysrootModule: 'apple_sysroot_module',
}

_SYSROOT_MODULEMAP = """sysroot_modulemap("sysroot_modulemap") {
  source = "module.modulemap.in"
  out = "${target_gen_dir}/module.modulemap"
}

"""


def _filter_targets(targets: list[Target], compiler: Compiler) -> list[Target]:
  module_to_modulemap = compiler.modulemaps_for_modules()
  modulemap_to_modules = collections.defaultdict(list)
  for k, v in module_to_modulemap.items():
    modulemap_to_modules[v].append(k)

  remaining = [target for target in targets if config.should_compile(target)]
  compiled = set(remaining)
  targets = {target.name: target for target in targets}
  while remaining:
    target = remaining.pop()
    for dep in target.public_deps:
      dep = targets[dep]
      if dep not in compiled:
        compiled.add(dep)
        remaining.append(dep)

    # If we use -fmodule-map-file=c_standard_library.modulemap for one target,
    # all modules in c_standard_library.modulemap need to be precompiled.
    if dep.name not in module_to_modulemap:
      continue
    for module in modulemap_to_modules[module_to_modulemap[dep.name]]:
      if module in targets and targets[module] not in compiled:
        compiled.add(targets[module])
        remaining.append(targets[module])
  return sorted(compiled)


def _update_content(path: pathlib.Path, content: str):
  """Updates the content of a file if changed, thus preventing a siso reload."""
  with contextlib.suppress(FileNotFoundError):
    if path.read_text() == content:
      return
  path.write_text(content)


def render_modulemap(out_dir: pathlib.Path, replacements: dict[pathlib.Path,
                                                               str],
                     targets: list[Target]):
  """Writes a modulemap to {out_dir}"""
  f = io.StringIO()
  f.write(_HEADER.replace('#', '//'))
  for target in targets:
    if target.include_dir != IncludeDir.Sysroot:
      continue
    f.write(f'\nmodule {target.name} [system] {{\n')
    for header in target.headers:
      assert header.include_dir == IncludeDir.Sysroot
      f.write(f'  module {header.submodule_name} {{\n')
      for single in sorted(header.group):
        assert single.abs is not None
        path = str(single.abs)
        for frm, to in replacements.items():
          path = path.replace(str(frm), to)
        # After replacements, all paths should be absolute.
        assert not path.startswith('/'), path
        f.write(f'    header "{path}"\n')
      if not header.exports and header.exports is not None:
        f.write('    export *\n')
      for export in header.exports or []:
        f.write(f'    export {export.submodule_name}\n')

      f.write('  }\n')

    f.write('}\n')
  _update_content(out_dir / 'module.modulemap.in', f.getvalue())


def _render_string_list(f, indent: int, key: str, values: list[str]):
  if not values:
    return
  indent = " " * indent
  f.write(f'{indent}{key} = ')

  if len(values) == 1:
    f.write(f'[ "{values[0]}" ]\n')
  elif len(values) > 1:
    f.write(f'[\n')
    for value in values:
      f.write(f'{indent}  "{value}",\n')
    f.write(f'{indent}]\n')


def render_build_gn(out_dir: pathlib.Path, targets: list[Target],
                    compiler: Compiler):
  """Renders a BUILD.gn file for a specific platform to {out_dir}"""
  targets = _filter_targets(targets, compiler)

  f = io.StringIO()
  f.write(_HEADER)
  f.write('import("//buildtools/third_party/libc++/modules.gni")\n\n')
  all_modulemap_configs = set()
  if compiler.sysroot_dir == IncludeDir.Sysroot:
    all_modulemap_configs.add("sysroot_modulemap")
    f.write(_SYSROOT_MODULEMAP)

  module_to_modulemaps = compiler.modulemaps_for_modules()

  for target in sorted(targets):
    rule = _RULE_MAP[target.include_dir]

    modulemap_target = None
    if target.name in module_to_modulemaps:
      kind, rel = compiler.split_path(module_to_modulemaps[target.name])
      modulemap_target = rel.removesuffix('/module.modulemap').removesuffix(
          '.modulemap').replace('/', '_') + '_modulemap'
      if kind == IncludeDir.Framework and modulemap_target not in all_modulemap_configs:
        f.write(f'framework_modulemap("{modulemap_target}") {{\n')
        f.write(f'  name = "{modulemap_target.removesuffix("_modulemap")}"\n')
        f.write('}\n')
        all_modulemap_configs.add(modulemap_target)
      elif kind == IncludeDir.SysrootModule and modulemap_target not in all_modulemap_configs:
        f.write(f'apple_sysroot_modulemap("{modulemap_target}") {{\n')
        f.write(f'  sysroot_path = "{rel}"\n')
        f.write('}\n')
        all_modulemap_configs.add(modulemap_target)

    f.write(f'{rule}("{target.name}") {{\n')
    if target.include_dir in [IncludeDir.SysrootModule, IncludeDir.Framework
                              ] and modulemap_target is not None:
      f.write(f'  modulemap = ":{modulemap_target}"\n')
      if kind == IncludeDir.SysrootModule:
        f.write(f'  modulemap_path = "{rel}"\n')
    _render_string_list(f, 2, 'public_deps',
                        [f':{dep}' for dep in target.public_deps])
    for k, v in sorted(target.kwargs.items()):
      _render_string_list(f, 2, k, sorted(v))
    f.write('}\n\n')

  f.write('group("all_modules") {\n')
  f.write('  public_deps = [\n')
  for target in sorted(targets):
    f.write(f'    ":{target.name}",\n')
  f.write('  ]\n')
  f.write('}\n\n')

  f.write('config("all_modulemap_configs") {\n')
  f.write('  configs = [\n')
  f.write('    "//buildtools/third_party/libc++:builtin_modulemap",\n')
  f.write('    "//buildtools/third_party/libc++:libcxx_modulemap",\n')
  for target in sorted(all_modulemap_configs):
    f.write(f'    ":{target}",\n')
  f.write('  ]\n')
  f.write('}\n')

  _update_content(out_dir / 'BUILD.gn', f.getvalue())