current commit
261030.6 · tools: remove `node.gyp` and `node.gypi` from `v8.nix` requisites
# Derivation for Node.js CI (not officially supported for regular applications)
{
  stdenv,
  lib,
  patchutils,
  validatePkgConfig,
  icu,

  buildInputs ? lib.optional (icu != null) icu,
  configureFlags ? [
    (
      if icu == null then
        "--without-intl"
      else
        "--with-intl=${if builtins.isString icu then icu else "system"}-icu"
    )
  ],

  nodejs-slim_latest ? null,
  configureScript ? nodejs-slim_latest.configureScript,
  nativeBuildInputs ? nodejs-slim_latest.nativeBuildInputs,
  patches ? nodejs-slim_latest.patches,
}:

let
  useSharedAbseil = builtins.elem "--shared-abseil" configureFlags;
  useSharedHighway = builtins.elem "--shared-highway" configureFlags;
  useSharedPerfetto = builtins.elem "--shared-perfetto" configureFlags;
  useSharedSimdutf = builtins.elem "--shared-simdutf" configureFlags;
  useSharedTemporal = builtins.elem "--shared-temporal_capi" configureFlags;

  src =
    let
      inherit (lib) fileset;
      root = ../../.;
      files = [
        ../../common.gypi
        ../../configure.py
        ../../deps/v8
        ../../tools/gyp/pylib
        ../../tools/gyp_node.py
        ../../tools/v8_gypfiles/features.gypi
        ../../tools/v8_gypfiles/ForEachFormat.py
        ../../tools/v8_gypfiles/ForEachReplace.py
        ../../tools/v8_gypfiles/GN-scraper.py
        ../../tools/v8_gypfiles/inspector.gypi
        ../../tools/v8_gypfiles/toolchain.gypi
        ../../tools/v8_gypfiles/v8.gyp
      ]
      ++ lib.optional (!useSharedAbseil) ../../tools/v8_gypfiles/abseil.gyp
      ++ lib.optional (!useSharedHighway) ../../tools/v8_gypfiles/highway.gyp
      ++ lib.optional (!useSharedSimdutf) ../../tools/v8_gypfiles/simdutf.gyp
      ++ lib.optional (
        if useSharedTemporal then
          icu == null
        else
          !(builtins.elem "--v8-disable-temporal-support" configureFlags)
      ) ../../deps/crates
      ++ lib.optional (
        builtins.elem "--with-perfetto" configureFlags && !useSharedPerfetto
      ) ../../deps/perfetto
      ++ lib.optionals (icu != null) [
        ../../tools/icu/icu_versions.json
        ../../tools/icu/icu-system.gyp
      ]
      ++ lib.optionals (icu == "small") [
        ../../deps/icu-small
        ../../tools/icu/current_ver.dep
        ../../tools/icu/icu_small.json
        ../../tools/icu/icu-generic.gyp
        ../../tools/icu/iculslocs.cc
        ../../tools/icu/icutrim.py
        ../../tools/icu/no-op.cc
      ];
      potentiallyAlreadyRemovedFiles =
        # Files that are removed in the release tarball (see Makefile $(TARBALL) target)
        [ (fileset.difference ../../deps/v8/test ../../deps/v8/test/torque) ]
        ++ (builtins.filter builtins.pathExists (
          [
            ../../deps/v8/samples
            ../../deps/v8/tools/profviz
            ../../deps/v8/tools/run-tests.py
            ../../deps/v8/third_party/ittapi
          ]
          ++ lib.optional useSharedAbseil ../../deps/v8/third_party/abseil-cpp
          ++ lib.optional useSharedHighway ../../deps/v8/third_party/highway
          ++ lib.optional useSharedSimdutf ../../deps/v8/third_party/simdutf
        ));
      trackedFiles =
        ({
          # This line is being modified by Makefile $(TARBALL) target, any change to it should be sync
          fileset = fileset.intersection (fileset.gitTracked root) (fileset.unions files);
        }).fileset;
    in
    fileset.toSource {
      inherit root;
      fileset = fileset.difference trackedFiles (fileset.unions potentiallyAlreadyRemovedFiles);
    };
  v8Dir = "${src}/deps/v8";
in
stdenv.mkDerivation (finalAttrs: {
  pname = "v8";
  version =
    let
      v8Version = builtins.match (
        ".*#define V8_MAJOR_VERSION ([0-9]+).*"
        + "#define V8_MINOR_VERSION ([0-9]+).*"
        + "#define V8_BUILD_NUMBER ([0-9]+).*"
        + "#define V8_PATCH_LEVEL ([0-9]+).*"
      ) (builtins.readFile "${v8Dir}/include/v8-version.h");
      v8_embedder_string = builtins.match ".*'v8_embedder_string': '-(node.[0-9]+)'.*" (
        builtins.readFile "${src}/common.gypi"
      );
    in
    if v8Version == null || v8_embedder_string == null then
      throw "V8 version not found"
    else
      "${builtins.elemAt v8Version 0}.${builtins.elemAt v8Version 1}.${builtins.elemAt v8Version 2}.${builtins.elemAt v8Version 3}-${builtins.elemAt v8_embedder_string 0}";

  # We need to patch tools/gyp/ to work from within Nix sandbox
  prePatch = ''
    ${lib.optionalString (builtins.length (finalAttrs.patches or [ ]) == 0) "patches=()"}
    for patch in ${lib.concatStringsSep " " patches}; do
      filtered=$(mktemp)
      filterdiff -p1 -i 'tools/gyp/pylib/*' "$patch" > "$filtered"
      if [ -s "$filtered" ]; then
        patches+=("$filtered")
      fi
    done
  ''
  # We also need to mock some Python util files that are not used to configure Python.
  # Mocking lets us avoid rebuilding the whole derivation if there's a unrelated
  # change in one of those files.
  + ''
    mkdir -p tools/configure.d
    cat -> tools/configure.d/nodedownload.py <<'EOF'
    def parse(opt):
      return {}
    def help():
      return ""
    EOF
    cat -> tools/getmoduleversion.py <<'EOF'
    def get_version():
      return "99"
    EOF
    cat -> tools/getnapibuildversion.py <<'EOF'
    def get_napi_version():
      return "9"
    EOF
    cat -> tools/utils.py <<'EOF'
    def SearchFiles(dir, ext):
      return []
    EOF
  '';
  # To avoid invalidating cached build on unrelated `node.gyp` and `node.gypi` changes,
  # we stub `node.gyp` to only reference the targets needed to build V8.
  postPatch = ''
    cat -> node.gyp <<'EOF'
    ${builtins.toJSON {
      targets = [
        {
          target_name = "v8_deps_root";
          type = "none";
          dependencies = lib.unique (
            builtins.concatLists (
              builtins.filter builtins.isList (
                builtins.split "'(tools/v8_gypfiles/v8\\.gyp:[A-Za-z0-9_]+)'" (builtins.readFile ../../node.gypi)
              )
            )
          );
        }
      ];
    }}
    EOF
  ''
  + lib.optionalString (icu == null) ''
    substituteInPlace configure.py \
      --replace-fail \
        "icu_versions = json.loads((tools_path / 'icu' / 'icu_versions.json').read_text(encoding='utf-8'))" \
        "icu_versions = { 'minimum_icu': 1 }"
  '';

  inherit
    src
    configureScript
    configureFlags
    buildInputs
    ;

  nativeBuildInputs = nativeBuildInputs ++ [
    patchutils
    validatePkgConfig
  ];

  buildPhase = ''
    runHook preBuild
    ninja -v -C out/Release v8_deps_root
    runHook postBuild
  '';
  installPhase = ''
    runHook preInstall

    ${
      if stdenv.hostPlatform.isDarwin then
        # Darwin is excluded from creating thin archive in tools/gyp/pylib/gyp/generator/ninja.py:2488
        "install -Dm644 out/Release/lib* -t $out/lib"
      else
        # On other platforms, we need to create non-thin archive.
        ''
          mkdir -p $out/lib
          for a in out/Release/obj/tools/v8_gypfiles/lib*; do
            base=$(basename "$a")
            dir=$(dirname "$a")

            (
              cd "$dir"
              "$AR" rc "$out/lib/$base" $("$AR" t "$base")
            )

            "$RANLIB" "$out/lib/$base"
          done
        ''
    }

    ${lib.optionalString (
      !useSharedSimdutf
    ) "install -Dm644 deps/v8/third_party/simdutf/simdutf.h -t $out/include"}
    find deps/v8/include -name '*.h' -print0 | while read -r -d "" file; do
      install -Dm644 "$file" -T "$out/include/''${file#deps/v8/include/}"
    done
    ${lib.optionalString (!useSharedAbseil) ''
      find deps/v8/third_party/abseil-cpp/absl -name '*.h' -print0 | while read -r -d "" file; do
        install -Dm644 "$file" -T "$out/include/''${file#deps/v8/third_party/abseil-cpp/}"
      done
    ''}

    mkdir -p $out/lib/pkgconfig
    cat -> $out/lib/pkgconfig/v8.pc << EOF
    prefix=$out
    exec_prefix=\''${prefix}
    libdir=\''${exec_prefix}/lib
    includedir=\''${prefix}/include

    Name: v8
    Description: V8 JavaScript Engine build for Node.js CI
    Version: ${finalAttrs.version}
    Libs: -L\''${libdir} $(for f in $out/lib/lib*.a; do
      b=$(basename "$f" .a)
      printf " -l%s" "''${b#lib}"
    done) -lstdc++
    Cflags: -I\''${includedir}
    EOF

    runHook postInstall
  '';
})