diff --git a/src/libstore/build-result.hh b/src/libstore/build-result.hh index 8c66cfeb353..88d9a739743 100644 --- a/src/libstore/build-result.hh +++ b/src/libstore/build-result.hh @@ -36,6 +36,7 @@ struct BuildResult NotDeterministic, ResolvesToAlreadyValid, NoSubstituters, + NoCompatibleBuilder, } status = MiscFailure; /** @@ -64,6 +65,7 @@ struct BuildResult case NotDeterministic: return "NotDeterministic"; case ResolvesToAlreadyValid: return "ResolvesToAlreadyValid"; case NoSubstituters: return "NoSubstituters"; + case NoCompatibleBuilder: return "NoCompatibleBuilder"; default: return "Unknown"; }; }(); diff --git a/src/libstore/build/worker.cc b/src/libstore/build/worker.cc index dbe86f43f6a..56bb9fcccd7 100644 --- a/src/libstore/build/worker.cc +++ b/src/libstore/build/worker.cc @@ -339,7 +339,7 @@ void Worker::run(const Goals & _topGoals) waitForInput(); else if (awake.empty() && 0U == settings.maxBuildJobs) { if (getMachines().empty()) - throw Error( + throw NoCompatibleBuilder( R"( Unable to start any build; either increase '--max-jobs' or enable remote builds. @@ -348,7 +348,7 @@ void Worker::run(const Goals & _topGoals) )" ); else - throw Error( + throw NoCompatibleBuilder( R"( Unable to start any build; remote machines may not have all required system features. diff --git a/src/libstore/store-api.hh b/src/libstore/store-api.hh index f45012061ff..cfd0f943625 100644 --- a/src/libstore/store-api.hh +++ b/src/libstore/store-api.hh @@ -61,6 +61,7 @@ MakeError(InvalidPath, Error); MakeError(Unsupported, Error); MakeError(SubstituteGone, Error); MakeError(SubstituterDisabled, Error); +MakeError(NoCompatibleBuilder, Error); MakeError(InvalidStoreReference, Error); diff --git a/src/libstore/unix/build/local-derivation-goal.cc b/src/libstore/unix/build/local-derivation-goal.cc index 06a2f85be84..1c686b9eade 100644 --- a/src/libstore/unix/build/local-derivation-goal.cc +++ b/src/libstore/unix/build/local-derivation-goal.cc @@ -264,6 +264,10 @@ Goal::Co LocalDerivationGoal::tryLocalBuild() buildUser.reset(); worker.permanentFailure = true; co_return done(BuildResult::InputRejected, {}, std::move(e)); + } catch (NoCompatibleBuilder & e) { + outputLocks.unlock(); + buildUser.reset(); + co_return done(BuildResult::NoCompatibleBuilder, {}, std::move(e)); } started(); @@ -532,7 +536,7 @@ void LocalDerivationGoal::startBuilder() /* Right platform? */ if (!parsedDrv->canBuildLocally(worker.store)) - throw Error("a '%s' with features {%s} is required to build '%s', but I am a '%s' with features {%s}", + throw NoCompatibleBuilder("a '%s' with features {%s} is required to build '%s', but I am a '%s' with features {%s}", drv->platform, concatStringsSep(", ", parsedDrv->getRequiredSystemFeatures()), worker.store.printStorePath(drvPath), diff --git a/tests/functional/keep-going.nix b/tests/functional/keep-going.nix new file mode 100644 index 00000000000..b95acc57240 --- /dev/null +++ b/tests/functional/keep-going.nix @@ -0,0 +1,33 @@ +with import ./config.nix; + +rec { + + # Hack to get the scheduler to do what we want: The `good` derivation can + # only be built after `delay_good` (which takes a long time to build) while + # the others don't have any dependency. + # This means that if we build this with parallelism (`-j2`), then we can be + # reasonably sure that the failing derivations will be scheduled _before_ the + # `good` one (and so we can check that `--keep-going` works fine) + delay_good = mkDerivation { + name = "delay-good"; + buildCommand = "sleep 3; touch $out"; + }; + + good = mkDerivation { + name = "good"; + buildCommand = "mkdir $out; echo foo > $out/bar"; + delay = delay_good; + }; + + failing = mkDerivation { + name = "failing"; + buildCommand = false; + }; + + requiresFooSystemFeature = mkDerivation { + name = "requires-foo-system-feature"; + buildCommand = "mkdir $out; echo foo > $out/bar"; + requiredSystemFeatures = [ "foo" ]; + }; + +} diff --git a/tests/functional/keep-going.sh b/tests/functional/keep-going.sh new file mode 100644 index 00000000000..94b3be7b14c --- /dev/null +++ b/tests/functional/keep-going.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash + +source common.sh + +clearStore + +mkdir -p "$TEST_ROOT/sync" +# XXX: These invocations of nix-build should always return 100 according to the manpage, but often return 1 +(! nix-build ./keep-going.nix -j2 --extra-sandbox-paths "$TEST_ROOT/sync" --no-out-link 2>&1 \ + | while read -r ln; do + echo "nix build: $ln" + if echo "$ln" | grepQuiet ...; then + touch "$TEST_ROOT/sync/failing-drv-failed" + fi + done) +(! nix-build ./keep-going.nix -A good -j0 --no-out-link) || \ + fail "Hello shouldn't have been built because of earlier errors" + +clearStore + +(! nix-build ./keep-going.nix --keep-going -j2 --no-out-link) +nix-build ./keep-going.nix -A good -j0 --no-out-link || \ + fail "Hello should have been built despite the errors because of '--keep-going'" diff --git a/tests/functional/meson.build b/tests/functional/meson.build index 83e08c4f5ad..bb78cadb1ad 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -164,6 +164,7 @@ suites = [ 'debugger.sh', 'extra-sandbox-profile.sh', 'help.sh', + 'keep-going.sh', ], 'workdir': meson.current_source_dir(), },