[buildout]
extends =
# Build dependencies:
  ../coreutils/buildout.cfg
  ../curl/buildout.cfg
  ../depot_tools/buildout.cfg
  ../git/buildout.cfg
  ../gperf/buildout.cfg
  ../pkgconfig/buildout.cfg
# Runtime dependencies:
  ../nspr/buildout.cfg
  ../nss/buildout.cfg

parts =
  headless-chromium-wrapper

# The Chromium project recommends using their own `fetch' tool rather
# than doing a `git clone', but this is a little more flexible and works
# fine.
#
# Setting depth=1000 is a middle ground between cloning no history
# (causes some scripts to break) and cloning the full history (takes a
# really long time).
#
# Note: we should add a `depth' option to slapos.recipe.build:gitclone
# and then migrate this part to use that recipe at some point.
[chromium-source]
recipe = plone.recipe.command
command =
  export PATH=$PATH:${git:location}/bin
  git clone ${:repository} ${:location} \
    --branch ${:version} \
    --depth 1000
repository = https://chromium.googlesource.com/chromium/src.git
stop-on-error = true

# We should place the .gclient file in the parent directory of the
# checkout/repository itself.
location = ${:gclient-location}/${:name}
gclient-location = ${buildout:parts-directory}/${:_buildout_section_name_}

# Theoretically the checkout can be put anywhere as long as you specify
# "name" appropriately in the .gclient file, but in practice it seems
# that some automated tools break. It's safest to put it in a directory
# called "src".
name = src


# 114.0.5735.340 version is the latest stable version in November 2023.
version = 114.0.5735.340


[headless-chromium]
recipe = slapos.recipe.cmmi
path = ${chromium-source:location}
# XXX
fake-location = ${:path}/out/headless

# Configuration file for GN, the tool to build the actual compilation
# configuration file.
default-gclient-config =
  solutions = [
    {
      "name": "${chromium-source:name}",
      "url": "${chromium-source:repository}",
      "managed": False,
      "custom_deps": {},
      "custom_vars": {},
    },
  ]

# Configuration for a headless build.
build-config-options =
  import("//build/args/headless.gn")
  is_debug = false
  symbol_level = 0
  blink_symbol_level = 0

  # We need to unbundle the build toolchain in order to set our own
  # LDFLAGS.
  custom_toolchain = "//build/toolchain/linux/unbundle:default"
  host_toolchain = "//build/toolchain/linux/unbundle:default"
  current_os = "linux"
  current_cpu = "x64"

# Chromium bundles its own LLVM toolchain, so we might as well use it.
llvm-toolchain = ${:path}/third_party/llvm-build/Release+Asserts/bin

configure-command =
# Sync build dependencies---this is a little finnicky.
  echo '${:default-gclient-config}' \
    > ${chromium-source:gclient-location}/.gclient
  gclient sync --no-history
# Generate build configuration files.
  mkdir -p ${:fake-location}
  echo '${:build-config-options}' > ${:fake-location}/args.gn
  gn gen ${:fake-location}

# You can run the headless Chromium shell using
# ${:binary} --remote-debugging-port=1234
make-binary =
  autoninja -C ${:fake-location} headless_shell
# By building our own version of Chromedriver, we can ensure version
# compatibility. The build is quite cheap compared to Chromium, anyway.
  autoninja -C ${:fake-location} chromedriver
environment =
  PATH=${depot_tools:location}:${gperf:location}/bin:${pkgconfig:location}/bin:${coreutils:location}/bin:${git:location}/bin:${curl:location}/bin:%(PATH)s
  LDFLAGS="-Wl,-rpath=${nss:location}/lib,-rpath=${nspr:location}/lib"
  CC="${:llvm-toolchain}/clang"
  CXX="${:llvm-toolchain}/clang++"
  AR="${:llvm-toolchain}/llvm-ar"
  NM="${:llvm-toolchain}/llvm-nm"
  DEPOT_TOOLS_UPDATE=0

# Fix permission for group, slapuserXX should be able to access subfolders
# in headless-chromium location.
post-install =
  find ${:fake-location} -type d -exec chmod g+xr {} \;

# Expose devtools frontend location.
devtools-frontend = ${:fake-location}/gen/third_party/devtools-frontend/src/front_end

binary = ${:fake-location}/headless_shell
chromedriver = ${:fake-location}/chromedriver
promises =
  ${:binary}
  ${:chromedriver}


# At runtime, Chromium tries to dynamically load the NSS certificate
# database from "libnssckbi.so". But Chromium does this through NSPR,
# which doesn't know where SlapOS installed NSS. Since we don't want to
# modify the NSPR component from the Chromium component, we just set
# LD_LIBRARY_PATH in a wrapper script so that NSPR knows where NSS is.
#
# Alternatively, we could patch crypto/nss_util.cc in the Chromium
# source code to use an absolute path for libnssckbi.so, but this is not
# as future-proof against new versions of Chromium.
[headless-chromium-wrapper]
recipe = slapos.recipe.template:jinja2
inline =
  #!/bin/sh
  export LD_LIBRARY_PATH="{{ nss_location }}/lib:$LD_LIBRARY_PATH"
  exec {{ chromium_binary }} "$@"
output = ${buildout:bin-directory}/headless-chromium
context =
  key nss_location nss:location
  key chromium_binary headless-chromium:binary
