222 lines
7.6 KiB
Nix
222 lines
7.6 KiB
Nix
{
|
|
pkgs,
|
|
lib,
|
|
kernel-src,
|
|
buildLinux,
|
|
structuredExtraConfig ? { },
|
|
kernelPatches ? [ ],
|
|
extraConfig ? "",
|
|
sccacheDir ? "/var/cache/sccache/nix-builds/kernel",
|
|
sccacheServerUds ? null,
|
|
enforceSccache ? true,
|
|
features ? { },
|
|
randstructSeed ? null,
|
|
...
|
|
}:
|
|
|
|
let
|
|
makefile = builtins.readFile "${kernel-src}/Makefile";
|
|
lines = lib.splitString "\n" makefile;
|
|
normalizeKernelPatch = p: p // { patch = p.patch or null; };
|
|
kernelPatches' = map normalizeKernelPatch kernelPatches;
|
|
|
|
get =
|
|
name:
|
|
let
|
|
line = lib.findFirst (
|
|
l: lib.hasPrefix "${name} =" (lib.strings.trim l)
|
|
) (throw "Makefile missing ${name}") lines;
|
|
in
|
|
lib.strings.trim (lib.removePrefix "${name} =" (lib.strings.trim line));
|
|
|
|
V = get "VERSION";
|
|
P = get "PATCHLEVEL";
|
|
S = get "SUBLEVEL";
|
|
E = get "EXTRAVERSION";
|
|
|
|
kver = "${V}.${P}.${S}${E}";
|
|
in
|
|
let
|
|
args = {
|
|
inherit
|
|
buildLinux
|
|
features
|
|
randstructSeed
|
|
;
|
|
};
|
|
llvm = pkgs.llvmPackages_latest;
|
|
rust = pkgs.rustc-unwrapped;
|
|
|
|
buildPkgs = args.buildPackages or pkgs.buildPackages;
|
|
buildLlvm = buildPkgs.llvmPackages_latest;
|
|
llvmBuildPackages = buildPkgs // {
|
|
stdenv = buildLlvm.stdenv;
|
|
};
|
|
|
|
realClang = lib.getExe llvm.clang-unwrapped;
|
|
realLd = lib.getExe' llvm.lld "ld.lld";
|
|
realAr = lib.getExe' llvm.llvm "llvm-ar";
|
|
realNm = lib.getExe' llvm.llvm "llvm-nm";
|
|
realStrip = lib.getExe' llvm.llvm "llvm-strip";
|
|
realObjcopy = lib.getExe' llvm.llvm "llvm-objcopy";
|
|
realObjdump = lib.getExe' llvm.llvm "llvm-objdump";
|
|
realReadelf = lib.getExe' llvm.llvm "llvm-readelf";
|
|
|
|
realHostCC = lib.getExe' buildLlvm.stdenv.cc "${buildLlvm.stdenv.cc.targetPrefix}cc";
|
|
realHostCXX = lib.getExe' buildLlvm.stdenv.cc "${buildLlvm.stdenv.cc.targetPrefix}c++";
|
|
realHostAr = lib.getExe' buildLlvm.llvm "llvm-ar";
|
|
realHostLd = lib.getExe' buildLlvm.lld "ld.lld";
|
|
realRustc = lib.getExe' rust "rustc";
|
|
realHostRustc = realRustc;
|
|
sccacheConfig = pkgs.writeText "kernel-sccache.conf" ''
|
|
[cache.disk]
|
|
dir = "${sccacheDir}"
|
|
size = "100G"
|
|
'';
|
|
mkSccacheWrapper =
|
|
name: compiler:
|
|
pkgs.runCommand "${name}-sccache-wrapper"
|
|
{
|
|
passthru.meta.mainProgram = name;
|
|
}
|
|
''
|
|
mkdir -p $out/bin
|
|
cat > $out/bin/${name} <<'WRAP'
|
|
#!${pkgs.bash}/bin/bash
|
|
exec ${pkgs.sccache}/bin/sccache ${compiler} "$@"
|
|
WRAP
|
|
chmod +x $out/bin/${name}
|
|
'';
|
|
sccacheSetup = lib.optionalString enforceSccache ''
|
|
mkdir -p ${lib.escapeShellArg sccacheDir} 2>/dev/null || true
|
|
mkdir -p "$NIX_BUILD_TOP/.sccache/preprocessor" 2>/dev/null || true
|
|
export SCCACHE_CONF=${lib.escapeShellArg sccacheConfig}
|
|
export SCCACHE_LOCAL_PREPROCESSOR_CACHE_DIR="$NIX_BUILD_TOP/.sccache/preprocessor"
|
|
export SCCACHE_NO_DAEMON=1
|
|
'';
|
|
|
|
clangSccache = mkSccacheWrapper "clang" realClang;
|
|
hostccSccache = mkSccacheWrapper "cc" realHostCC;
|
|
hostcxxSccache = mkSccacheWrapper "c++" realHostCXX;
|
|
rustcSccache = pkgs.writeShellScriptBin "rustc" ''
|
|
set -euo pipefail
|
|
mkdir -p ${lib.escapeShellArg sccacheDir} 2>/dev/null || true
|
|
mkdir -p "$NIX_BUILD_TOP/.sccache/preprocessor" 2>/dev/null || true
|
|
export SCCACHE_CONF=${lib.escapeShellArg sccacheConfig}
|
|
export SCCACHE_LOCAL_PREPROCESSOR_CACHE_DIR="$NIX_BUILD_TOP/.sccache/preprocessor"
|
|
export SCCACHE_NO_DAEMON=1
|
|
${lib.optionalString (
|
|
sccacheServerUds != null
|
|
) "export SCCACHE_SERVER_UDS=${lib.escapeShellArg sccacheServerUds}"}
|
|
if [ -n "''${SCCACHE_ENFORCE_MARKER-}" ]; then
|
|
: > "''${SCCACHE_ENFORCE_MARKER}"
|
|
fi
|
|
exec ${pkgs.sccache}/bin/sccache ${realRustc} "$@"
|
|
'';
|
|
|
|
hostrustcSccache = pkgs.writeShellScriptBin "rustc" ''
|
|
set -euo pipefail
|
|
mkdir -p ${lib.escapeShellArg sccacheDir} 2>/dev/null || true
|
|
mkdir -p "$NIX_BUILD_TOP/.sccache/preprocessor" 2>/dev/null || true
|
|
export SCCACHE_CONF=${lib.escapeShellArg sccacheConfig}
|
|
export SCCACHE_LOCAL_PREPROCESSOR_CACHE_DIR="$NIX_BUILD_TOP/.sccache/preprocessor"
|
|
export SCCACHE_NO_DAEMON=1
|
|
${lib.optionalString (
|
|
sccacheServerUds != null
|
|
) "export SCCACHE_SERVER_UDS=${lib.escapeShellArg sccacheServerUds}"}
|
|
'';
|
|
|
|
structuredExtraConfig' =
|
|
(with lib.kernel; {
|
|
LTO_CLANG_THIN = yes;
|
|
# LTO_CLANG_FULL = no;
|
|
# LTO_NONE = no;
|
|
})
|
|
// structuredExtraConfig;
|
|
in
|
|
(buildLinux (
|
|
args
|
|
// {
|
|
kernelPatches = kernelPatches';
|
|
inherit extraConfig;
|
|
|
|
version = kver;
|
|
modDirVersion = kver;
|
|
src = kernel-src;
|
|
|
|
stdenv = llvm.stdenv;
|
|
buildPackages = llvmBuildPackages;
|
|
|
|
extraMakeFlags = (args.extraMakeFlags or [ ]) ++ [
|
|
"LLVM=1"
|
|
"LLVM_IAS=1"
|
|
"CC=${lib.getExe clangSccache}"
|
|
"LD=${realLd}"
|
|
"AR=${realAr}"
|
|
"NM=${realNm}"
|
|
"STRIP=${realStrip}"
|
|
"OBJCOPY=${realObjcopy}"
|
|
"OBJDUMP=${realObjdump}"
|
|
"READELF=${realReadelf}"
|
|
"HOSTCC=${lib.getExe hostccSccache}"
|
|
"HOSTCXX=${lib.getExe hostcxxSccache}"
|
|
"HOSTAR=${realHostAr}"
|
|
"HOSTLD=${realHostLd}"
|
|
"RUSTC=${lib.getExe rustcSccache}"
|
|
"HOSTRUSTC=${lib.getExe hostrustcSccache}"
|
|
];
|
|
|
|
structuredExtraConfig = structuredExtraConfig';
|
|
|
|
ignoreConfigErrors = true;
|
|
|
|
extraMeta.branch = "${V}.${P}";
|
|
}
|
|
)).overrideAttrs
|
|
(old: {
|
|
__noChroot = true;
|
|
postPatch =
|
|
(old.postPatch or "")
|
|
+ sccacheSetup
|
|
+ lib.optionalString enforceSccache ''
|
|
# -- Sccache caching smoke test --
|
|
echo "==> Testing sccache caching..."
|
|
SCCACHE_TEST_SRC="$NIX_BUILD_TOP/.sccache-test.c"
|
|
SCCACHE_TEST_OBJ="$NIX_BUILD_TOP/.sccache-test.o"
|
|
SCCACHE_TEST_ID=$(date +%s%N)
|
|
printf '// sccache smoke test %s\nint main(void) { return 0; }\n' "$SCCACHE_TEST_ID" > "$SCCACHE_TEST_SRC"
|
|
${pkgs.sccache}/bin/sccache --zero-stats >/dev/null 2>&1 || true
|
|
|
|
${lib.getExe clangSccache} -c "$SCCACHE_TEST_SRC" -o "$SCCACHE_TEST_OBJ" 2>&1
|
|
STATS1=$(${pkgs.sccache}/bin/sccache --show-stats --stats-format json 2>/dev/null || echo '{}')
|
|
COMPILED=$(echo "$STATS1" | ${pkgs.jq}/bin/jq '[.stats.cache_hits.counts // {}, .stats.cache_misses.counts // {}] | add | to_entries | map(.value) | add // 0' 2>/dev/null || echo 0)
|
|
WRITE_ERRORS=$(echo "$STATS1" | ${pkgs.jq}/bin/jq '.stats.cache_errors.counts // {} | to_entries | map(.value) | add // 0' 2>/dev/null || echo 0)
|
|
|
|
if [ "$COMPILED" -lt 1 ]; then
|
|
echo "FATAL: sccache first compile was not cached."
|
|
${pkgs.sccache}/bin/sccache --show-stats --stats-format text || true
|
|
exit 1
|
|
fi
|
|
if [ "$WRITE_ERRORS" -gt 0 ]; then
|
|
echo "FATAL: sccache cache write errors detected ($WRITE_ERRORS)."
|
|
${pkgs.sccache}/bin/sccache --show-stats --stats-format text || true
|
|
exit 1
|
|
fi
|
|
echo "==> sccache first compile OK (cached=$COMPILED, write_errors=$WRITE_ERRORS)"
|
|
|
|
rm -f "$SCCACHE_TEST_OBJ"
|
|
${lib.getExe clangSccache} -c "$SCCACHE_TEST_SRC" -o "$SCCACHE_TEST_OBJ" 2>&1
|
|
STATS2=$(${pkgs.sccache}/bin/sccache --show-stats --stats-format json 2>/dev/null || echo '{}')
|
|
HITS=$(echo "$STATS2" | ${pkgs.jq}/bin/jq '.stats.cache_hits.counts // {} | to_entries | map(.value) | add // 0' 2>/dev/null || echo 0)
|
|
|
|
if [ "$HITS" -lt 1 ]; then
|
|
echo "FATAL: sccache second compile did not hit cache."
|
|
${pkgs.sccache}/bin/sccache --show-stats --stats-format text || true
|
|
exit 1
|
|
fi
|
|
echo "==> sccache cache hit OK (hits=$HITS)"
|
|
rm -f "$SCCACHE_TEST_SRC" "$SCCACHE_TEST_OBJ"
|
|
${pkgs.sccache}/bin/sccache --zero-stats >/dev/null 2>&1 || true
|
|
# -- End sccache caching smoke test --
|
|
'';
|
|
})
|