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 pathlib
import typing

from graph import all_headers
from graph import calculate_rdeps
from graph import Header
from graph import IncludeDir
from graph import Target
from platforms import Os

if typing.TYPE_CHECKING:
  # To fix circular dependency.
  from compiler import Compiler

IGNORED_MODULES = [
    # This is a builtin module with feature requirements.
    'opencl_c',
    # This is a mac module with feature requirements that should be disabled.
    '_stddef',
]

# When any of the following directory names are in the path, it's treated as a
# sysroot directory.
SYSROOT_DIRS = {
    'android_toolchain',
    'debian_bullseye_amd64-sysroot',
    'debian_bullseye_arm64-sysroot',
    'debian_bullseye_armhf-sysroot',
    'debian_bullseye_i386-sysroot',
    'fuchsia-sdk',
    'MacOSX.platform',
    'win_toolchain',
}

# It doesn't matter if these don't work on all platforms.
# It'll just print a warning saying it failed to compile.
# This contains a list of files that aren't depended on by libc++, but we still
# want to precompile.
SYSROOT_PRECOMPILED_HEADERS = [
    'fcntl.h',
    'getopt.h',
    'linux/types.h',
    'sys/ioctl.h',
    'syscall.h',
]


def fix_graph(graph: dict[str, Header],
              compiler: 'Compiler') -> dict[pathlib.Path, str]:
  """Applies manual augmentation of the header graph."""

  def force_textual(key: str):
    if key in graph:
      graph[key].textual = True

  def add_dep(frm, to, check=True):
    if check:
      assert to not in frm.deps
    if to not in frm.deps:
      frm.deps.append(to)

  def skip_module(name):
    found = False
    for hdr in graph.values():
      while True:
        if hdr.root_module == name:
          hdr.textual = True
          found = True
        if hdr.next is None:
          break
        hdr = hdr.next
    assert found

  # We made the assumption that the deps of something we couldn't compile is
  # the intersection of the deps of all users of it.
  # This does not hold true for stddef.h because of __need_size_t
  add_dep(graph['stddef.h'].next, graph['__stddef_size_t.h'], check=False)

  if compiler.os in [Os.Android, Os.Win, Os.Fuchsia]:
    # include_next behaves differently in module builds and non-module builds.
    # Because of this, module builds include libcxx's wchar.h instead of
    # the sysroot's wchar.h
    add_dep(graph['__mbstate_t.h'], graph['wchar.h'])
    # This makes the libcxx/wchar.h included by mbstate_t.h act more like
    # sysroot/wchar.h by preventing it from defining functions.
    graph['__mbstate_t.h'].kwargs['defines'].append(
        '_LIBCPP_WCHAR_H_HAS_CONST_OVERLOADS')
  elif compiler.os.is_apple:
    # This is shadowed by the builtin iso646, so we don't need to build it.
    graph['iso646.h'].next.textual = True

  rdeps = calculate_rdeps(all_headers(graph))

  sysroot = graph['assert.h'].abs.parent
  for header in all_headers(graph):
    header.direct_deps = header.calculate_direct_deps(graph, sysroot=sysroot)

  if compiler.os.is_apple:
    # See https://github.com/llvm/llvm-project/issues/154675
    # Darwin defines the symbol "echo" in curses.h
    # Although curses.h is not included, the symbol is part of the module and
    # thus we get an error when attempting to use the symbol "echo" after
    # including *any* part of the module Darwin.
    skip_module("Darwin")
    # This module isn't intended to be used - it's intended to catch
    # misconfigured sysroots.
    skip_module("_c_standard_library_obsolete")
  else:
    for header in all_headers(graph):
      if header.include_dir != IncludeDir.Sysroot:
        continue

      parts = set(pathlib.Path(header.rel).parts)
      # We want non-textual, but we don't need to do so if the header including
      # you via include_next is non-textual.
      if header.prev is not None:
        header.textual = not header.prev.textual
      # Anything not to be included by the user directly that was only included
      # once can be marked as textual. Unfortunately since .d files calculate
      # *transitive* dependencies this is not particularly effective.
      elif (len(rdeps[header]) < 2
            and parts.intersection(['asm', 'asm-generic', 'bits'])):
        header.textual = True
      elif '#pragma once' in (header.content or ''):
        header.textual = False
      elif 'bits' in parts:
        header.textual = True

    # Assert is inherently textual.
    graph['assert.h'].textual = True

  force_textual('asm-generic/unistd.h')
  force_textual('asm-generic/bitsperlong.h')

  if compiler.os == Os.Android:
    graph['android/legacy_stdlib_inlines.h'].textual = True
    graph['android/legacy_threads_inlines.h'].textual = True
    graph['android/legacy_unistd_inlines.h'].textual = True
    graph['bits/threads_inlines.h'].textual = True

    graph['asm-generic/posix_types.h'].textual = True
    graph['asm/posix_types.h'].textual = True

    # sys/syscall.h includes asm/unistd.h, which includes
    # asm/unistd_<platform>.h, which defines some macros.
    # It then includes bits/glibc-syscalls.h which uses said macros, so both
    # must be non-textual.
    for k in graph:
      if k.startswith('asm/unistd'):
        graph[k].textual = True
    graph['bits/glibc-syscalls.h'].textual = True

  elif compiler.os == Os.Linux:
    # See https://codebrowser.dev/glibc/glibc/sysdeps/unix/sysv/linux/bits/local_lim.h.html#56
    # if linux/limits.h is non-textual, then limits.h undefs the limits.h
    # defined in the linux/limits.h module.
    # Thus, limits.h exports an undef.
    # if it's textual, limits.h undefs something it defined itself.
    graph['linux/limits.h'].textual = True

    # This is not included on arm32
    graph['asm-generic/types.h'].textual = True

    # On chromeos, x86_64-linux-gnu/foo.h will be either moved to foo.h or to
    # x86_64-cros-gnu.
    # So we just mark them all as textual so they don't appear in the modulemap.
    for hdr in graph.values():
      if '-linux-gnu' in str(hdr.abs):
        hdr.textual = True

  # Windows has multiple include directories contained with the sysroot.
  if compiler.os == Os.Win:
    graph['math.h'].kwargs['defines'].append('_USE_MATH_DEFINES')
    return {
        graph['corecrt.h'].abs.parent.parent: '$windows_kits',
        graph['eh.h'].abs.parent: '$msvc',
    }
  else:
    return {sysroot: '$sysroot'}


def should_compile(target: Target) -> bool:
  """Decides whether a target should be compiled or not.

  If this returns true, the target should be compiled.
  If this returns false, the target *may* be compiled (eg. if a target that
    should be compiled depends on this).
  """
  for header in target.headers:
    # For now, we only precompile the transitive dependencies of libcxx, and
    # nothing else in the sysroot.
    if header.include_dir == IncludeDir.LibCxx:
      return True

  return False