Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions builder/comp-builder.nix
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
, prePatch ? component.prePatch, postPatch ? component.postPatch
, preConfigure ? component.preConfigure, postConfigure ? component.postConfigure
, setupBuildFlags ? component.setupBuildFlags
, buildJobs ? component.buildJobs
, preBuild ? component.preBuild , postBuild ? component.postBuild
, preCheck ? component.preCheck , postCheck ? component.postCheck
, setupInstallFlags ? component.setupInstallFlags
Expand Down Expand Up @@ -229,6 +230,15 @@ let
then allComponent
else component;

# The `-jN` flag passed to `Setup build`. When `buildJobs` is null we keep
# the historical default (`-j` capped at min(NIX_BUILD_CORES, 4)); otherwise
# the component pins the job count (e.g. `buildJobs = 1` for a sequential,
# low-memory build). See #1479.
buildJobsFlag =
if buildJobs == null
then "-j$(($NIX_BUILD_CORES > 4 ? 4 : $NIX_BUILD_CORES))"
else "-j${toString buildJobs}";

# Ignore attempts to include DWARF info when it is not possible
enableDWARF = drvArgs.enableDWARF or false
&& stdenv.hostPlatform.isLinux
Expand Down Expand Up @@ -460,7 +470,7 @@ let
LANG = "en_US.UTF-8"; # GHC needs the locale configured during the Haddock phase.
LC_ALL = "en_US.UTF-8";

enableParallelBuilding = true;
enableParallelBuilding = buildJobs != 1;

SETUP_HS = setup + "/bin/${setup.exeName}";

Expand Down Expand Up @@ -699,7 +709,7 @@ let
'' else ''
runHook preBuild
# https://gitlab.haskell.org/ghc/ghc/issues/9221
$SETUP_HS build ${haskellLib.componentTarget componentId} -j$(($NIX_BUILD_CORES > 4 ? 4 : $NIX_BUILD_CORES)) ${lib.concatStringsSep " " setupBuildFlags}
$SETUP_HS build ${haskellLib.componentTarget componentId} ${buildJobsFlag} ${lib.concatStringsSep " " setupBuildFlags}
runHook postBuild
'');

Expand Down
13 changes: 13 additions & 0 deletions modules/component-options.nix
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,19 @@
default = [];
};

buildJobs = lib.mkOption {
description = ''
Number of jobs to pass to `Setup build` as `-jN` for this component.
`null` (the default) keeps the standard behaviour of `-j` capped at
`min(NIX_BUILD_CORES, 4)`. Set to `1` to build sequentially (useful
for memory-hungry packages that OOM under parallel builds), or to any
other positive integer to pin the parallelism. Only affects the v1
(`builderVersion = 1`) builder.
'';
type = lib.types.nullOr lib.types.ints.positive;
Comment on lines +20 to +28
default = null;
};

testFlags = lib.mkOption {
type = haskellLib.types.listOfFilteringNulls lib.types.str;
default = [];
Expand Down
71 changes: 71 additions & 0 deletions test/build-jobs/default.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# Test the per-component `buildJobs` option (#1479).
#
# `buildJobs` controls the `-jN` flag passed to `Setup build` and the
# derivation's `enableParallelBuilding`. We assert on the component
# derivation's `buildPhase` / `enableParallelBuilding` attributes directly
# (an eval-level check) so the test does not need to compile anything.
{ stdenv, lib, project', haskellLib, testSrc, compiler-nix-name, evalPackages, runCommand }:

with lib;

let
mkProject = extra: project' {
inherit compiler-nix-name evalPackages;
src = testSrc "cabal-simple";
modules = [
({
# cabal-simple's library has no exposed modules -> haddock fails.
packages.cabal-simple.doHaddock = false;
} // extra)
];
};

# Default project: no `buildJobs` set.
defaultExe = (mkProject {}).hsPkgs.cabal-simple.components.exes.cabal-simple;

# `buildJobs = 1`: sequential build.
seqExe = (mkProject {
packages.cabal-simple.components.exes.cabal-simple.buildJobs = 1;
}).hsPkgs.cabal-simple.components.exes.cabal-simple;

# `buildJobs = 3`: pinned parallelism.
pinnedExe = (mkProject {
packages.cabal-simple.components.exes.cabal-simple.buildJobs = 3;
}).hsPkgs.cabal-simple.components.exes.cabal-simple;

cappedDefault = "-j$(($NIX_BUILD_CORES > 4 ? 4 : $NIX_BUILD_CORES))";

checks = [
# Default is unchanged: capped `-j` and parallel building on.
{ name = "default keeps capped -j";
ok = hasInfix cappedDefault defaultExe.buildPhase; }
{ name = "default enables parallel building";
ok = defaultExe.enableParallelBuilding == true; }
# buildJobs = 1 -> `-j1`, parallel building off, capped default gone.
{ name = "buildJobs=1 passes -j1";
ok = hasInfix "-j1 " seqExe.buildPhase; }
{ name = "buildJobs=1 disables parallel building";
ok = seqExe.enableParallelBuilding == false; }
{ name = "buildJobs=1 drops the capped default";
ok = !(hasInfix cappedDefault seqExe.buildPhase); }
# buildJobs = 3 -> `-j3`, parallel building on.
{ name = "buildJobs=3 passes -j3";
ok = hasInfix "-j3 " pinnedExe.buildPhase; }
{ name = "buildJobs=3 keeps parallel building";
ok = pinnedExe.enableParallelBuilding == true; }
];

failures = filter (c: !c.ok) checks;

in lib.recurseIntoAttrs {
ifdInputs = {
inherit ((mkProject {})) plan-nix;
};

run = assert lib.assertMsg (failures == [])
"build-jobs test failed: ${toString (map (c: c.name) failures)}";
runCommand "build-jobs-test" { passthru = { inherit defaultExe seqExe pinnedExe; }; } ''
printf "build-jobs: all %d eval checks passed\n" ${toString (length checks)} >&2
touch $out
'';
}
1 change: 1 addition & 0 deletions test/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ let
# All tests.
allTests = {
cabal-simple = callTest ./cabal-simple { inherit util; };
build-jobs = callTest ./build-jobs {};
dummy-ghc-info = callTest ./dummy-ghc-info {};
check-datadir = callTest ./check-datadir {};
pkgconf-pc-version = callTest ./pkgconf-pc-version {};
Expand Down
Loading