nixos-conf/custom/sccache.nix
2026-03-26 04:54:48 +02:00

387 lines
12 KiB
Nix

{ final, prev }:
let
sandboxSccacheDir = "/var/cache/sccache/nix-builds/packages";
useSocketedSccache = false; # fuckin broken
sharedSccacheServerUds = "/run/sccache/server.sock";
effectiveSccacheServerUds = if useSocketedSccache then sharedSccacheServerUds else null;
sccacheRuntimeModeSetup = ''
unset SCCACHE_NO_DAEMON
${final.lib.optionalString (effectiveSccacheServerUds != null) ''
export SCCACHE_SERVER_UDS=${final.lib.escapeShellArg effectiveSccacheServerUds}
unset SCCACHE_IDLE_TIMEOUT
''}
${final.lib.optionalString (effectiveSccacheServerUds == null) ''
unset SCCACHE_SERVER_UDS
export SCCACHE_IDLE_TIMEOUT=0
''}
'';
sharedSccacheConfig = final.writeText "sccache.conf" ''
[cache.disk]
dir = "${sandboxSccacheDir}"
size = "100G"
'';
modify =
target: f:
if target ? overridePythonAttrs then
target.overridePythonAttrs f
else if target ? overrideAttrs then
target.overrideAttrs f
else if target ? overrideScope then
target.overrideScope f
else
throw "modify: target does not support overridePythonAttrs, overrideAttrs, or overrideScope";
mkSccacheLauncher =
{
name,
sccacheDir ? null,
sccacheServerUds ? effectiveSccacheServerUds,
sccacheCacheSize ? "100G",
noDaemon ? false,
bypassIfCmakeLauncher ? false,
passthroughWrappedCompiler ? false,
extraSetup ? "",
}:
let
sccacheConfig = final.writeText "${name}-config" ''
[cache.disk]
dir = "${if sccacheDir != null then sccacheDir else sandboxSccacheDir}"
size = "${sccacheCacheSize}"
'';
effectiveSccacheConfig =
if sccacheDir == null && sccacheCacheSize == "100G" then sharedSccacheConfig else sccacheConfig;
in
final.writeShellScriptBin name ''
is_cmake_derivation() {
case " ''${nativeBuildInputs:-} ''${propagatedNativeBuildInputs:-} " in
*"/cmake-"*) return 0 ;;
esac
[ -n "''${cmakeFlags:-}" ] || [ -n "''${cmakeDir:-}" ] || [ -n "''${cmakeBuildType:-}" ]
}
is_cmake_probe_invocation() {
local arg response_file
for arg in "$@"; do
case "$arg" in
*CMakeFiles/*CompilerId*|*CMakeFiles/CMakeTmp/*|*CMakeScratch/*)
return 0
;;
@*)
response_file="''${arg#@}"
if [ -f "$response_file" ] && grep -Eq 'CMakeFiles/.+CompilerId|CMakeFiles/CMakeTmp|CMakeScratch' "$response_file"; then
return 0
fi
;;
esac
done
return 1
}
has_cmake_compiler_launcher() {
[ -n "''${CMAKE_C_COMPILER_LAUNCHER:-}" ] \
|| [ -n "''${CMAKE_CXX_COMPILER_LAUNCHER:-}" ] \
|| [ -n "''${CMAKE_Fortran_COMPILER_LAUNCHER:-}" ] \
|| [ -n "''${CMAKE_CUDA_COMPILER_LAUNCHER:-}" ] \
|| [ -n "''${CMAKE_HIP_COMPILER_LAUNCHER:-}" ] \
|| [ -n "''${CMAKE_OBJC_COMPILER_LAUNCHER:-}" ] \
|| [ -n "''${CMAKE_OBJCXX_COMPILER_LAUNCHER:-}" ]
}
export SCCACHE_CONF=${final.lib.escapeShellArg effectiveSccacheConfig}
mkdir -p ${final.lib.escapeShellArg sandboxSccacheDir}
unset SCCACHE_START_SERVER
${final.lib.optionalString noDaemon ''
export SCCACHE_NO_DAEMON=1
unset SCCACHE_SERVER_UDS
''}
${final.lib.optionalString (!noDaemon && sccacheServerUds != null) ''
export SCCACHE_SERVER_UDS=${final.lib.escapeShellArg sccacheServerUds}
''}
${final.lib.optionalString (!noDaemon && sccacheServerUds == null) ''
unset SCCACHE_SERVER_UDS
unset SCCACHE_NO_DAEMON
''}
${extraSetup}
if [ "$#" -eq 0 ]; then
exit 0
fi
if [ "''${1#-}" != "$1" ]; then
exec ${final.sccache}/bin/sccache "$@"
fi
compiler="$1"
shift
if [ "''${SCCACHE_WRAPPED_COMPILER_PASSTHROUGH:-}" = 1 ] \
|| { [ ${if bypassIfCmakeLauncher then "1" else "0"} -eq 1 ] && has_cmake_compiler_launcher; } \
|| {
is_cmake_derivation && is_cmake_probe_invocation "$@"
}; then
exec "$compiler" "$@"
fi
${final.lib.optionalString passthroughWrappedCompiler ''
export SCCACHE_WRAPPED_COMPILER_PASSTHROUGH=1
''}
exec ${final.sccache}/bin/sccache "$compiler" "$@"
'';
buildSccacheLauncher = mkSccacheLauncher {
name = "build-sccache";
};
mkBuildSccacheAttrs = old: {
nativeBuildInputs = (old.nativeBuildInputs or [ ]) ++ [ buildSccacheLauncher ];
preConfigure = (old.preConfigure or "") + ''
mkdir -p ${final.lib.escapeShellArg sandboxSccacheDir}
${sccacheRuntimeModeSetup}
'';
postBuild = (old.postBuild or "") + ''
${buildSccacheLauncher}/bin/build-sccache --show-stats --stats-format text || true
'';
};
mkCmakeSccacheAttrs =
old:
let
sccacheLauncher = mkSccacheLauncher {
name = "cmake-sccache";
passthroughWrappedCompiler = true;
};
in
{
cmakeFlags = (old.cmakeFlags or [ ]) ++ [
"-DCMAKE_C_COMPILER_LAUNCHER=${sccacheLauncher}/bin/cmake-sccache"
"-DCMAKE_CXX_COMPILER_LAUNCHER=${sccacheLauncher}/bin/cmake-sccache"
];
nativeBuildInputs = (old.nativeBuildInputs or [ ]) ++ [ sccacheLauncher ];
preConfigure = (old.preConfigure or "") + ''
mkdir -p ${final.lib.escapeShellArg sandboxSccacheDir}
export SCCACHE_WRAPPED_COMPILER_PASSTHROUGH=1
${sccacheRuntimeModeSetup}
'';
postConfigure = (old.postConfigure or "") + ''
unset SCCACHE_WRAPPED_COMPILER_PASSTHROUGH
'';
postBuild = (old.postBuild or "") + ''
${sccacheLauncher}/bin/cmake-sccache --show-stats --stats-format text || true
'';
};
mkCmakeSccacheWithLlvmTargetAttrs =
llvmTarget: old:
let
base = mkCmakeSccacheAttrs old;
targetFlag = "-DLLVM_TARGETS_TO_BUILD=${llvmTarget}";
hasTargetFlag = flag: final.lib.hasPrefix "-DLLVM_TARGETS_TO_BUILD=" flag;
replacedFlags = map (flag: if hasTargetFlag flag then targetFlag else flag) (
base.cmakeFlags or [ ]
);
withTargetFlag =
replacedFlags ++ final.lib.optional (!(final.lib.any hasTargetFlag replacedFlags)) targetFlag;
in
base
// {
cmakeFlags = withTargetFlag;
};
mkSccacheExtraConfig =
{
sccacheDir ? sandboxSccacheDir,
extraConfig ? "",
}:
''
mkdir -p ${final.lib.escapeShellArg sccacheDir}
export SCCACHE_CONF=${final.lib.escapeShellArg (
final.writeText "sccache-stdenv.conf" ''
[cache.disk]
dir = "${sccacheDir}"
size = "100G"
''
)}
${sccacheRuntimeModeSetup}
${extraConfig}
'';
mkWrappedCcForSccache =
{
cc,
extraConfig ? "",
}:
let
wrappedCompiler = final.sccache.links {
inherit extraConfig;
unwrappedCC = cc.cc;
};
overrideArgs = cc.override.__functionArgs or { };
in
if overrideArgs ? cc then
cc.override { cc = wrappedCompiler; }
else if overrideArgs ? llvm then
cc.override { llvm = wrappedCompiler; }
else
throw "mkWrappedCcForSccache: unsupported compiler wrapper override interface";
mkSccacheStdenv =
{
stdenv,
sccacheDir ? "/var/cache/sccache/nix-builds/packages",
extraConfig ? "",
}:
final.overrideCC stdenv (mkWrappedCcForSccache {
inherit (stdenv) cc;
extraConfig = mkSccacheExtraConfig {
inherit extraConfig sccacheDir;
};
});
in
{
inherit
sandboxSccacheDir
sharedSccacheConfig
sccacheRuntimeModeSetup
mkSccacheLauncher
mkBuildSccacheAttrs
mkCmakeSccacheAttrs
mkCmakeSccacheWithLlvmTargetAttrs
mkSccacheExtraConfig
;
overlay = {
sccache = modify prev.sccache (old: {
postPatch = (old.postPatch or "") + ''
substituteInPlace src/compiler/gcc.rs \
--replace-fail \
' take_arg!("-isystem", PathBuf, CanBeSeparated, PreprocessorArgumentPath),' \
' take_arg!("-isystem", PathBuf, CanBeSeparated, PreprocessorArgumentPath),
take_arg!("-cxx-isystem", PathBuf, CanBeSeparated, PreprocessorArgumentPath),'
'';
env = (old.env or { }) // {
RUSTC_WRAPPER = "${prev.sccache}/bin/sccache";
SCCACHE_CONF = sharedSccacheConfig;
};
preBuild = (old.preBuild or "") + ''
mkdir -p ${final.lib.escapeShellArg sandboxSccacheDir}
'';
passthru = (old.passthru or { }) // {
links =
{
unwrappedCC,
extraConfig ? "",
}:
let
sccacheLauncher = mkSccacheLauncher {
name = "wrapped-sccache";
bypassIfCmakeLauncher = true;
extraSetup = extraConfig;
};
in
final.stdenv.mkDerivation {
pname = "sccache-links";
inherit (old) version;
passthru = {
isClang = unwrappedCC.isClang or false;
isGNU = unwrappedCC.isGNU or false;
isSccache = true;
dev = unwrappedCC.dev or null;
lib = unwrappedCC.lib or (final.lib.getLib unwrappedCC);
}
// final.lib.optionalAttrs (unwrappedCC ? rsrc) {
inherit (unwrappedCC) rsrc;
};
dev = unwrappedCC.dev or null;
lib = unwrappedCC.lib or (final.lib.getLib unwrappedCC);
rsrc = unwrappedCC.rsrc or null;
nativeBuildInputs = [ final.makeWrapper ];
buildCommand =
let
targetPrefix =
if unwrappedCC.isClang or false then
""
else
(final.lib.optionalString (
unwrappedCC ? targetConfig && unwrappedCC.targetConfig != null && unwrappedCC.targetConfig != ""
) "${unwrappedCC.targetConfig}-");
in
''
mkdir -p $out/bin
mkdir -p $out/nix-support
wrap() {
local cname="${targetPrefix}$1"
if [ -x "${unwrappedCC}/bin/$cname" ]; then
makeWrapper ${sccacheLauncher}/bin/wrapped-sccache $out/bin/$cname \
--add-flags ${unwrappedCC}/bin/$cname
fi
}
wrap cc
wrap c++
wrap gcc
wrap g++
wrap clang
wrap clang++
for executable in $(ls ${unwrappedCC}/bin); do
if [ ! -x "$out/bin/$executable" ]; then
ln -s ${unwrappedCC}/bin/$executable $out/bin/$executable
fi
done
for file in $(ls ${unwrappedCC} | grep -vw bin); do
ln -s ${unwrappedCC}/$file $out/$file
done
printf '%s\n' "${unwrappedCC}" > $out/nix-support/orig-cc
'';
meta = final.lib.optionalAttrs (unwrappedCC.meta ? mainProgram) {
inherit (unwrappedCC.meta) mainProgram;
};
};
};
});
sccacheWrapper =
final.lib.makeOverridable
(
{
extraConfig ? "",
cc,
}:
mkWrappedCcForSccache {
inherit cc extraConfig;
}
)
{
extraConfig = "";
inherit (final.stdenv) cc;
};
sccacheStdenv = final.lib.lowPrio (
final.lib.makeOverridable
(
{
stdenv,
...
}@extraArgs:
final.overrideCC stdenv (
final.buildPackages.sccacheWrapper.override (
{
inherit (stdenv) cc;
}
// final.lib.optionalAttrs (builtins.hasAttr "extraConfig" extraArgs) {
extraConfig = extraArgs.extraConfig;
}
)
)
)
{
inherit (final) stdenv;
}
);
sccache-config = sharedSccacheConfig;
};
}