summary refs log tree commit diff
path: root/pkgs/applications/networking/browsers/chromium/common.nix
blob: ff0c83527aabfa88ae142e6c867868d395d675bd (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
{ stdenv, lib, fetchurl, fetchpatch
, fetchzip, zstd
, buildPackages
, pkgsBuildBuild
, pkgsBuildTarget
# Channel data:
, channel, upstream-info
# Helper functions:
, chromiumVersionAtLeast, versionRange

# Native build inputs:
, ninja, pkg-config
, python3, perl
, which
, llvmPackages_attrName
, libuuid
, overrideCC
# postPatch:
, pkgsBuildHost
# configurePhase:
, gnChromium

# Build inputs:
, libpng
, bzip2, flac, speex, libopus
, libevent, expat, libjpeg, snappy
, libcap
, xdg-utils, minizip, libwebp
, libusb1, re2
, ffmpeg, libxslt, libxml2
, nasm
, nspr, nss
, util-linux, alsa-lib
, bison, gperf, libkrb5
, glib, gtk3, dbus-glib
, libXScrnSaver, libXcursor, libXtst, libxshmfence, libGLU, libGL
, mesa
, pciutils, protobuf, speechd, libXdamage, at-spi2-core
, pipewire
, libva
, libdrm, wayland, libxkbcommon # Ozone
, curl
, libffi
, libepoxy
, libevdev
# postPatch:
, glibc # gconv + locale
# postFixup:
, vulkan-loader

# Package customization:
, cupsSupport ? true, cups ? null
, proprietaryCodecs ? true
, pulseSupport ? false, libpulseaudio ? null
, ungoogled ? false, ungoogled-chromium
# Optional dependencies:
, libgcrypt ? null # cupsSupport
, systemdSupport ? lib.meta.availableOn stdenv.hostPlatform systemd
, systemd
}:

buildFun:

let
  python3WithPackages = python3.pythonOnBuildForHost.withPackages(ps: with ps; [
    ply jinja2 setuptools
  ]);
  clangFormatPython3 = fetchurl {
    url = "https://chromium.googlesource.com/chromium/tools/build/+/e77882e0dde52c2ccf33c5570929b75b4a2a2522/recipes/recipe_modules/chromium/resources/clang-format?format=TEXT";
    hash = "sha256-1BRxXP+0QgejAWdFHJzGrLMhk/MsRDoVdK/GVoyFg0U=";
  };

  # The additional attributes for creating derivations based on the chromium
  # source tree.
  extraAttrs = buildFun base;

  githubPatch = { commit, hash, revert ? false }: fetchpatch {
    url = "https://github.com/chromium/chromium/commit/${commit}.patch";
    inherit hash revert;
  };

  mkGnFlags =
    let
      # Serialize Nix types into GN types according to this document:
      # https://source.chromium.org/gn/gn/+/master:docs/language.md
      mkGnString = value: "\"${lib.escape ["\"" "$" "\\"] value}\"";
      sanitize = value:
        if value == true then "true"
        else if value == false then "false"
        else if lib.isList value then "[${lib.concatMapStringsSep ", " sanitize value}]"
        else if lib.isInt value then toString value
        else if lib.isString value then mkGnString value
        else throw "Unsupported type for GN value `${value}'.";
      toFlag = key: value: "${key}=${sanitize value}";
    in attrs: lib.concatStringsSep " " (lib.attrValues (lib.mapAttrs toFlag attrs));

  # https://source.chromium.org/chromium/chromium/src/+/master:build/linux/unbundle/replace_gn_files.py
  gnSystemLibraries = [
    # TODO:
    # "ffmpeg"
    # "snappy"
    "flac"
    "libjpeg"
    "libpng"
    "libwebp"
    "libxslt"
    # "opus"
  ];

  opusWithCustomModes = libopus.override {
    withCustomModes = true;
  };

  # build paths and release info
  packageName = extraAttrs.packageName or extraAttrs.name;
  buildType = "Release";
  buildPath = "out/${buildType}";
  libExecPath = "$out/libexec/${packageName}";

  ungoogler = ungoogled-chromium {
    inherit (upstream-info.deps.ungoogled-patches) rev hash;
  };

  # There currently isn't a (much) more concise way to get a stdenv
  # that uses lld as its linker without bootstrapping pkgsLLVM; see
  # https://github.com/NixOS/nixpkgs/issues/142901
  buildPlatformLlvmStdenv =
    let
      llvmPackages = pkgsBuildBuild.${llvmPackages_attrName};
    in
      overrideCC llvmPackages.stdenv
        (llvmPackages.stdenv.cc.override {
          inherit (llvmPackages) bintools;
        });

  chromiumRosettaStone = {
    cpu = platform:
      let name = platform.parsed.cpu.name;
      in ({ "x86_64" = "x64";
            "i686" = "x86";
            "arm" = "arm";
            "aarch64" = "arm64";
          }.${platform.parsed.cpu.name}
        or (throw "no chromium Rosetta Stone entry for cpu: ${name}"));
    os = platform:
      if platform.isLinux
      then "linux"
      else throw "no chromium Rosetta Stone entry for os: ${platform.config}";
  };

  recompressTarball = { version, hash ? "" }: fetchzip {
    name = "chromium-${version}.tar.zstd";
    url = "https://commondatastorage.googleapis.com/chromium-browser-official/chromium-${version}.tar.xz";
    inherit hash;

    nativeBuildInputs = [ zstd ];

    postFetch = ''
      echo removing unused code from tarball to stay under hydra limit
      rm -r $out/third_party/{rust-src,llvm}

      echo moving remains out of \$out
      mv $out source

      echo recompressing final contents into new tarball
      # try to make a deterministic tarball
      tar \
        --use-compress-program "zstd -T$NIX_BUILD_CORES" \
        --sort name \
        --mtime 1970-01-01 \
        --owner=root --group=root \
        --numeric-owner --mode=go=rX,u+rw,a-s \
        -cf $out source
    '';
  };


  base = rec {
    pname = "${packageName}-unwrapped";
    inherit (upstream-info) version;
    inherit packageName buildType buildPath;

    src = recompressTarball { inherit version; inherit (upstream-info) hash; };

    nativeBuildInputs = [
      ninja pkg-config
      python3WithPackages perl
      which
      buildPackages.${llvmPackages_attrName}.bintools
      bison gperf
    ];

    depsBuildBuild = [
      buildPlatformLlvmStdenv
      buildPlatformLlvmStdenv.cc
      pkg-config
      libuuid
      libpng # needed for "host/generate_colors_info"
    ]
    # When cross-compiling, chromium builds a huge proportion of its
    # components for both the `buildPlatform` (which it calls
    # `host`) as well as for the `hostPlatform` -- easily more than
    # half of the dependencies are needed here.  To avoid having to
    # maintain a separate list of buildPlatform-dependencies, we
    # simply throw in the kitchen sink.
    ++ buildInputs
    ;

    buildInputs = [
      (libpng.override { apngSupport = false; }) # https://bugs.chromium.org/p/chromium/issues/detail?id=752403
      bzip2 flac speex opusWithCustomModes
      libevent expat libjpeg snappy
      libcap
    ] ++ lib.optionals (!xdg-utils.meta.broken) [
      xdg-utils
    ] ++ [
      minizip libwebp
      libusb1 re2
      ffmpeg libxslt libxml2
      nasm
      nspr nss
      util-linux alsa-lib
      libkrb5
      glib gtk3 dbus-glib
      libXScrnSaver libXcursor libXtst libxshmfence libGLU libGL
      mesa # required for libgbm
      pciutils protobuf speechd libXdamage at-spi2-core
      pipewire
      libva
      libdrm wayland mesa.drivers libxkbcommon
      curl
      libepoxy
      libffi
      libevdev
    ] ++ lib.optional systemdSupport systemd
      ++ lib.optionals cupsSupport [ libgcrypt cups ]
      ++ lib.optional pulseSupport libpulseaudio;

    patches = [
      ./cross-compile.patch
      # Optional patch to use SOURCE_DATE_EPOCH in compute_build_timestamp.py (should be upstreamed):
      ./patches/no-build-timestamps.patch
      # For bundling Widevine (DRM), might be replaceable via bundle_widevine_cdm=true in gnFlags:
      ./patches/widevine-79.patch
      # Required to fix the build with a more recent wayland-protocols version
      # (we currently package 1.26 in Nixpkgs while Chromium bundles 1.21):
      # Source: https://bugs.chromium.org/p/angleproject/issues/detail?id=7582#c1
      ./patches/angle-wayland-include-protocol.patch
      # We need to revert this patch to build M114+ with LLVM 16:
      (githubPatch {
        # Reland [clang] Disable autoupgrading debug info in ThinLTO builds
        commit = "54969766fd2029c506befc46e9ce14d67c7ed02a";
        hash = "sha256-Vryjg8kyn3cxWg3PmSwYRG6zrHOqYWBMSdEMGiaPg6M=";
        revert = true;
      })
    ];

    postPatch = ''
      # Workaround/fix for https://bugs.chromium.org/p/chromium/issues/detail?id=1313361:
      substituteInPlace BUILD.gn \
        --replace '"//infra/orchestrator:orchestrator_all",' ""
      # Disable build flags that require LLVM 15:
      substituteInPlace build/config/compiler/BUILD.gn \
        --replace '"-Xclang",' "" \
        --replace '"-no-opaque-pointers",' ""
      # remove unused third-party
      for lib in ${toString gnSystemLibraries}; do
        if [ -d "third_party/$lib" ]; then
          find "third_party/$lib" -type f \
            \! -path "third_party/$lib/chromium/*" \
            \! -path "third_party/$lib/google/*" \
            \! -path "third_party/harfbuzz-ng/utils/hb_scoped.h" \
            \! -regex '.*\.\(gn\|gni\|isolate\)' \
            -delete
        fi
      done

      if [[ -e native_client/SConstruct ]]; then
        # Required for patchShebangs (unsupported interpreter directive, basename: invalid option -- '*', etc.):
        substituteInPlace native_client/SConstruct --replace "#! -*- python -*-" ""
      fi
      if [ -e third_party/harfbuzz-ng/src/src/update-unicode-tables.make ]; then
        substituteInPlace third_party/harfbuzz-ng/src/src/update-unicode-tables.make \
          --replace "/usr/bin/env -S make -f" "/usr/bin/make -f"
      fi
      if [ -e third_party/webgpu-cts/src/tools/run_deno ]; then
        chmod -x third_party/webgpu-cts/src/tools/run_deno
      fi
      if [ -e third_party/dawn/third_party/webgpu-cts/tools/run_deno ]; then
        chmod -x third_party/dawn/third_party/webgpu-cts/tools/run_deno
      fi

      # We want to be able to specify where the sandbox is via CHROME_DEVEL_SANDBOX
      substituteInPlace sandbox/linux/suid/client/setuid_sandbox_host.cc \
        --replace \
          'return sandbox_binary;' \
          'return base::FilePath(GetDevelSandboxPath());'

      substituteInPlace services/audio/audio_sandbox_hook_linux.cc \
        --replace \
          '/usr/share/alsa/' \
          '${alsa-lib}/share/alsa/' \
        --replace \
          '/usr/lib/x86_64-linux-gnu/gconv/' \
          '${glibc}/lib/gconv/' \
        --replace \
          '/usr/share/locale/' \
          '${glibc}/share/locale/'

    '' + lib.optionalString (!xdg-utils.meta.broken) ''
      sed -i -e 's@"\(#!\)\?.*xdg-@"\1${xdg-utils}/bin/xdg-@' \
        chrome/browser/shell_integration_linux.cc

    '' + lib.optionalString systemdSupport ''
      sed -i -e '/lib_loader.*Load/s!"\(libudev\.so\)!"${lib.getLib systemd}/lib/\1!' \
        device/udev_linux/udev?_loader.cc
    '' + ''
      # Allow to put extensions into the system-path.
      sed -i -e 's,/usr,/run/current-system/sw,' chrome/common/chrome_paths.cc

      # We need the fix for https://bugs.chromium.org/p/chromium/issues/detail?id=1254408:
      base64 --decode ${clangFormatPython3} > buildtools/linux64/clang-format

      # Add final newlines to scripts that do not end with one.
      # This is a temporary workaround until https://github.com/NixOS/nixpkgs/pull/255463 (or similar) has been merged,
      # as patchShebangs hard-crashes when it encounters files that contain only a shebang and do not end with a final
      # newline.
      find . -type f -perm -0100 -exec sed -i -e '$a\' {} +

      patchShebangs .
      # Link to our own Node.js and Java (required during the build):
      mkdir -p third_party/node/linux/node-linux-x64/bin
      ln -s "${pkgsBuildHost.nodejs}/bin/node" third_party/node/linux/node-linux-x64/bin/node
      ln -s "${pkgsBuildHost.jdk17_headless}/bin/java" third_party/jdk/current/bin/

      # Allow building against system libraries in official builds
      sed -i 's/OFFICIAL_BUILD/GOOGLE_CHROME_BUILD/' tools/generate_shim_headers/generate_shim_headers.py

    '' + lib.optionalString (stdenv.hostPlatform == stdenv.buildPlatform && stdenv.hostPlatform.isAarch64) ''
      substituteInPlace build/toolchain/linux/BUILD.gn \
        --replace 'toolprefix = "aarch64-linux-gnu-"' 'toolprefix = ""'
    '' + lib.optionalString ungoogled ''
      ${ungoogler}/utils/prune_binaries.py . ${ungoogler}/pruning.list || echo "some errors"
      ${ungoogler}/utils/patches.py . ${ungoogler}/patches
      ${ungoogler}/utils/domain_substitution.py apply -r ${ungoogler}/domain_regex.list -f ${ungoogler}/domain_substitution.list -c ./ungoogled-domsubcache.tar.gz .
    '';

    gnFlags = mkGnFlags ({
      # Main build and toolchain settings:
      # Create an official and optimized release build (only official builds
      # should be distributed to users, as non-official builds are intended for
      # development and may not be configured appropriately for production,
      # e.g. unsafe developer builds have developer-friendly features that may
      # weaken or disable security measures like sandboxing or ASLR):
      is_official_build = true;
      disable_fieldtrial_testing_config = true;

      # note: chromium calls buildPlatform "host" and calls hostPlatform "target"
      host_cpu      = chromiumRosettaStone.cpu stdenv.buildPlatform;
      host_os       = chromiumRosettaStone.os  stdenv.buildPlatform;
      target_cpu    = chromiumRosettaStone.cpu stdenv.hostPlatform;
      v8_target_cpu = chromiumRosettaStone.cpu stdenv.hostPlatform;
      target_os     = chromiumRosettaStone.os  stdenv.hostPlatform;

      # Build Chromium using the system toolchain (for Linux distributions):
      #
      # What you would expect to be caled "target_toolchain" is
      # actually called either "default_toolchain" or "custom_toolchain",
      # depending on which part of the codebase you are in; see:
      # https://github.com/chromium/chromium/blob/d36462cc9279464395aea5e65d0893d76444a296/build/config/BUILDCONFIG.gn#L17-L44
      custom_toolchain = "//build/toolchain/linux/unbundle:default";
      host_toolchain = "//build/toolchain/linux/unbundle:host";
      v8_snapshot_toolchain = "//build/toolchain/linux/unbundle:host";

      host_pkg_config = "${pkgsBuildBuild.pkg-config}/bin/pkg-config";
      pkg_config      = "${pkgsBuildHost.pkg-config}/bin/${stdenv.cc.targetPrefix}pkg-config";

      # Don't build against a sysroot image downloaded from Cloud Storage:
      use_sysroot = false;
      # Because we use a different toolchain / compiler version:
      treat_warnings_as_errors = false;
      # We aren't compiling with Chrome's Clang (would enable Chrome-specific
      # plugins for enforcing coding guidelines, etc.):
      clang_use_chrome_plugins = false;
      # Disable symbols (they would negatively affect the performance of the
      # build since the symbols are large and dealing with them is slow):
      symbol_level = 0;
      blink_symbol_level = 0;

      # Google API key, see: https://www.chromium.org/developers/how-tos/api-keys
      # Note: The API key is for NixOS/nixpkgs use ONLY.
      # For your own distribution, please get your own set of keys.
      google_api_key = "AIzaSyDGi15Zwl11UNe6Y-5XW_upsfyw31qwZPI";

      # Optional features:
      use_gio = true;
      use_cups = cupsSupport;

      # Feature overrides:
      # Native Client support was deprecated in 2020 and support will end in June 2021:
      enable_nacl = false;
      # Enabling the Widevine component here doesn't affect whether we can
      # redistribute the chromium package; the Widevine component is either
      # added later in the wrapped -wv build or downloaded from Google:
      enable_widevine = true;
      # Provides the enable-webrtc-pipewire-capturer flag to support Wayland screen capture:
      rtc_use_pipewire = true;
      # Disable PGO because the profile data requires a newer compiler version (LLVM 14 isn't sufficient):
      chrome_pgo_phase = 0;
      clang_base_path = "${pkgsBuildTarget.${llvmPackages_attrName}.stdenv.cc}";
      use_qt = false;
      # To fix the build as we don't provide libffi_pic.a
      # (ld.lld: error: unable to find library -l:libffi_pic.a):
      use_system_libffi = true;
      # Use nixpkgs Rust compiler instead of the one shipped by Chromium.
      # We do intentionally not set rustc_version as nixpkgs will never do incremental
      # rebuilds, thus leaving this empty is fine.
      rust_sysroot_absolute = "${buildPackages.rustc}";
      # Building with rust is disabled for now - this matches the flags in other major distributions.
      enable_rust = false;
    } // lib.optionalAttrs (!(stdenv.buildPlatform.canExecute stdenv.hostPlatform)) {
      # https://www.mail-archive.com/v8-users@googlegroups.com/msg14528.html
      arm_control_flow_integrity = "none";
    } // lib.optionalAttrs proprietaryCodecs {
      # enable support for the H.264 codec
      proprietary_codecs = true;
      enable_hangout_services_extension = true;
      ffmpeg_branding = "Chrome";
    } // lib.optionalAttrs pulseSupport {
      use_pulseaudio = true;
      link_pulseaudio = true;
    } // lib.optionalAttrs ungoogled (lib.importTOML ./ungoogled-flags.toml)
    // (extraAttrs.gnFlags or {}));

    configurePhase = ''
      runHook preConfigure

      # This is to ensure expansion of $out.
      libExecPath="${libExecPath}"
      ${python3.pythonOnBuildForHost}/bin/python3 build/linux/unbundle/replace_gn_files.py --system-libraries ${toString gnSystemLibraries}
      ${gnChromium}/bin/gn gen --args=${lib.escapeShellArg gnFlags} out/Release | tee gn-gen-outputs.txt

      # Fail if `gn gen` contains a WARNING.
      grep -o WARNING gn-gen-outputs.txt && echo "Found gn WARNING, exiting nix build" && exit 1

      runHook postConfigure
    '';

    # Don't spam warnings about unknown warning options. This is useful because
    # our Clang is always older than Chromium's and the build logs have a size
    # of approx. 25 MB without this option (and this saves e.g. 66 %).
    env.NIX_CFLAGS_COMPILE = "-Wno-unknown-warning-option";
    env.BUILD_CC = "$CC_FOR_BUILD";
    env.BUILD_CXX = "$CXX_FOR_BUILD";
    env.BUILD_AR = "$AR_FOR_BUILD";
    env.BUILD_NM = "$NM_FOR_BUILD";
    env.BUILD_READELF = "$READELF_FOR_BUILD";

    buildPhase = let
      buildCommand = target: ''
        TERM=dumb ninja -C "${buildPath}" -j$NIX_BUILD_CORES "${target}"
        (
          source chrome/installer/linux/common/installer.include
          PACKAGE=$packageName
          MENUNAME="Chromium"
          process_template chrome/app/resources/manpage.1.in "${buildPath}/chrome.1"
        )
      '';
      targets = extraAttrs.buildTargets or [];
      commands = map buildCommand targets;
    in ''
      runHook preBuild
      ${lib.concatStringsSep "\n" commands}
      runHook postBuild
    '';

    postFixup = ''
      # Make sure that libGLESv2 and libvulkan are found by dlopen.
      # libpci (from pciutils) is needed by dlopen in angle/src/gpu_info_util/SystemInfo_libpci.cpp
      chromiumBinary="$libExecPath/$packageName"
      origRpath="$(patchelf --print-rpath "$chromiumBinary")"
      patchelf --set-rpath "${lib.makeLibraryPath [ libGL vulkan-loader pciutils ]}:$origRpath" "$chromiumBinary"
    '';

    passthru = {
      updateScript = ./update.py;
      chromiumDeps = {
        gn = gnChromium;
      };
      inherit recompressTarball;
    };
  }
  # overwrite `version` with the exact same `version` from the same source,
  # except it internally points to `upstream-info.nix` for
  # `builtins.unsafeGetAttrPos`, which is used by ofborg to decide
  # which maintainers need to be pinged.
  // builtins.removeAttrs upstream-info (builtins.filter (e: e != "version") (builtins.attrNames upstream-info));

# Remove some extraAttrs we supplied to the base attributes already.
in stdenv.mkDerivation (base // removeAttrs extraAttrs [
  "name" "gnFlags" "buildTargets"
] // { passthru = base.passthru // (extraAttrs.passthru or {}); })