diff options
45 files changed, 586 insertions, 116 deletions
diff --git a/.gitignore b/.gitignore index 8e0db013f..47815fde6 100644 --- a/.gitignore +++ b/.gitignore @@ -37,14 +37,14 @@ perl/Makefile.config /src/libexpr/parser-tab.hh /src/libexpr/parser-tab.output /src/libexpr/nix.tbl -/src/libexpr/tests/libexpr-tests +/src/libexpr/tests/libnixexpr-tests # /src/libstore/ *.gen.* -/src/libstore/tests/libstore-tests +/src/libstore/tests/libnixstore-tests # /src/libutil/ -/src/libutil/tests/libutil-tests +/src/libutil/tests/libnixutil-tests /src/nix/nix @@ -36,4 +36,4 @@ endif include mk/lib.mk -GLOBAL_CXXFLAGS += -g -Wall -include config.h -std=c++17 -I src +GLOBAL_CXXFLAGS += -g -Wall -include config.h -std=c++20 -I src diff --git a/configure.ac b/configure.ac index 0066bc389..09b3651b9 100644 --- a/configure.ac +++ b/configure.ac @@ -276,8 +276,11 @@ PKG_CHECK_MODULES([GTEST], [gtest_main]) # Look for rapidcheck. # No pkg-config yet, https://github.com/emil-e/rapidcheck/issues/302 +AC_LANG_PUSH(C++) AC_CHECK_HEADERS([rapidcheck/gtest.h], [], [], [#include <gtest/gtest.h>]) -AC_CHECK_LIB([rapidcheck], []) +dnl No good for C++ libs with mangled symbols +dnl AC_CHECK_LIB([rapidcheck], []) +AC_LANG_POP(C++) # Look for nlohmann/json. diff --git a/doc/manual/src/command-ref/nix-store.md b/doc/manual/src/command-ref/nix-store.md index 403cf285d..31fdd7806 100644 --- a/doc/manual/src/command-ref/nix-store.md +++ b/doc/manual/src/command-ref/nix-store.md @@ -633,7 +633,7 @@ written to standard output. A NAR archive is like a TAR or Zip archive, but it contains only the information that Nix considers important. For instance, timestamps are -elided because all files in the Nix store have their timestamp set to 0 +elided because all files in the Nix store have their timestamp set to 1 anyway. Likewise, all permissions are left out except for the execute bit, because all files in the Nix store have 444 or 555 permission. diff --git a/doc/manual/src/language/advanced-attributes.md b/doc/manual/src/language/advanced-attributes.md index f02425b13..a164b3997 100644 --- a/doc/manual/src/language/advanced-attributes.md +++ b/doc/manual/src/language/advanced-attributes.md @@ -255,3 +255,67 @@ Derivations can declare some infrequently used optional attributes. > substituted. Thus it is usually a good idea to align `system` with > `builtins.currentSystem` when setting `allowSubstitutes` to > `false`. For most trivial derivations this should be the case. + + - [`__structuredAttrs`]{#adv-attr-structuredAttrs}\ + If the special attribute `__structuredAttrs` is set to `true`, the other derivation + attributes are serialised in JSON format and made available to the + builder via the file `.attrs.json` in the builder’s temporary + directory. This obviates the need for `passAsFile` since JSON files + have no size restrictions, unlike process environments. + + It also makes it possible to tweak derivation settings in a structured way; see + [`outputChecks`](#adv-attr-outputChecks) for example. + + As a convenience to Bash builders, + Nix writes a script named `.attrs.sh` to the builder’s directory + that initialises shell variables corresponding to all attributes + that are representable in Bash. This includes non-nested + (associative) arrays. For example, the attribute `hardening.format = true` + ends up as the Bash associative array element `${hardening[format]}`. + + - [`outputChecks`]{#adv-attr-outputChecks}\ + When using [structured attributes](#adv-attr-structuredAttrs), the `outputChecks` + attribute allows defining checks per-output. + + In addition to + [`allowedReferences`](#adv-attr-allowedReferences), [`allowedRequisites`](#adv-attr-allowedRequisites), + [`disallowedReferences`](#adv-attr-disallowedReferences) and [`disallowedRequisites`](#adv-attr-disallowedRequisites), + the following attributes are available: + + - `maxSize` defines the maximum size of the output path. + - `maxClosureSize` defines the maximum size of the output's closure. + - `ignoreSelfRefs` controls whether self-references should be considered when + checking for allowed references/requisites. + + ```nix + __structuredAttrs = true; + + outputChecks.out = { + # The closure of 'out' must not be larger than 256 MiB. + maxClosureSize = 256 * 1024 * 1024; + + # It must not refer to the C compiler or to the 'dev' output. + disallowedRequisites = [ stdenv.cc "dev" ]; + }; + + outputChecks.dev = { + # The 'dev' output must not be larger than 128 KiB. + maxSize = 128 * 1024; + }; + ``` + + - [`unsafeDiscardReferences`]{#adv-attr-unsafeDiscardReferences}\ + When using [structured attributes](#adv-attr-structuredAttrs), the **experimental** + attribute `unsafeDiscardReferences` is a per-output boolean which, if set to `true`, + disables scanning the build output for runtime dependencies altogether. + + ```nix + __structuredAttrs = true; + unsafeDiscardReferences.out = true; + ``` + + This is useful, for example, when generating self-contained filesystem images with + their own embedded Nix store: hashes found inside such an image refer + to the embedded store and not to the host's Nix store. + + This is only allowed if the `discard-references` experimental feature is enabled. diff --git a/mk/programs.mk b/mk/programs.mk index 0fc1990f7..204409332 100644 --- a/mk/programs.mk +++ b/mk/programs.mk @@ -3,6 +3,9 @@ programs-list := # Build a program with symbolic name $(1). The program is defined by # various variables prefixed by ‘$(1)_’: # +# - $(1)_NAME: the name of the program (e.g. ‘foo’); defaults to +# $(1). +# # - $(1)_DIR: the directory where the (non-installed) program will be # placed. # @@ -23,11 +26,12 @@ programs-list := # - $(1)_INSTALL_DIR: the directory where the program will be # installed; defaults to $(bindir). define build-program + $(1)_NAME ?= $(1) _d := $(buildprefix)$$($(1)_DIR) _srcs := $$(sort $$(foreach src, $$($(1)_SOURCES), $$(src))) $(1)_OBJS := $$(addprefix $(buildprefix), $$(addsuffix .o, $$(basename $$(_srcs)))) _libs := $$(foreach lib, $$($(1)_LIBS), $$($$(lib)_PATH)) - $(1)_PATH := $$(_d)/$(1) + $(1)_PATH := $$(_d)/$$($(1)_NAME) $$(eval $$(call create-dir, $$(_d))) @@ -38,7 +42,7 @@ define build-program ifdef $(1)_INSTALL_DIR - $(1)_INSTALL_PATH := $$($(1)_INSTALL_DIR)/$(1) + $(1)_INSTALL_PATH := $$($(1)_INSTALL_DIR)/$$($(1)_NAME) $$(eval $$(call create-dir, $$($(1)_INSTALL_DIR))) diff --git a/perl/Makefile b/perl/Makefile index 708f86882..2d759e6fc 100644 --- a/perl/Makefile +++ b/perl/Makefile @@ -1,6 +1,6 @@ makefiles = local.mk -GLOBAL_CXXFLAGS += -g -Wall -std=c++17 -I ../src +GLOBAL_CXXFLAGS += -g -Wall -std=c++20 -I ../src -include Makefile.config diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc index 5090ea6d2..24f458f1a 100644 --- a/src/libcmd/installables.cc +++ b/src/libcmd/installables.cc @@ -379,10 +379,9 @@ Installable::getCursors(EvalState & state) ref<eval_cache::AttrCursor> Installable::getCursor(EvalState & state) { - auto cursors = getCursors(state); - if (cursors.empty()) - throw Error("cannot find flake attribute '%s'", what()); - return cursors[0]; + /* Although getCursors should return at least one element, in case it doesn't, + bound check to avoid an undefined behavior for vector[0] */ + return getCursors(state).at(0); } static StorePath getDeriver( @@ -696,46 +695,28 @@ InstallableFlake::getCursors(EvalState & state) std::vector<ref<eval_cache::AttrCursor>> res; - for (auto & attrPath : getActualAttrPaths()) { - auto attr = root->findAlongAttrPath(parseAttrPath(state, attrPath)); - if (attr) res.push_back(ref(*attr)); - } - - return res; -} - -ref<eval_cache::AttrCursor> InstallableFlake::getCursor(EvalState & state) -{ - auto lockedFlake = getLockedFlake(); - - auto cache = openEvalCache(state, lockedFlake); - auto root = cache->getRoot(); - Suggestions suggestions; - auto attrPaths = getActualAttrPaths(); for (auto & attrPath : attrPaths) { debug("trying flake output attribute '%s'", attrPath); - auto attrOrSuggestions = root->findAlongAttrPath( - parseAttrPath(state, attrPath), - true - ); - - if (!attrOrSuggestions) { - suggestions += attrOrSuggestions.getSuggestions(); - continue; + auto attr = root->findAlongAttrPath(parseAttrPath(state, attrPath)); + if (attr) { + res.push_back(ref(*attr)); + } else { + suggestions += attr.getSuggestions(); } - - return *attrOrSuggestions; } - throw Error( - suggestions, - "flake '%s' does not provide attribute %s", - flakeRef, - showAttrPaths(attrPaths)); + if (res.size() == 0) + throw Error( + suggestions, + "flake '%s' does not provide attribute %s", + flakeRef, + showAttrPaths(attrPaths)); + + return res; } std::shared_ptr<flake::LockedFlake> InstallableFlake::getLockedFlake() const diff --git a/src/libcmd/installables.hh b/src/libcmd/installables.hh index 3d12639b0..da6a3addd 100644 --- a/src/libcmd/installables.hh +++ b/src/libcmd/installables.hh @@ -103,9 +103,13 @@ struct Installable return {}; } + /* Get a cursor to each value this Installable could refer to. However + if none exists, throw exception instead of returning empty vector. */ virtual std::vector<ref<eval_cache::AttrCursor>> getCursors(EvalState & state); + /* Get the first and most preferred cursor this Installable could refer + to, or throw an exception if none exists. */ virtual ref<eval_cache::AttrCursor> getCursor(EvalState & state); @@ -193,15 +197,11 @@ struct InstallableFlake : InstallableValue std::pair<Value *, PosIdx> toValue(EvalState & state) override; - /* Get a cursor to every attrpath in getActualAttrPaths() that - exists. */ + /* Get a cursor to every attrpath in getActualAttrPaths() + that exists. However if none exists, throw an exception. */ std::vector<ref<eval_cache::AttrCursor>> getCursors(EvalState & state) override; - /* Get a cursor to the first attrpath in getActualAttrPaths() that - exists, or throw an exception with suggestions if none exists. */ - ref<eval_cache::AttrCursor> getCursor(EvalState & state) override; - std::shared_ptr<flake::LockedFlake> getLockedFlake() const; FlakeRef nixpkgsFlakeRef() const override; diff --git a/src/libcmd/nix-cmd.pc.in b/src/libcmd/nix-cmd.pc.in index 1761a9f41..a21d93f1d 100644 --- a/src/libcmd/nix-cmd.pc.in +++ b/src/libcmd/nix-cmd.pc.in @@ -6,4 +6,4 @@ Name: Nix Description: Nix Package Manager Version: @PACKAGE_VERSION@ Libs: -L${libdir} -lnixcmd -Cflags: -I${includedir}/nix -std=c++17 +Cflags: -I${includedir}/nix -std=c++20 diff --git a/src/libexpr/nix-expr.pc.in b/src/libexpr/nix-expr.pc.in index 80f7a492b..95d452ca8 100644 --- a/src/libexpr/nix-expr.pc.in +++ b/src/libexpr/nix-expr.pc.in @@ -7,4 +7,4 @@ Description: Nix Package Manager Version: @PACKAGE_VERSION@ Requires: nix-store bdw-gc Libs: -L${libdir} -lnixexpr -Cflags: -I${includedir}/nix -std=c++17 +Cflags: -I${includedir}/nix -std=c++20 diff --git a/src/libexpr/tests/error_traces.cc b/src/libexpr/tests/error_traces.cc index 5e2213f69..24e95ac39 100644 --- a/src/libexpr/tests/error_traces.cc +++ b/src/libexpr/tests/error_traces.cc @@ -1,7 +1,7 @@ #include <gmock/gmock.h> #include <gtest/gtest.h> -#include "libexprtests.hh" +#include "tests/libexpr.hh" namespace nix { diff --git a/src/libexpr/tests/json.cc b/src/libexpr/tests/json.cc index f1ea1b197..411bc0ac3 100644 --- a/src/libexpr/tests/json.cc +++ b/src/libexpr/tests/json.cc @@ -1,4 +1,4 @@ -#include "libexprtests.hh" +#include "tests/libexpr.hh" #include "value-to-json.hh" namespace nix { diff --git a/src/libexpr/tests/libexprtests.hh b/src/libexpr/tests/libexpr.hh index 03e468fbb..8534d9567 100644 --- a/src/libexpr/tests/libexprtests.hh +++ b/src/libexpr/tests/libexpr.hh @@ -7,18 +7,19 @@ #include "eval-inline.hh" #include "store-api.hh" +#include "tests/libstore.hh" namespace nix { - class LibExprTest : public ::testing::Test { + class LibExprTest : public LibStoreTest { public: static void SetUpTestSuite() { - initLibStore(); + LibStoreTest::SetUpTestSuite(); initGC(); } protected: LibExprTest() - : store(openStore("dummy://")) + : LibStoreTest() , state({}, store) { } @@ -36,7 +37,6 @@ namespace nix { return state.symbols.create(value); } - ref<Store> store; EvalState state; }; diff --git a/src/libexpr/tests/local.mk b/src/libexpr/tests/local.mk index e483575a4..3e5504f71 100644 --- a/src/libexpr/tests/local.mk +++ b/src/libexpr/tests/local.mk @@ -2,6 +2,8 @@ check: libexpr-tests_RUN programs += libexpr-tests +libexpr-tests_NAME := libnixexpr-tests + libexpr-tests_DIR := $(d) libexpr-tests_INSTALL_DIR := @@ -12,6 +14,6 @@ libexpr-tests_SOURCES := \ libexpr-tests_CXXFLAGS += -I src/libexpr -I src/libutil -I src/libstore -I src/libexpr/tests -libexpr-tests_LIBS = libexpr libutil libstore libfetchers +libexpr-tests_LIBS = libstore-tests libutils-tests libexpr libutil libstore libfetchers libexpr-tests_LDFLAGS := $(GTEST_LIBS) -lgmock diff --git a/src/libexpr/tests/primops.cc b/src/libexpr/tests/primops.cc index 9cdcf64a1..e1d3ac503 100644 --- a/src/libexpr/tests/primops.cc +++ b/src/libexpr/tests/primops.cc @@ -1,7 +1,7 @@ #include <gmock/gmock.h> #include <gtest/gtest.h> -#include "libexprtests.hh" +#include "tests/libexpr.hh" namespace nix { class CaptureLogger : public Logger diff --git a/src/libexpr/tests/trivial.cc b/src/libexpr/tests/trivial.cc index 8ce276e52..171727ac7 100644 --- a/src/libexpr/tests/trivial.cc +++ b/src/libexpr/tests/trivial.cc @@ -1,4 +1,4 @@ -#include "libexprtests.hh" +#include "tests/libexpr.hh" namespace nix { // Testing of trivial expressions diff --git a/src/libexpr/tests/value/context.cc b/src/libexpr/tests/value/context.cc index d5c9d3bce..083359b7a 100644 --- a/src/libexpr/tests/value/context.cc +++ b/src/libexpr/tests/value/context.cc @@ -1,6 +1,10 @@ -#include "value/context.hh" +#include <nlohmann/json.hpp> +#include <gtest/gtest.h> +#include <rapidcheck/gtest.h> -#include "libexprtests.hh" +#include "tests/path.hh" +#include "tests/libexpr.hh" +#include "tests/value/context.hh" namespace nix { @@ -70,3 +74,54 @@ TEST_F(NixStringContextElemTest, built) { } } + +namespace rc { +using namespace nix; + +Gen<NixStringContextElem::Opaque> Arbitrary<NixStringContextElem::Opaque>::arbitrary() +{ + return gen::just(NixStringContextElem::Opaque { + .path = *gen::arbitrary<StorePath>(), + }); +} + +Gen<NixStringContextElem::DrvDeep> Arbitrary<NixStringContextElem::DrvDeep>::arbitrary() +{ + return gen::just(NixStringContextElem::DrvDeep { + .drvPath = *gen::arbitrary<StorePath>(), + }); +} + +Gen<NixStringContextElem::Built> Arbitrary<NixStringContextElem::Built>::arbitrary() +{ + return gen::just(NixStringContextElem::Built { + .drvPath = *gen::arbitrary<StorePath>(), + .output = (*gen::arbitrary<StorePathName>()).name, + }); +} + +Gen<NixStringContextElem> Arbitrary<NixStringContextElem>::arbitrary() +{ + switch (*gen::inRange<uint8_t>(0, 2)) { + case 0: + return gen::just<NixStringContextElem>(*gen::arbitrary<NixStringContextElem::Opaque>()); + case 1: + return gen::just<NixStringContextElem>(*gen::arbitrary<NixStringContextElem::DrvDeep>()); + default: + return gen::just<NixStringContextElem>(*gen::arbitrary<NixStringContextElem::Built>()); + } +} + +} + +namespace nix { + +RC_GTEST_FIXTURE_PROP( + NixStringContextElemTest, + prop_round_rip, + (const NixStringContextElem & o)) +{ + RC_ASSERT(o == NixStringContextElem::parse(store(), o.to_string(store()))); +} + +} diff --git a/src/libexpr/tests/value/context.hh b/src/libexpr/tests/value/context.hh new file mode 100644 index 000000000..54d21760e --- /dev/null +++ b/src/libexpr/tests/value/context.hh @@ -0,0 +1,30 @@ +#pragma once + +#include <rapidcheck/gen/Arbitrary.h> + +#include <value/context.hh> + +namespace rc { +using namespace nix; + +template<> +struct Arbitrary<NixStringContextElem::Opaque> { + static Gen<NixStringContextElem::Opaque> arbitrary(); +}; + +template<> +struct Arbitrary<NixStringContextElem::Built> { + static Gen<NixStringContextElem::Built> arbitrary(); +}; + +template<> +struct Arbitrary<NixStringContextElem::DrvDeep> { + static Gen<NixStringContextElem::DrvDeep> arbitrary(); +}; + +template<> +struct Arbitrary<NixStringContextElem> { + static Gen<NixStringContextElem> arbitrary(); +}; + +} diff --git a/src/libexpr/value/context.hh b/src/libexpr/value/context.hh index d8008c436..721563cba 100644 --- a/src/libexpr/value/context.hh +++ b/src/libexpr/value/context.hh @@ -1,9 +1,10 @@ #pragma once #include "util.hh" +#include "comparator.hh" #include "path.hh" -#include <optional> +#include <variant> #include <nlohmann/json_fwd.hpp> @@ -31,7 +32,9 @@ class Store; Encoded as just the path: ‘<path>’. */ struct NixStringContextElem_Opaque { - StorePath path; + StorePath path; + + GENERATE_CMP(NixStringContextElem_Opaque, me->path); }; /* Path to a derivation and its entire build closure. @@ -43,7 +46,9 @@ struct NixStringContextElem_Opaque { Encoded in the form ‘=<drvPath>’. */ struct NixStringContextElem_DrvDeep { - StorePath drvPath; + StorePath drvPath; + + GENERATE_CMP(NixStringContextElem_DrvDeep, me->drvPath); }; /* Derivation output. @@ -51,8 +56,10 @@ struct NixStringContextElem_DrvDeep { Encoded in the form ‘!<output>!<drvPath>’. */ struct NixStringContextElem_Built { - StorePath drvPath; - std::string output; + StorePath drvPath; + std::string output; + + GENERATE_CMP(NixStringContextElem_Built, me->drvPath, me->output); }; using _NixStringContextElem_Raw = std::variant< diff --git a/src/libmain/nix-main.pc.in b/src/libmain/nix-main.pc.in index 37b03dcd4..b46ce1990 100644 --- a/src/libmain/nix-main.pc.in +++ b/src/libmain/nix-main.pc.in @@ -6,4 +6,4 @@ Name: Nix Description: Nix Package Manager Version: @PACKAGE_VERSION@ Libs: -L${libdir} -lnixmain -Cflags: -I${includedir}/nix -std=c++17 +Cflags: -I${includedir}/nix -std=c++20 diff --git a/src/libstore/binary-cache-store.cc b/src/libstore/binary-cache-store.cc index 05bf0501d..751cf8c30 100644 --- a/src/libstore/binary-cache-store.cc +++ b/src/libstore/binary-cache-store.cc @@ -370,7 +370,7 @@ void BinaryCacheStore::queryPathInfoUncached(const StorePath & storePath, auto callbackPtr = std::make_shared<decltype(callback)>(std::move(callback)); getFile(narInfoFile, - {[=](std::future<std::optional<std::string>> fut) { + {[=,this](std::future<std::optional<std::string>> fut) { try { auto data = fut.get(); diff --git a/src/libstore/build/worker.cc b/src/libstore/build/worker.cc index b94fb8416..f775f8486 100644 --- a/src/libstore/build/worker.cc +++ b/src/libstore/build/worker.cc @@ -276,7 +276,7 @@ void Worker::run(const Goals & _topGoals) if (!children.empty() || !waitingForAWhile.empty()) waitForInput(); else { - if (awake.empty() && 0 == settings.maxBuildJobs) + if (awake.empty() && 0U == settings.maxBuildJobs) { if (getMachines().empty()) throw Error("unable to start any build; either increase '--max-jobs' " diff --git a/src/libstore/derived-path.hh b/src/libstore/derived-path.hh index 4edff7467..9e0cce377 100644 --- a/src/libstore/derived-path.hh +++ b/src/libstore/derived-path.hh @@ -4,8 +4,9 @@ #include "path.hh" #include "realisation.hh" #include "outputs-spec.hh" +#include "comparator.hh" -#include <optional> +#include <variant> #include <nlohmann/json_fwd.hpp> @@ -27,8 +28,7 @@ struct DerivedPathOpaque { std::string to_string(const Store & store) const; static DerivedPathOpaque parse(const Store & store, std::string_view); - bool operator < (const DerivedPathOpaque & b) const - { return path < b.path; } + GENERATE_CMP(DerivedPathOpaque, me->path); }; /** @@ -51,8 +51,7 @@ struct DerivedPathBuilt { static DerivedPathBuilt parse(const Store & store, std::string_view, std::string_view); nlohmann::json toJSON(ref<Store> store) const; - bool operator < (const DerivedPathBuilt & b) const - { return std::make_pair(drvPath, outputs) < std::make_pair(b.drvPath, b.outputs); } + GENERATE_CMP(DerivedPathBuilt, me->drvPath, me->outputs); }; using _DerivedPathRaw = std::variant< @@ -96,6 +95,8 @@ struct BuiltPathBuilt { nlohmann::json toJSON(ref<Store> store) const; static BuiltPathBuilt parse(const Store & store, std::string_view); + + GENERATE_CMP(BuiltPathBuilt, me->drvPath, me->outputs); }; using _BuiltPathRaw = std::variant< diff --git a/src/libstore/globals.cc b/src/libstore/globals.cc index 130c5b670..8e33a3dec 100644 --- a/src/libstore/globals.cc +++ b/src/libstore/globals.cc @@ -222,19 +222,19 @@ template<> void BaseSetting<SandboxMode>::convertToArg(Args & args, const std::s .longName = name, .description = "Enable sandboxing.", .category = category, - .handler = {[=]() { override(smEnabled); }} + .handler = {[this]() { override(smEnabled); }} }); args.addFlag({ .longName = "no-" + name, .description = "Disable sandboxing.", .category = category, - .handler = {[=]() { override(smDisabled); }} + .handler = {[this]() { override(smDisabled); }} }); args.addFlag({ .longName = "relaxed-" + name, .description = "Enable sandboxing, but allow builds to disable it.", .category = category, - .handler = {[=]() { override(smRelaxed); }} + .handler = {[this]() { override(smRelaxed); }} }); } diff --git a/src/libstore/legacy-ssh-store.cc b/src/libstore/legacy-ssh-store.cc index e1a4e13a3..2c9dd2680 100644 --- a/src/libstore/legacy-ssh-store.cc +++ b/src/libstore/legacy-ssh-store.cc @@ -134,7 +134,6 @@ struct LegacySSHStore : public virtual LegacySSHStoreConfig, public virtual Stor /* Hash will be set below. FIXME construct ValidPathInfo at end. */ auto info = std::make_shared<ValidPathInfo>(path, Hash::dummy); - PathSet references; auto deriver = readString(conn->from); if (deriver != "") info->deriver = parseStorePath(deriver); diff --git a/src/libstore/nix-store.pc.in b/src/libstore/nix-store.pc.in index 6d67b1e03..385169a13 100644 --- a/src/libstore/nix-store.pc.in +++ b/src/libstore/nix-store.pc.in @@ -6,4 +6,4 @@ Name: Nix Description: Nix Package Manager Version: @PACKAGE_VERSION@ Libs: -L${libdir} -lnixstore -lnixutil -Cflags: -I${includedir}/nix -std=c++17 +Cflags: -I${includedir}/nix -std=c++20 diff --git a/src/libstore/path-info.hh b/src/libstore/path-info.hh index b4b54e593..a7fcbd232 100644 --- a/src/libstore/path-info.hh +++ b/src/libstore/path-info.hh @@ -81,12 +81,6 @@ struct ValidPathInfo /* Return true iff the path is verifiably content-addressed. */ bool isContentAddressed(const Store & store) const; - /* Functions to view references + hasSelfReference as one set, mainly for - compatibility's sake. */ - StorePathSet referencesPossiblyToSelf() const; - void insertReferencePossiblyToSelf(StorePath && ref); - void setReferencesPossiblyToSelf(StorePathSet && refs); - static const size_t maxSigs = std::numeric_limits<size_t>::max(); /* Return the number of signatures on this .narinfo that were diff --git a/src/libstore/tests/derived-path.cc b/src/libstore/tests/derived-path.cc new file mode 100644 index 000000000..d1ac2c5e7 --- /dev/null +++ b/src/libstore/tests/derived-path.cc @@ -0,0 +1,62 @@ +#include <regex> + +#include <nlohmann/json.hpp> +#include <gtest/gtest.h> +#include <rapidcheck/gtest.h> + +#include "tests/derived-path.hh" +#include "tests/libstore.hh" + +namespace rc { +using namespace nix; + +Gen<DerivedPath::Opaque> Arbitrary<DerivedPath::Opaque>::arbitrary() +{ + return gen::just(DerivedPath::Opaque { + .path = *gen::arbitrary<StorePath>(), + }); +} + +Gen<DerivedPath::Built> Arbitrary<DerivedPath::Built>::arbitrary() +{ + return gen::just(DerivedPath::Built { + .drvPath = *gen::arbitrary<StorePath>(), + .outputs = *gen::arbitrary<OutputsSpec>(), + }); +} + +Gen<DerivedPath> Arbitrary<DerivedPath>::arbitrary() +{ + switch (*gen::inRange<uint8_t>(0, 1)) { + case 0: + return gen::just<DerivedPath>(*gen::arbitrary<DerivedPath::Opaque>()); + default: + return gen::just<DerivedPath>(*gen::arbitrary<DerivedPath::Built>()); + } +} + +} + +namespace nix { + +class DerivedPathTest : public LibStoreTest +{ +}; + +// FIXME: `RC_GTEST_FIXTURE_PROP` isn't calling `SetUpTestSuite` because it is +// no a real fixture. +// +// See https://github.com/emil-e/rapidcheck/blob/master/doc/gtest.md#rc_gtest_fixture_propfixture-name-args +TEST_F(DerivedPathTest, force_init) +{ +} + +RC_GTEST_FIXTURE_PROP( + DerivedPathTest, + prop_round_rip, + (const DerivedPath & o)) +{ + RC_ASSERT(o == DerivedPath::parse(*store, o.to_string(*store))); +} + +} diff --git a/src/libstore/tests/derived-path.hh b/src/libstore/tests/derived-path.hh new file mode 100644 index 000000000..3bc812440 --- /dev/null +++ b/src/libstore/tests/derived-path.hh @@ -0,0 +1,28 @@ +#pragma once + +#include <rapidcheck/gen/Arbitrary.h> + +#include <derived-path.hh> + +#include "tests/path.hh" +#include "tests/outputs-spec.hh" + +namespace rc { +using namespace nix; + +template<> +struct Arbitrary<DerivedPath::Opaque> { + static Gen<DerivedPath::Opaque> arbitrary(); +}; + +template<> +struct Arbitrary<DerivedPath::Built> { + static Gen<DerivedPath::Built> arbitrary(); +}; + +template<> +struct Arbitrary<DerivedPath> { + static Gen<DerivedPath> arbitrary(); +}; + +} diff --git a/src/libstore/tests/libstoretests.hh b/src/libstore/tests/libstore.hh index 05397659b..05397659b 100644 --- a/src/libstore/tests/libstoretests.hh +++ b/src/libstore/tests/libstore.hh diff --git a/src/libstore/tests/local.mk b/src/libstore/tests/local.mk index a2cf8a0cf..03becc7d1 100644 --- a/src/libstore/tests/local.mk +++ b/src/libstore/tests/local.mk @@ -1,6 +1,20 @@ -check: libstore-tests_RUN +check: libstore-tests-exe_RUN -programs += libstore-tests +programs += libstore-tests-exe + +libstore-tests-exe_NAME = libnixstore-tests + +libstore-tests-exe_DIR := $(d) + +libstore-tests-exe_INSTALL_DIR := + +libstore-tests-exe_LIBS = libstore-tests + +libstore-tests-exe_LDFLAGS := $(GTEST_LIBS) + +libraries += libstore-tests + +libstore-tests_NAME = libnixstore-tests libstore-tests_DIR := $(d) @@ -10,6 +24,6 @@ libstore-tests_SOURCES := $(wildcard $(d)/*.cc) libstore-tests_CXXFLAGS += -I src/libstore -I src/libutil -libstore-tests_LIBS = libstore libutil +libstore-tests_LIBS = libutil-tests libstore libutil libstore-tests_LDFLAGS := -lrapidcheck $(GTEST_LIBS) diff --git a/src/libstore/tests/outputs-spec.cc b/src/libstore/tests/outputs-spec.cc index 06e4cabbd..984d1d963 100644 --- a/src/libstore/tests/outputs-spec.cc +++ b/src/libstore/tests/outputs-spec.cc @@ -2,6 +2,7 @@ #include <nlohmann/json.hpp> #include <gtest/gtest.h> +#include <rapidcheck/gtest.h> namespace nix { @@ -199,3 +200,34 @@ TEST_JSON(ExtendedOutputsSpec, names, R"(["a","b"])", (ExtendedOutputsSpec::Expl #undef TEST_JSON } + +namespace rc { +using namespace nix; + +Gen<OutputsSpec> Arbitrary<OutputsSpec>::arbitrary() +{ + switch (*gen::inRange<uint8_t>(0, 1)) { + case 0: + return gen::just((OutputsSpec) OutputsSpec::All { }); + default: + return gen::just((OutputsSpec) OutputsSpec::Names { + *gen::nonEmpty(gen::container<StringSet>(gen::map( + gen::arbitrary<StorePathName>(), + [](StorePathName n) { return n.name; }))), + }); + } +} + +} + +namespace nix { + +RC_GTEST_PROP( + OutputsSpec, + prop_round_rip, + (const OutputsSpec & o)) +{ + RC_ASSERT(o == OutputsSpec::parse(o.to_string())); +} + +} diff --git a/src/libstore/tests/outputs-spec.hh b/src/libstore/tests/outputs-spec.hh new file mode 100644 index 000000000..2d455c817 --- /dev/null +++ b/src/libstore/tests/outputs-spec.hh @@ -0,0 +1,17 @@ +#pragma once + +#include <rapidcheck/gen/Arbitrary.h> + +#include <outputs-spec.hh> + +#include <tests/path.hh> + +namespace rc { +using namespace nix; + +template<> +struct Arbitrary<OutputsSpec> { + static Gen<OutputsSpec> arbitrary(); +}; + +} diff --git a/src/libstore/tests/path.cc b/src/libstore/tests/path.cc index 8ea252c92..430aa0099 100644 --- a/src/libstore/tests/path.cc +++ b/src/libstore/tests/path.cc @@ -7,7 +7,9 @@ #include "path-regex.hh" #include "store-api.hh" -#include "libstoretests.hh" +#include "tests/hash.hh" +#include "tests/libstore.hh" +#include "tests/path.hh" namespace nix { @@ -73,17 +75,14 @@ void showValue(const StorePath & p, std::ostream & os) { namespace rc { using namespace nix; -template<> -struct Arbitrary<StorePath> { - static Gen<StorePath> arbitrary(); -}; - -Gen<StorePath> Arbitrary<StorePath>::arbitrary() +Gen<StorePathName> Arbitrary<StorePathName>::arbitrary() { - auto len = *gen::inRange<size_t>(1, StorePath::MaxPathLen); + auto len = *gen::inRange<size_t>( + 1, + StorePath::MaxPathLen - std::string_view { HASH_PART }.size()); - std::string pre { HASH_PART "-" }; - pre.reserve(pre.size() + len); + std::string pre; + pre.reserve(len); for (size_t c = 0; c < len; ++c) { switch (auto i = *gen::inRange<uint8_t>(0, 10 + 2 * 26 + 6)) { @@ -118,7 +117,17 @@ Gen<StorePath> Arbitrary<StorePath>::arbitrary() } } - return gen::just(StorePath { pre }); + return gen::just(StorePathName { + .name = std::move(pre), + }); +} + +Gen<StorePath> Arbitrary<StorePath>::arbitrary() +{ + return gen::just(StorePath { + *gen::arbitrary<Hash>(), + (*gen::arbitrary<StorePathName>()).name, + }); } } // namespace rc diff --git a/src/libstore/tests/path.hh b/src/libstore/tests/path.hh new file mode 100644 index 000000000..d7f1a8988 --- /dev/null +++ b/src/libstore/tests/path.hh @@ -0,0 +1,28 @@ +#pragma once + +#include <rapidcheck/gen/Arbitrary.h> + +#include <path.hh> + +namespace nix { + +struct StorePathName { + std::string name; +}; + +} + +namespace rc { +using namespace nix; + +template<> +struct Arbitrary<StorePathName> { + static Gen<StorePathName> arbitrary(); +}; + +template<> +struct Arbitrary<StorePath> { + static Gen<StorePath> arbitrary(); +}; + +} diff --git a/src/libutil/args.cc b/src/libutil/args.cc index 753980fd4..2930913d6 100644 --- a/src/libutil/args.cc +++ b/src/libutil/args.cc @@ -324,7 +324,7 @@ MultiCommand::MultiCommand(const Commands & commands_) expectArgs({ .label = "subcommand", .optional = true, - .handler = {[=](std::string s) { + .handler = {[=,this](std::string s) { assert(!command); auto i = commands.find(s); if (i == commands.end()) { diff --git a/src/libutil/config.cc b/src/libutil/config.cc index 9bb412b4f..b349f2d80 100644 --- a/src/libutil/config.cc +++ b/src/libutil/config.cc @@ -209,7 +209,7 @@ void BaseSetting<T>::convertToArg(Args & args, const std::string & category) .description = fmt("Set the `%s` setting.", name), .category = category, .labels = {"value"}, - .handler = {[=](std::string s) { overridden = true; set(s); }}, + .handler = {[this](std::string s) { overridden = true; set(s); }}, }); if (isAppendable()) @@ -218,7 +218,7 @@ void BaseSetting<T>::convertToArg(Args & args, const std::string & category) .description = fmt("Append to the `%s` setting.", name), .category = category, .labels = {"value"}, - .handler = {[=](std::string s) { overridden = true; set(s, true); }}, + .handler = {[this](std::string s) { overridden = true; set(s, true); }}, }); } @@ -270,13 +270,13 @@ template<> void BaseSetting<bool>::convertToArg(Args & args, const std::string & .longName = name, .description = fmt("Enable the `%s` setting.", name), .category = category, - .handler = {[=]() { override(true); }} + .handler = {[this]() { override(true); }} }); args.addFlag({ .longName = "no-" + name, .description = fmt("Disable the `%s` setting.", name), .category = category, - .handler = {[=]() { override(false); }} + .handler = {[this]() { override(false); }} }); } diff --git a/src/libutil/config.hh b/src/libutil/config.hh index 79ec0f9cf..7ac43c854 100644 --- a/src/libutil/config.hh +++ b/src/libutil/config.hh @@ -250,11 +250,15 @@ public: operator const T &() const { return value; } operator T &() { return value; } const T & get() const { return value; } - bool operator ==(const T & v2) const { return value == v2; } - bool operator !=(const T & v2) const { return value != v2; } - void operator =(const T & v) { assign(v); } + template<typename U> + bool operator ==(const U & v2) const { return value == v2; } + template<typename U> + bool operator !=(const U & v2) const { return value != v2; } + template<typename U> + void operator =(const U & v) { assign(v); } virtual void assign(const T & v) { value = v; } - void setDefault(const T & v) { if (!overridden) value = v; } + template<typename U> + void setDefault(const U & v) { if (!overridden) value = v; } void set(const std::string & str, bool append = false) override; diff --git a/src/libutil/tests/hash.cc b/src/libutil/tests/hash.cc index 412c03030..e4e928b3b 100644 --- a/src/libutil/tests/hash.cc +++ b/src/libutil/tests/hash.cc @@ -1,5 +1,12 @@ -#include "hash.hh" +#include <regex> + +#include <nlohmann/json.hpp> #include <gtest/gtest.h> +#include <rapidcheck/gtest.h> + +#include <hash.hh> + +#include "tests/hash.hh" namespace nix { @@ -73,3 +80,16 @@ namespace nix { "c7d329eeb6dd26545e96e55b874be909"); } } + +namespace rc { +using namespace nix; + +Gen<Hash> Arbitrary<Hash>::arbitrary() +{ + Hash hash(htSHA1); + for (size_t i = 0; i < hash.hashSize; ++i) + hash.hash[i] = *gen::arbitrary<uint8_t>(); + return gen::just(hash); +} + +} diff --git a/src/libutil/tests/hash.hh b/src/libutil/tests/hash.hh new file mode 100644 index 000000000..9e9650e6e --- /dev/null +++ b/src/libutil/tests/hash.hh @@ -0,0 +1,15 @@ +#pragma once + +#include <rapidcheck/gen/Arbitrary.h> + +#include <hash.hh> + +namespace rc { +using namespace nix; + +template<> +struct Arbitrary<Hash> { + static Gen<Hash> arbitrary(); +}; + +} diff --git a/src/libutil/tests/local.mk b/src/libutil/tests/local.mk index 815e18560..167915439 100644 --- a/src/libutil/tests/local.mk +++ b/src/libutil/tests/local.mk @@ -2,14 +2,28 @@ check: libutil-tests_RUN programs += libutil-tests +libutil-tests-exe_NAME = libnixutil-tests + +libutil-tests-exe_DIR := $(d) + +libutil-tests-exe_INSTALL_DIR := + +libutil-tests-exe_LIBS = libutil-tests + +libutil-tests-exe_LDFLAGS := $(GTEST_LIBS) + +libraries += libutil-tests + +libutil-tests_NAME = libnixutil-tests + libutil-tests_DIR := $(d) libutil-tests_INSTALL_DIR := libutil-tests_SOURCES := $(wildcard $(d)/*.cc) -libutil-tests_CXXFLAGS += -I src/libutil -I src/libexpr +libutil-tests_CXXFLAGS += -I src/libutil libutil-tests_LIBS = libutil -libutil-tests_LDFLAGS := $(GTEST_LIBS) +libutil-tests_LDFLAGS := -lrapidcheck $(GTEST_LIBS) diff --git a/src/nix/flake.cc b/src/nix/flake.cc index d2f68c37c..c025bc7a6 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -1002,6 +1002,61 @@ struct CmdFlakeShow : FlakeCommand, MixJSON auto flake = std::make_shared<LockedFlake>(lockFlake()); auto localSystem = std::string(settings.thisSystem.get()); + std::function<bool( + eval_cache::AttrCursor & visitor, + const std::vector<Symbol> &attrPath, + const Symbol &attr)> hasContent; + + // For frameworks it's important that structures are as lazy as possible + // to prevent infinite recursions, performance issues and errors that + // aren't related to the thing to evaluate. As a consequence, they have + // to emit more attributes than strictly (sic) necessary. + // However, these attributes with empty values are not useful to the user + // so we omit them. + hasContent = [&]( + eval_cache::AttrCursor & visitor, + const std::vector<Symbol> &attrPath, + const Symbol &attr) -> bool + { + auto attrPath2(attrPath); + attrPath2.push_back(attr); + auto attrPathS = state->symbols.resolve(attrPath2); + const auto & attrName = state->symbols[attr]; + + auto visitor2 = visitor.getAttr(attrName); + + if ((attrPathS[0] == "apps" + || attrPathS[0] == "checks" + || attrPathS[0] == "devShells" + || attrPathS[0] == "legacyPackages" + || attrPathS[0] == "packages") + && (attrPathS.size() == 1 || attrPathS.size() == 2)) { + for (const auto &subAttr : visitor2->getAttrs()) { + if (hasContent(*visitor2, attrPath2, subAttr)) { + return true; + } + } + return false; + } + + if ((attrPathS.size() == 1) + && (attrPathS[0] == "formatter" + || attrPathS[0] == "nixosConfigurations" + || attrPathS[0] == "nixosModules" + || attrPathS[0] == "overlays" + )) { + for (const auto &subAttr : visitor2->getAttrs()) { + if (hasContent(*visitor2, attrPath2, subAttr)) { + return true; + } + } + return false; + } + + // If we don't recognize it, it's probably content + return true; + }; + std::function<nlohmann::json( eval_cache::AttrCursor & visitor, const std::vector<Symbol> & attrPath, @@ -1027,7 +1082,12 @@ struct CmdFlakeShow : FlakeCommand, MixJSON { if (!json) logger->cout("%s", headerPrefix); - auto attrs = visitor.getAttrs(); + std::vector<Symbol> attrs; + for (const auto &attr : visitor.getAttrs()) { + if (hasContent(visitor, attrPath, attr)) + attrs.push_back(attr); + } + for (const auto & [i, attr] : enumerate(attrs)) { const auto & attrName = state->symbols[attr]; bool last = i + 1 == attrs.size(); diff --git a/src/nix/search.cc b/src/nix/search.cc index d2a31607d..4fa1e7837 100644 --- a/src/nix/search.cc +++ b/src/nix/search.cc @@ -56,8 +56,8 @@ struct CmdSearch : InstallableCommand, MixJSON Strings getDefaultFlakeAttrPaths() override { return { - "packages." + settings.thisSystem.get() + ".", - "legacyPackages." + settings.thisSystem.get() + "." + "packages." + settings.thisSystem.get(), + "legacyPackages." + settings.thisSystem.get() }; } diff --git a/tests/flakes/show.sh b/tests/flakes/show.sh index 995de8dc3..dd13264b9 100644 --- a/tests/flakes/show.sh +++ b/tests/flakes/show.sh @@ -37,3 +37,30 @@ in assert show_output.legacyPackages.${builtins.currentSystem}.hello.name == "simple"; true ' + +# Test that attributes are only reported when they have actual content +cat >flake.nix <<EOF +{ + description = "Bla bla"; + + outputs = inputs: rec { + apps.$system = { }; + checks.$system = { }; + devShells.$system = { }; + legacyPackages.$system = { }; + packages.$system = { }; + packages.someOtherSystem = { }; + + formatter = { }; + nixosConfigurations = { }; + nixosModules = { }; + }; +} +EOF +nix flake show --json --all-systems > show-output.json +nix eval --impure --expr ' +let show_output = builtins.fromJSON (builtins.readFile ./show-output.json); +in +assert show_output == { }; +true +' |