diff options
-rw-r--r-- | .editorconfig | 4 | ||||
-rw-r--r-- | doc/internal-api/doxygen.cfg.in | 39 | ||||
-rw-r--r-- | doc/internal-api/meson.build | 46 | ||||
-rw-r--r-- | doc/manual/meson.build | 1 | ||||
-rw-r--r-- | meson.build | 6 | ||||
-rw-r--r-- | src/libstore/build/derivation-goal.cc | 9 | ||||
-rw-r--r-- | src/libstore/build/derivation-goal.hh | 8 | ||||
-rw-r--r-- | src/libstore/build/worker.cc | 47 | ||||
-rw-r--r-- | src/libstore/build/worker.hh | 2 | ||||
-rw-r--r-- | tests/functional/build.sh | 4 |
10 files changed, 101 insertions, 65 deletions
diff --git a/.editorconfig b/.editorconfig index bcee9cfce..bbc2a4908 100644 --- a/.editorconfig +++ b/.editorconfig @@ -29,3 +29,7 @@ trim_trailing_whitespace = false indent_style = space indent_size = 2 max_line_length = 0 + +[meson.build] +indent_style = space +indent_size = 2 diff --git a/doc/internal-api/doxygen.cfg.in b/doc/internal-api/doxygen.cfg.in index 73fba6948..662fb4333 100644 --- a/doc/internal-api/doxygen.cfg.in +++ b/doc/internal-api/doxygen.cfg.in @@ -33,32 +33,7 @@ GENERATE_LATEX = NO # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. -# FIXME Make this list more maintainable somehow. We could maybe generate this -# in the Makefile, but we would need to change how `.in` files are preprocessed -# so they can expand variables despite configure variables. - -INPUT = \ - src/libcmd \ - src/libexpr \ - src/libexpr/flake \ - tests/unit/libexpr \ - tests/unit/libexpr/value \ - tests/unit/libexpr/test \ - tests/unit/libexpr/test/value \ - src/libexpr/value \ - src/libfetchers \ - src/libmain \ - src/libstore \ - src/libstore/build \ - src/libstore/builtins \ - tests/unit/libstore \ - tests/unit/libstore/test \ - src/libutil \ - tests/unit/libutil \ - tests/unit/libutil/test \ - src/nix \ - src/nix-env \ - src/nix-store +INPUT = @INPUT_PATHS@ # If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names # in the source code. If set to NO, only conditional compilation will be @@ -97,3 +72,15 @@ EXPAND_AS_DEFINED = \ DECLARE_WORKER_SERIALISER \ DECLARE_SERVE_SERIALISER \ LENGTH_PREFIXED_PROTO_HELPER + +# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. +# Stripping is only done if one of the specified strings matches the left-hand +# part of the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the path to +# strip. +# +# Note that you can specify absolute paths here, but also relative paths, which +# will be relative from the directory where doxygen is started. +# This tag requires that the tag FULL_PATH_NAMES is set to YES. + +STRIP_FROM_PATH = "@PROJECT_SOURCE_ROOT@" diff --git a/doc/internal-api/meson.build b/doc/internal-api/meson.build index faa30f194..af93b6943 100644 --- a/doc/internal-api/meson.build +++ b/doc/internal-api/meson.build @@ -1,3 +1,35 @@ +internal_api_sources = [ + 'src/libcmd', + 'src/libexpr', + 'src/libexpr/flake', + 'tests/unit/libexpr', + 'tests/unit/libexpr/value', + 'tests/unit/libexpr/test', + 'tests/unit/libexpr/test/value', + 'src/libexpr/value', + 'src/libfetchers', + 'src/libmain', + 'src/libstore', + 'src/libstore/build', + 'src/libstore/builtins', + 'tests/unit/libstore', + 'tests/unit/libstore/test', + 'src/libutil', + 'tests/unit/libutil', + 'tests/unit/libutil/test', + 'src/nix', + 'src/nix-env', + 'src/nix-store', +] + +# We feed Doxygen absolute paths so it can be invoked from any working directory. +internal_api_sources_absolute = [] +foreach src : internal_api_sources + internal_api_sources_absolute += '"' + (meson.project_source_root() / src) + '"' +endforeach + +internal_api_sources_oneline = ' \\\n '.join(internal_api_sources_absolute) + doxygen_cfg = configure_file( input : 'doxygen.cfg.in', output : 'doxygen.cfg', @@ -5,22 +37,16 @@ doxygen_cfg = configure_file( 'PACKAGE_VERSION': meson.project_version(), 'RAPIDCHECK_HEADERS': rapidcheck_meson.get_variable('includedir'), 'docdir' : meson.current_build_dir(), + 'INPUT_PATHS' : internal_api_sources_oneline, + 'PROJECT_SOURCE_ROOT' : meson.project_source_root(), }, ) internal_api_docs = custom_target( 'internal-api-docs', command : [ - bash, - # Meson can you please just give us a `workdir` argument to custom targets... - '-c', - # We have to prefix the doxygen_cfg path with the project build root - # because of the cd in front. - 'cd @0@ && @1@ @2@/@INPUT0@'.format( - meson.project_source_root(), - doxygen.full_path(), - meson.project_build_root(), - ), + doxygen.full_path(), + '@INPUT0@', ], input : [ doxygen_cfg, diff --git a/doc/manual/meson.build b/doc/manual/meson.build index 38aad55b5..35d94740c 100644 --- a/doc/manual/meson.build +++ b/doc/manual/meson.build @@ -126,6 +126,7 @@ manual = custom_target( 'manual', 'markdown', ], + install : true, install_dir : [ datadir / 'doc/nix', false, diff --git a/meson.build b/meson.build index ea2050b6b..a8c6b9621 100644 --- a/meson.build +++ b/meson.build @@ -493,12 +493,6 @@ add_project_arguments( '-Wdeprecated-copy', '-Wignored-qualifiers', '-Werror=suggest-override', - # Enable assertions in libstdc++ by default. Harmless on libc++. Benchmarked - # at ~1% overhead in `nix search`. - # - # FIXME: remove when we get meson 1.4.0 which will default this to on for us: - # https://mesonbuild.com/Release-notes-for-1-4-0.html#ndebug-setting-now-controls-c-stdlib-assertions - '-D_GLIBCXX_ASSERTIONS=1', language : 'cpp', ) diff --git a/src/libstore/build/derivation-goal.cc b/src/libstore/build/derivation-goal.cc index fe2bb0145..60389fdd5 100644 --- a/src/libstore/build/derivation-goal.cc +++ b/src/libstore/build/derivation-goal.cc @@ -130,8 +130,12 @@ kj::Promise<Result<Goal::WorkResult>> DerivationGoal::work() noexcept return useDerivation ? getDerivation() : haveDerivation(); } -void DerivationGoal::addWantedOutputs(const OutputsSpec & outputs) +bool DerivationGoal::addWantedOutputs(const OutputsSpec & outputs) { + if (isDone) { + return false; + } + auto newWanted = wantedOutputs.union_(outputs); switch (needRestart) { case NeedRestartForMoreOutputs::OutputsUnmodifedDontNeed: @@ -148,6 +152,7 @@ void DerivationGoal::addWantedOutputs(const OutputsSpec & outputs) break; }; wantedOutputs = newWanted; + return true; } @@ -1680,6 +1685,8 @@ Goal::Finished DerivationGoal::done( SingleDrvOutputs builtOutputs, std::optional<Error> ex) { + isDone = true; + outputLocks.unlock(); buildResult.status = status; if (ex) diff --git a/src/libstore/build/derivation-goal.hh b/src/libstore/build/derivation-goal.hh index 7505409c0..1e2f9b55c 100644 --- a/src/libstore/build/derivation-goal.hh +++ b/src/libstore/build/derivation-goal.hh @@ -74,6 +74,12 @@ struct DerivationGoal : public Goal struct InputStream; /** + * Whether this goal has completed. Completed goals can not be + * asked for more outputs, a new goal must be created instead. + */ + bool isDone = false; + + /** * Whether to use an on-disk .drv file. */ bool useDerivation; @@ -249,7 +255,7 @@ struct DerivationGoal : public Goal /** * Add wanted outputs to an already existing derivation goal. */ - void addWantedOutputs(const OutputsSpec & outputs); + bool addWantedOutputs(const OutputsSpec & outputs); /** * The states. diff --git a/src/libstore/build/worker.cc b/src/libstore/build/worker.cc index 5be706e42..e063ede71 100644 --- a/src/libstore/build/worker.cc +++ b/src/libstore/build/worker.cc @@ -57,24 +57,35 @@ std::pair<std::shared_ptr<G>, kj::Promise<void>> Worker::makeGoalCommon( std::map<ID, CachedGoal<G>> & map, const ID & key, InvocableR<std::unique_ptr<G>> auto create, - std::invocable<G &> auto modify + InvocableR<bool, G &> auto modify ) { auto [it, _inserted] = map.try_emplace(key); - auto & goal_weak = it->second; - auto goal = goal_weak.goal.lock(); - if (!goal) { - goal = create(); - goal->notify = std::move(goal_weak.fulfiller); - goal_weak.goal = goal; - // do not start working immediately, this round of the event loop - // may have more calls to this function lined up that'll also run - // modify(). starting early can then cause the goals to misbehave - childStarted(goal, kj::evalLater([goal] { return goal->work(); })); - } else { - modify(*goal); + // try twice to create the goal. we can only loop if we hit the continue, + // and then we only want to recreate the goal *once*. concurrent accesses + // to the worker are not sound, we want to catch them if at all possible. + for ([[maybe_unused]] auto _attempt : {1, 2}) { + auto & goal_weak = it->second; + auto goal = goal_weak.goal.lock(); + if (!goal) { + goal = create(); + goal->notify = std::move(goal_weak.fulfiller); + goal_weak.goal = goal; + // do not start working immediately. if we are not yet running we + // may create dependencies as though they were toplevel goals, in + // which case the dependencies will not report build errors. when + // we are running we may be called for this same goal more times, + // and then we want to modify rather than recreate when possible. + childStarted(goal, kj::evalLater([goal] { return goal->work(); })); + } else { + if (!modify(*goal)) { + goal_weak = {}; + continue; + } + } + return {goal, goal_weak.promise->addBranch()}; } - return {goal, goal_weak.promise->addBranch()}; + assert(false && "could not make a goal. possible concurrent worker access"); } @@ -94,7 +105,7 @@ std::pair<std::shared_ptr<DerivationGoal>, kj::Promise<void>> Worker::makeDeriva drvPath, wantedOutputs, *this, running, buildMode ); }, - [&](DerivationGoal & g) { g.addWantedOutputs(wantedOutputs); } + [&](DerivationGoal & g) { return g.addWantedOutputs(wantedOutputs); } ); } @@ -118,7 +129,7 @@ std::pair<std::shared_ptr<DerivationGoal>, kj::Promise<void>> Worker::makeBasicD drvPath, drv, wantedOutputs, *this, running, buildMode ); }, - [&](DerivationGoal & g) { g.addWantedOutputs(wantedOutputs); } + [&](DerivationGoal & g) { return g.addWantedOutputs(wantedOutputs); } ); } @@ -132,7 +143,7 @@ Worker::makePathSubstitutionGoal( substitutionGoals, path, [&] { return std::make_unique<PathSubstitutionGoal>(path, *this, running, repair, ca); }, - [&](auto &) {} + [&](auto &) { return true; } ); } @@ -146,7 +157,7 @@ Worker::makeDrvOutputSubstitutionGoal( drvOutputSubstitutionGoals, id, [&] { return std::make_unique<DrvOutputSubstitutionGoal>(id, *this, running, repair, ca); }, - [&](auto &) {} + [&](auto &) { return true; } ); } diff --git a/src/libstore/build/worker.hh b/src/libstore/build/worker.hh index d6cde8384..cd49fb860 100644 --- a/src/libstore/build/worker.hh +++ b/src/libstore/build/worker.hh @@ -241,7 +241,7 @@ private: std::map<ID, CachedGoal<G>> & map, const ID & key, InvocableR<std::unique_ptr<G>> auto create, - std::invocable<G &> auto modify + InvocableR<bool, G &> auto modify ); std::pair<std::shared_ptr<DerivationGoal>, kj::Promise<void>> makeDerivationGoal( const StorePath & drvPath, diff --git a/tests/functional/build.sh b/tests/functional/build.sh index a14f6e3c2..58fba83aa 100644 --- a/tests/functional/build.sh +++ b/tests/functional/build.sh @@ -144,8 +144,8 @@ test "$(<<<"$out" grep -E '^error:' | wc -l)" = 1 # --keep-going and FOD out="$(nix build -f fod-failing.nix -L 2>&1)" && status=0 || status=$? test "$status" = 1 -# one "hash mismatch" error, one "build of ... failed" -test "$(<<<"$out" grep -E '^error:' | wc -l)" = 2 +# at least one "hash mismatch" error, one "build of ... failed" +test "$(<<<"$out" grep -E '^error:' | wc -l)" -ge 2 <<<"$out" grepQuiet -E "hash mismatch in fixed-output derivation '.*-x.\\.drv'" <<<"$out" grepQuiet -E "likely URL: " <<<"$out" grepQuiet -E "error: build of '.*-x[1-4]\\.drv\\^out', '.*-x[1-4]\\.drv\\^out', '.*-x[1-4]\\.drv\\^out', '.*-x[1-4]\\.drv\\^out' failed" |