From b2752a4f74bf2697ed9be4e9cb913de03b1b1660 Mon Sep 17 00:00:00 2001 From: Taeer Bar-Yam Date: Mon, 16 Jan 2023 10:37:04 -0500 Subject: add comments --- src/nix-store/nix-store.cc | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src') diff --git a/src/nix-store/nix-store.cc b/src/nix-store/nix-store.cc index 3bbefedbe..6b79c373f 100644 --- a/src/nix-store/nix-store.cc +++ b/src/nix-store/nix-store.cc @@ -72,11 +72,13 @@ static PathSet realisePath(StorePathWithOutputs path, bool build = true) Derivation drv = store->derivationFromPath(path.path); rootNr++; + /* FIXME: Encode this empty special case explicitly in the type. */ if (path.outputs.empty()) for (auto & i : drv.outputs) path.outputs.insert(i.first); PathSet outputs; for (auto & j : path.outputs) { + /* Match outputs of a store path with outputs of the derivation that produces it. */ DerivationOutputs::iterator i = drv.outputs.find(j); if (i == drv.outputs.end()) throw Error("derivation '%s' does not have an output named '%s'", @@ -141,6 +143,7 @@ static void opRealise(Strings opFlags, Strings opArgs) toDerivedPaths(paths), willBuild, willSubstitute, unknown, downloadSize, narSize); + /* Filter out unknown paths from `paths`. */ if (ignoreUnknown) { std::vector paths2; for (auto & i : paths) -- cgit v1.2.3 From b31d4b689ca9f82e1c4060ea41e92691d5c94ee8 Mon Sep 17 00:00:00 2001 From: aameen-tulip <98349328+aameen-tulip@users.noreply.github.com> Date: Thu, 16 Feb 2023 18:47:45 -0600 Subject: Document `hasAllInfo` If this documentation is inaccurate in any way please do not hesitate to suggest corrections. My understanding of this function is strictly from reading the source code and some limited experience implementing fetchers. --- src/libfetchers/fetchers.hh | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'src') diff --git a/src/libfetchers/fetchers.hh b/src/libfetchers/fetchers.hh index 17da37f47..95c0f5974 100644 --- a/src/libfetchers/fetchers.hh +++ b/src/libfetchers/fetchers.hh @@ -63,6 +63,11 @@ public: one that contains a commit hash or content hash. */ bool isLocked() const { return locked; } + /* Check whether the input carries all necessary info required + for cache insertion and substitution. + These fields are used to uniquely identify cached trees + within the "tarball TTL" window without necessarily + indicating that the input's origin is unchanged. */ bool hasAllInfo() const; bool operator ==(const Input & other) const; -- cgit v1.2.3 From 3113b13df9afaab918792c725742c6bc3fcec88b Mon Sep 17 00:00:00 2001 From: Bob van der Linden Date: Wed, 8 Feb 2023 20:03:57 +0100 Subject: buildenv: throw BuildEnvFileConflictError with more context At the moment an Error is thrown that only holds an error message regarding `nix-env` and `nix profile`. These tools make use of builtins.buildEnv, but buildEnv is also used in other places. These places are unrelated to Nix profiles, so the error shouldn't mention these tools. This generic error is now BuildEnvFileConflictError, which holds more contextual information about the files that were conflicting while building the environment. --- src/libstore/builtins/buildenv.cc | 12 +++++------- src/libstore/builtins/buildenv.hh | 26 ++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/libstore/builtins/buildenv.cc b/src/libstore/builtins/buildenv.cc index b1fbda13d..7bba33fb9 100644 --- a/src/libstore/builtins/buildenv.cc +++ b/src/libstore/builtins/buildenv.cc @@ -92,13 +92,11 @@ static void createLinks(State & state, const Path & srcDir, const Path & dstDir, if (S_ISLNK(dstSt.st_mode)) { auto prevPriority = state.priorities[dstFile]; if (prevPriority == priority) - throw Error( - "files '%1%' and '%2%' have the same priority %3%; " - "use 'nix-env --set-flag priority NUMBER INSTALLED_PKGNAME' " - "or type 'nix profile install --help' if using 'nix profile' to find out how " - "to change the priority of one of the conflicting packages" - " (0 being the highest priority)", - srcFile, readLink(dstFile), priority); + throw BuildEnvFileConflictError( + readLink(dstFile), + srcFile, + priority + ); if (prevPriority < priority) continue; if (unlink(dstFile.c_str()) == -1) diff --git a/src/libstore/builtins/buildenv.hh b/src/libstore/builtins/buildenv.hh index 73c0f5f7f..a018de3af 100644 --- a/src/libstore/builtins/buildenv.hh +++ b/src/libstore/builtins/buildenv.hh @@ -12,6 +12,32 @@ struct Package { Package(const Path & path, bool active, int priority) : path{path}, active{active}, priority{priority} {} }; +class BuildEnvFileConflictError : public Error +{ +public: + const Path fileA; + const Path fileB; + int priority; + + BuildEnvFileConflictError( + const Path fileA, + const Path fileB, + int priority + ) + : Error( + "Unable to build profile. There is a conflict for the following files:\n" + "\n" + " %1%\n" + " %2%", + fileA, + fileB + ) + , fileA(fileA) + , fileB(fileB) + , priority(priority) + {} +}; + typedef std::vector Packages; void buildProfile(const Path & out, Packages && pkgs); -- cgit v1.2.3 From 872cdb4346f72186ae683f90ac97b8a7d9bddfd4 Mon Sep 17 00:00:00 2001 From: Bob van der Linden Date: Fri, 10 Feb 2023 14:18:27 +0100 Subject: nix-profile-install: show helpful error upon package conflict Whenever a file conflict happens during "nix profile install" an error is shown that was previously thrown inside builtins.buildEnv. We catch BuildProfileConflictError here so that we can provide the user with more useful instructions on what to do next. Most notably, we give the user concrete commands to use with all parameters already filled in. This avoids the need for the user to look up these commands in manual pages. --- src/nix/profile.cc | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nix/profile.cc b/src/nix/profile.cc index 32364e720..3464a977d 100644 --- a/src/nix/profile.cc +++ b/src/nix/profile.cc @@ -329,7 +329,61 @@ struct CmdProfileInstall : InstallablesCommand, MixDefaultProfile manifest.elements.push_back(std::move(element)); } - updateProfile(manifest.build(store)); + try { + updateProfile(manifest.build(store)); + } catch (BuildEnvFileConflictError & conflictError) { + auto findRefByFilePath = [&](Iterator begin, Iterator end) { + for (auto it = begin; it != end; it++) { + auto profileElement = *it; + for (auto & storePath : profileElement.storePaths) { + if (conflictError.fileA.starts_with(store->printStorePath(storePath))) { + return std::pair(conflictError.fileA, profileElement.source->originalRef); + } + if (conflictError.fileB.starts_with(store->printStorePath(storePath))) { + return std::pair(conflictError.fileB, profileElement.source->originalRef); + } + } + } + throw conflictError; + }; + // There are 2 conflicting files. We need to find out which one is from the already installed package and + // which one is the package that is the new package that is being installed. + // The first matching package is the one that was already installed (original). + auto [originalConflictingFilePath, originalConflictingRef] = findRefByFilePath(manifest.elements.begin(), manifest.elements.end()); + // The last matching package is the one that was going to be installed (new). + auto [newConflictingFilePath, newConflictingRef] = findRefByFilePath(manifest.elements.rbegin(), manifest.elements.rend()); + + throw Error( + "An existing package already provides the following file:\n" + "\n" + " %1%\n" + "\n" + "This is the conflicting file from the new package:\n" + "\n" + " %2%\n" + "\n" + "To remove the existing package:\n" + "\n" + " nix profile remove %3%\n" + "\n" + "The new package can also be installed next to the existing one by assigning a different priority.\n" + "The conflicting packages have a priority of %5%.\n" + "To prioritise the new package:\n" + "\n" + " nix profile install %4% --priority %6%\n" + "\n" + "To prioritise the existing package:\n" + "\n" + " nix profile install %4% --priority %7%\n", + originalConflictingFilePath, + newConflictingFilePath, + originalConflictingRef.to_string(), + newConflictingRef.to_string(), + conflictError.priority, + conflictError.priority - 1, + conflictError.priority + 1 + ); + } } }; -- cgit v1.2.3 From 269caa531729524915761f8a71e47e59e6a8598d Mon Sep 17 00:00:00 2001 From: Timothy DeHerrera Date: Thu, 12 Jan 2023 12:35:22 -0700 Subject: feat: read installable paths from stdin Resolves #7437 for new `nix` commands only by adding a `--stdin` flag. If paths are also passed on the cli they will be combined with the ones from standard input. --- src/libcmd/command.hh | 2 ++ src/libcmd/installables.cc | 17 ++++++++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/libcmd/command.hh b/src/libcmd/command.hh index b6d554aab..49c7b4f9b 100644 --- a/src/libcmd/command.hh +++ b/src/libcmd/command.hh @@ -128,6 +128,8 @@ struct InstallablesCommand : virtual Args, SourceExprCommand virtual bool useDefaultInstallables() { return true; } + bool readFromStdIn; + std::vector getFlakesForCompletion() override; protected: diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc index 00c6f9516..a67841bb6 100644 --- a/src/libcmd/installables.cc +++ b/src/libcmd/installables.cc @@ -691,6 +691,13 @@ StorePathSet Installable::toDerivations( InstallablesCommand::InstallablesCommand() { + + addFlag({ + .longName = "stdin", + .description = "Read installables from the standard input.", + .handler = {&readFromStdIn, true} + }); + expectArgs({ .label = "installables", .handler = {&_installables}, @@ -707,10 +714,18 @@ void InstallablesCommand::prepare() Installables InstallablesCommand::load() { - if (_installables.empty() && useDefaultInstallables()) + if (_installables.empty() && useDefaultInstallables() && !readFromStdIn) // FIXME: commands like "nix profile install" should not have a // default, probably. _installables.push_back("."); + + if (readFromStdIn && !isatty(STDIN_FILENO)) { + std::string word; + while (std::cin >> word) { + _installables.emplace_back(std::move(word)); + } + } + return parseInstallables(getStore(), _installables); } -- cgit v1.2.3 From df643051e282465b73fc29e3f223b4fd497137d7 Mon Sep 17 00:00:00 2001 From: Timothy DeHerrera Date: Thu, 12 Jan 2023 12:45:45 -0700 Subject: nix-store: read paths from standard input Resolves #7437 for new `nix-store` by adding a `--stdin` flag. --- src/nix-store/nix-store.cc | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'src') diff --git a/src/nix-store/nix-store.cc b/src/nix-store/nix-store.cc index 3bbefedbe..d4218550a 100644 --- a/src/nix-store/nix-store.cc +++ b/src/nix-store/nix-store.cc @@ -1020,6 +1020,7 @@ static int main_nix_store(int argc, char * * argv) { Strings opFlags, opArgs; Operation op = 0; + bool readFromStdIn; parseCmdLine(argc, argv, [&](Strings::iterator & arg, const Strings::iterator & end) { Operation oldOp = op; @@ -1078,6 +1079,8 @@ static int main_nix_store(int argc, char * * argv) op = opGenerateBinaryCacheKey; else if (*arg == "--add-root") gcRoot = absPath(getArg(*arg, arg, end)); + else if (*arg == "--stdin" && !isatty(STDIN_FILENO)) + readFromStdIn = true; else if (*arg == "--indirect") ; else if (*arg == "--no-output") @@ -1090,6 +1093,13 @@ static int main_nix_store(int argc, char * * argv) else opArgs.push_back(*arg); + if (readFromStdIn && op != opImport && op != opRestore && op != opServe) { + std::string word; + while (std::cin >> word) { + opArgs.emplace_back(std::move(word)); + }; + } + if (oldOp && oldOp != op) throw UsageError("only one operation may be specified"); -- cgit v1.2.3 From ea0adfc582e8c0d233cec03da9258caf718678b7 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 15 Dec 2022 22:44:14 -0500 Subject: Get rid of `.drv` special-casing for store path installables The release notes document the change in behavior, I don't include it here so there is no risk to it getting out of sync. > Motivation >> Plumbing CLI should be simple Store derivation installations are intended as "plumbing": very simple utilities for advanced users and scripts, and not what regular users interact with. (Similarly, regular Git users will use branch and tag names not explicit hashes for most things.) The plumbing CLI should prize simplicity over convenience; that is its raison d'etre. If the user provides a path, we should treat it the same way not caring what sort of path it is. >> Scripting This is especially important for the scripting use-case. when arbitrary paths are sent to e.g. `nix copy` and the script author wants consistent behavior regardless of what those store paths are. Otherwise the script author needs to be careful to filter out `.drv` ones, and then run `nix copy` again with those paths and `--derivation`. That is not good! >> Surprisingly low impact Only two lines in the tests need changing, showing that the impact of this is pretty light. Many command, like `nix log` will continue to work with just the derivation passed as before. This because we used to: - Special case the drv path and replace it with it's outputs (what this gets rid of). - Turn those output path *back* into the original drv path. Now we just skip that entire round trip! > Context Issue #7261 lays out a broader vision for getting rid of `--derivation`, and has this as one of its dependencies. But we can do this with or without that. `Installable::toDerivations` is changed to handle the case of a `DerivedPath::Opaque` ending in `.drv`, which is new: it simply doesn't need to do any extra work in that case. On this basis, commands like `nix {show-derivation,log} /nix/store/...-foo.drv` still work as before, as described above. When testing older daemons, the post-build-hook will be run against the old CLI, so we need the old version of the post-build-hook to support that use-case. Co-authored-by: Travis A. Everett Co-authored-by: Valentin Gagarin --- src/libcmd/installable-derived-path.cc | 33 +++++++++++++++------------------ src/libcmd/installables.cc | 9 ++++++--- 2 files changed, 21 insertions(+), 21 deletions(-) (limited to 'src') diff --git a/src/libcmd/installable-derived-path.cc b/src/libcmd/installable-derived-path.cc index a9921b901..729dc7d31 100644 --- a/src/libcmd/installable-derived-path.cc +++ b/src/libcmd/installable-derived-path.cc @@ -31,27 +31,24 @@ InstallableDerivedPath InstallableDerivedPath::parse( ExtendedOutputsSpec extendedOutputsSpec) { auto derivedPath = std::visit(overloaded { - // If the user did not use ^, we treat the output more liberally. + // If the user did not use ^, we treat the output more + // liberally: we accept a symlink chain or an actual + // store path. [&](const ExtendedOutputsSpec::Default &) -> DerivedPath { - // First, we accept a symlink chain or an actual store path. auto storePath = store->followLinksToStorePath(prefix); - // Second, we see if the store path ends in `.drv` to decide what sort - // of derived path they want. - // - // This handling predates the `^` syntax. The `^*` in - // `/nix/store/hash-foo.drv^*` unambiguously means "do the - // `DerivedPath::Built` case", so plain `/nix/store/hash-foo.drv` could - // also unambiguously mean "do the DerivedPath::Opaque` case". - // - // Issue #7261 tracks reconsidering this `.drv` dispatching. - return storePath.isDerivation() - ? (DerivedPath) DerivedPath::Built { - .drvPath = std::move(storePath), - .outputs = OutputsSpec::All {}, - } - : (DerivedPath) DerivedPath::Opaque { - .path = std::move(storePath), + // Remove this prior to stabilizing the new CLI. + if (storePath.isDerivation()) { + auto oldDerivedPath = DerivedPath::Built { + .drvPath = storePath, + .outputs = OutputsSpec::All { }, }; + warn( + "The interpretation of store paths arguments ending in `.drv` recently changed. If this command is now failing try again with '%s'", + oldDerivedPath.to_string(*store)); + }; + return DerivedPath::Opaque { + .path = std::move(storePath), + }; }, // If the user did use ^, we just do exactly what is written. [&](const ExtendedOutputsSpec::Explicit & outputSpec) -> DerivedPath { diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc index 00c6f9516..90f001902 100644 --- a/src/libcmd/installables.cc +++ b/src/libcmd/installables.cc @@ -677,9 +677,12 @@ StorePathSet Installable::toDerivations( for (const auto & b : i->toDerivedPaths()) std::visit(overloaded { [&](const DerivedPath::Opaque & bo) { - if (!useDeriver) - throw Error("argument '%s' did not evaluate to a derivation", i->what()); - drvPaths.insert(getDeriver(store, *i, bo.path)); + drvPaths.insert( + bo.path.isDerivation() + ? bo.path + : useDeriver + ? getDeriver(store, *i, bo.path) + : throw Error("argument '%s' did not evaluate to a derivation", i->what())); }, [&](const DerivedPath::Built & bfd) { drvPaths.insert(bfd.drvPath); -- cgit v1.2.3 From f86f2b973f3b07134736fd96c1eff1da184dc1af Mon Sep 17 00:00:00 2001 From: Matthew Kenigsberg Date: Tue, 28 Feb 2023 16:04:17 -0700 Subject: Always set valid in path-info --json output Currently the valid key is only present when the path is invalid, which makes checking path validity more complex than it should be. With this change, the valid key can always be used to check if a path is valid --- src/libstore/store-api.cc | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc index 601efa1cc..f32c2d30c 100644 --- a/src/libstore/store-api.cc +++ b/src/libstore/store-api.cc @@ -855,6 +855,7 @@ json Store::pathInfoToJSON(const StorePathSet & storePaths, auto info = queryPathInfo(storePath); jsonPath["path"] = printStorePath(info->path); + jsonPath["valid"] = true; jsonPath["narHash"] = info->narHash.to_string(hashBase, true); jsonPath["narSize"] = info->narSize; -- cgit v1.2.3 From 12538605fdc99be846b736080558567edbb8a393 Mon Sep 17 00:00:00 2001 From: Bob van der Linden Date: Wed, 1 Mar 2023 07:40:44 +0100 Subject: nix-profile: add FIXME about using C++20 std::ranges --- src/nix/profile.cc | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/nix/profile.cc b/src/nix/profile.cc index 3464a977d..3d5c0c8a3 100644 --- a/src/nix/profile.cc +++ b/src/nix/profile.cc @@ -332,6 +332,8 @@ struct CmdProfileInstall : InstallablesCommand, MixDefaultProfile try { updateProfile(manifest.build(store)); } catch (BuildEnvFileConflictError & conflictError) { + // FIXME use C++20 std::ranges once macOS has it + // See https://github.com/NixOS/nix/compare/3efa476c5439f8f6c1968a6ba20a31d1239c2f04..1fe5d172ece51a619e879c4b86f603d9495cc102 auto findRefByFilePath = [&](Iterator begin, Iterator end) { for (auto it = begin; it != end; it++) { auto profileElement = *it; -- cgit v1.2.3 From 0fd8f542a8ddb303f589ff6ca3343f36e3a783c0 Mon Sep 17 00:00:00 2001 From: Yorick van Pelt Date: Tue, 31 Jan 2023 17:54:35 +0100 Subject: tests/coro-gc: create test for boehm stack patch Regression test for #7679 --- src/libexpr/tests/coro-gc.cc | 99 ++++++++++++++++++++++++++++++++++++++++++++ src/libexpr/tests/local.mk | 2 +- 2 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 src/libexpr/tests/coro-gc.cc (limited to 'src') diff --git a/src/libexpr/tests/coro-gc.cc b/src/libexpr/tests/coro-gc.cc new file mode 100644 index 000000000..6023b9b5e --- /dev/null +++ b/src/libexpr/tests/coro-gc.cc @@ -0,0 +1,99 @@ +#include +#if HAVE_BOEHMGC +#include +#endif + +#include "eval.hh" +#include "serialise.hh" + + +#define guard_gc(x) GC_register_finalizer((void*)x, finalizer, x##_collected, nullptr, nullptr) + + +namespace nix { +#if HAVE_BOEHMGC + static bool* uncollectable_bool() { + bool* res = (bool*)GC_MALLOC_UNCOLLECTABLE(1); + *res = false; + return res; + } + + static void finalizer(void *obj, void *data) { + //printf("finalizer: obj %p data %p\n", obj, data); + *((bool*)data) = true; + } + + // Generate 2 objects, discard one, run gc, + // see if one got collected and the other didn't + static void testFinalizerCalls() { + bool* do_collect_collected = uncollectable_bool(); + bool* dont_collect_collected = uncollectable_bool(); + { + volatile void* do_collect = GC_MALLOC_ATOMIC(128); + guard_gc(do_collect); + } + volatile void* dont_collect = GC_MALLOC_ATOMIC(128); + guard_gc(dont_collect); + GC_gcollect(); + GC_invoke_finalizers(); + + ASSERT_TRUE(*do_collect_collected); + ASSERT_FALSE(*dont_collect_collected); + ASSERT_NE(nullptr, dont_collect); + } + + // This test tests that boehm handles coroutine stacks correctly + TEST(CoroGC, CoroutineStackNotGCd) { + initGC(); + testFinalizerCalls(); + + bool* dont_collect_collected = uncollectable_bool(); + bool* do_collect_collected = uncollectable_bool(); + + volatile void* dont_collect = GC_MALLOC_ATOMIC(128); + guard_gc(dont_collect); + { + volatile void* do_collect = GC_MALLOC_ATOMIC(128); + guard_gc(do_collect); + } + + auto source = sinkToSource([&](Sink& sink) { + testFinalizerCalls(); + + bool* dont_collect_inner_collected = uncollectable_bool(); + bool* do_collect_inner_collected = uncollectable_bool(); + + volatile void* dont_collect_inner = GC_MALLOC_ATOMIC(128); + guard_gc(dont_collect_inner); + { + volatile void* do_collect_inner = GC_MALLOC_ATOMIC(128); + guard_gc(do_collect_inner); + } + // pass control to main + writeString("foo", sink); + + ASSERT_TRUE(*do_collect_inner_collected); + ASSERT_FALSE(*dont_collect_inner_collected); + ASSERT_NE(nullptr, dont_collect_inner); + + // pass control to main + writeString("bar", sink); + }); + + // pass control to coroutine + std::string foo = readString(*source); + ASSERT_EQ(foo, "foo"); + + GC_gcollect(); + GC_invoke_finalizers(); + + // pass control to coroutine + std::string bar = readString(*source); + ASSERT_EQ(bar, "bar"); + + ASSERT_FALSE(*dont_collect_collected); + ASSERT_TRUE(*do_collect_collected); + ASSERT_NE(nullptr, dont_collect); + } +#endif +} diff --git a/src/libexpr/tests/local.mk b/src/libexpr/tests/local.mk index 3e5504f71..c36d21bfe 100644 --- a/src/libexpr/tests/local.mk +++ b/src/libexpr/tests/local.mk @@ -16,4 +16,4 @@ libexpr-tests_CXXFLAGS += -I src/libexpr -I src/libutil -I src/libstore -I src/l libexpr-tests_LIBS = libstore-tests libutils-tests libexpr libutil libstore libfetchers -libexpr-tests_LDFLAGS := $(GTEST_LIBS) -lgmock +libexpr-tests_LDFLAGS := $(GTEST_LIBS) -lgmock -lboost_context -- cgit v1.2.3 From eaeb994d8b9d2152e076897bf430c8ac205d3d1a Mon Sep 17 00:00:00 2001 From: Yorick van Pelt Date: Fri, 3 Feb 2023 17:50:01 +0100 Subject: Disable GC inside coroutines on mac OS --- src/libexpr/eval.cc | 20 ++++++++++++++++++++ src/libexpr/tests/coro-gc.cc | 11 +++++++++++ src/libutil/serialise.cc | 38 +++++++++++++++++++++++++++++++++----- src/libutil/serialise.hh | 10 ++++++++++ 4 files changed, 74 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 3e37c7f60..a5a198926 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -326,6 +326,20 @@ static Symbol getName(const AttrName & name, EvalState & state, Env & env) } +class BoehmDisableGC : public DisableGC { +public: + BoehmDisableGC() { +#if HAVE_BOEHMGC + GC_disable(); +#endif + }; + virtual ~BoehmDisableGC() override { +#if HAVE_BOEHMGC + GC_enable(); +#endif + }; +}; + static bool gcInitialised = false; void initGC() @@ -349,6 +363,12 @@ void initGC() StackAllocator::defaultAllocator = &boehmGCStackAllocator; + + /* Used to disable GC when entering coroutines on macOS */ + DisableGC::create = []() { + return std::dynamic_pointer_cast(std::make_shared()); + }; + /* Set the initial heap size to something fairly big (25% of physical RAM, up to a maximum of 384 MiB) so that in most cases we don't need to garbage collect at all. (Collection has a diff --git a/src/libexpr/tests/coro-gc.cc b/src/libexpr/tests/coro-gc.cc index 6023b9b5e..8d9585cc7 100644 --- a/src/libexpr/tests/coro-gc.cc +++ b/src/libexpr/tests/coro-gc.cc @@ -25,6 +25,7 @@ namespace nix { // Generate 2 objects, discard one, run gc, // see if one got collected and the other didn't + // GC is disabled inside coroutines on __APPLE__ static void testFinalizerCalls() { bool* do_collect_collected = uncollectable_bool(); bool* dont_collect_collected = uncollectable_bool(); @@ -37,7 +38,9 @@ namespace nix { GC_gcollect(); GC_invoke_finalizers(); +#if !__APPLE__ ASSERT_TRUE(*do_collect_collected); +#endif ASSERT_FALSE(*dont_collect_collected); ASSERT_NE(nullptr, dont_collect); } @@ -58,6 +61,10 @@ namespace nix { } auto source = sinkToSource([&](Sink& sink) { + +#if __APPLE__ + ASSERT_TRUE(GC_is_disabled()); +#endif testFinalizerCalls(); bool* dont_collect_inner_collected = uncollectable_bool(); @@ -71,6 +78,9 @@ namespace nix { } // pass control to main writeString("foo", sink); +#if __APPLE__ + ASSERT_TRUE(GC_is_disabled()); +#endif ASSERT_TRUE(*do_collect_inner_collected); ASSERT_FALSE(*dont_collect_inner_collected); @@ -84,6 +94,7 @@ namespace nix { std::string foo = readString(*source); ASSERT_EQ(foo, "foo"); + ASSERT_FALSE(GC_is_disabled()); GC_gcollect(); GC_invoke_finalizers(); diff --git a/src/libutil/serialise.cc b/src/libutil/serialise.cc index c653db9d0..531100c01 100644 --- a/src/libutil/serialise.cc +++ b/src/libutil/serialise.cc @@ -186,6 +186,10 @@ static DefaultStackAllocator defaultAllocatorSingleton; StackAllocator *StackAllocator::defaultAllocator = &defaultAllocatorSingleton; +std::shared_ptr (*DisableGC::create)() = []() { + return std::dynamic_pointer_cast(std::make_shared()); +}; + std::unique_ptr sourceToSink(std::function fun) { struct SourceToSink : FinishSink @@ -206,7 +210,11 @@ std::unique_ptr sourceToSink(std::function fun) if (in.empty()) return; cur = in; - if (!coro) + if (!coro) { +#if __APPLE__ + /* Disable GC when entering the coroutine on macOS, since it doesn't find the main thread stack in this case */ + auto disablegc = DisableGC::create(); +#endif coro = coro_t::push_type(VirtualStackAllocator{}, [&](coro_t::pull_type & yield) { LambdaSource source([&](char *out, size_t out_len) { if (cur.empty()) { @@ -223,17 +231,28 @@ std::unique_ptr sourceToSink(std::function fun) }); fun(source); }); + } if (!*coro) { abort(); } - if (!cur.empty()) (*coro)(false); + if (!cur.empty()) { +#if __APPLE__ + auto disablegc = DisableGC::create(); +#endif + (*coro)(false); + } } void finish() override { if (!coro) return; if (!*coro) abort(); - (*coro)(true); + { +#if __APPLE__ + auto disablegc = DisableGC::create(); +#endif + (*coro)(true); + } if (*coro) abort(); } }; @@ -264,18 +283,27 @@ std::unique_ptr sinkToSource( size_t read(char * data, size_t len) override { - if (!coro) + if (!coro) { +#if __APPLE__ + auto disablegc = DisableGC::create(); +#endif coro = coro_t::pull_type(VirtualStackAllocator{}, [&](coro_t::push_type & yield) { LambdaSink sink([&](std::string_view data) { if (!data.empty()) yield(std::string(data)); }); fun(sink); }); + } if (!*coro) { eof(); abort(); } if (pos == cur.size()) { - if (!cur.empty()) (*coro)(); + if (!cur.empty()) { +#if __APPLE__ + auto disablegc = DisableGC::create(); +#endif + (*coro)(); + } cur = coro->get(); pos = 0; } diff --git a/src/libutil/serialise.hh b/src/libutil/serialise.hh index 7da5b07fd..fb1f14a3b 100644 --- a/src/libutil/serialise.hh +++ b/src/libutil/serialise.hh @@ -501,4 +501,14 @@ struct StackAllocator { static StackAllocator *defaultAllocator; }; +/* Disabling GC when entering a coroutine (on macos). + ::create is to avoid boehm gc dependency in libutil. + */ +class DisableGC { +public: + DisableGC() {}; + virtual ~DisableGC() {}; + static std::shared_ptr (*create)(); +}; + } -- cgit v1.2.3 From 53bb4a53279170c236c8a9252297b1132620f4ea Mon Sep 17 00:00:00 2001 From: Yorick van Pelt Date: Tue, 7 Feb 2023 14:59:45 +0100 Subject: tests/coro-gc: refactor and split into 3 tests --- src/libexpr/tests/coro-gc.cc | 129 ++++++++++++++++++++++++++++--------------- 1 file changed, 83 insertions(+), 46 deletions(-) (limited to 'src') diff --git a/src/libexpr/tests/coro-gc.cc b/src/libexpr/tests/coro-gc.cc index 8d9585cc7..f848bc2f0 100644 --- a/src/libexpr/tests/coro-gc.cc +++ b/src/libexpr/tests/coro-gc.cc @@ -1,89 +1,126 @@ #include #if HAVE_BOEHMGC #include -#endif #include "eval.hh" #include "serialise.hh" - -#define guard_gc(x) GC_register_finalizer((void*)x, finalizer, x##_collected, nullptr, nullptr) +#endif namespace nix { #if HAVE_BOEHMGC - static bool* uncollectable_bool() { - bool* res = (bool*)GC_MALLOC_UNCOLLECTABLE(1); - *res = false; - return res; - } static void finalizer(void *obj, void *data) { - //printf("finalizer: obj %p data %p\n", obj, data); *((bool*)data) = true; } + static bool* make_witness(volatile void* obj) { + /* We can't store the witnesses on the stack, + since they might be collected long afterwards */ + bool* res = (bool*)GC_MALLOC_UNCOLLECTABLE(1); + *res = false; + GC_register_finalizer((void*)obj, finalizer, res, nullptr, nullptr); + return res; + } + // Generate 2 objects, discard one, run gc, // see if one got collected and the other didn't // GC is disabled inside coroutines on __APPLE__ static void testFinalizerCalls() { - bool* do_collect_collected = uncollectable_bool(); - bool* dont_collect_collected = uncollectable_bool(); - { - volatile void* do_collect = GC_MALLOC_ATOMIC(128); - guard_gc(do_collect); - } + volatile void* do_collect = GC_MALLOC_ATOMIC(128); volatile void* dont_collect = GC_MALLOC_ATOMIC(128); - guard_gc(dont_collect); + + bool* do_collect_witness = make_witness(do_collect); + bool* dont_collect_witness = make_witness(dont_collect); GC_gcollect(); GC_invoke_finalizers(); -#if !__APPLE__ - ASSERT_TRUE(*do_collect_collected); -#endif - ASSERT_FALSE(*dont_collect_collected); + ASSERT_TRUE(GC_is_disabled() || *do_collect_witness); + ASSERT_FALSE(*dont_collect_witness); ASSERT_NE(nullptr, dont_collect); } - // This test tests that boehm handles coroutine stacks correctly - TEST(CoroGC, CoroutineStackNotGCd) { + TEST(CoroGC, BasicFinalizers) { initGC(); testFinalizerCalls(); + } - bool* dont_collect_collected = uncollectable_bool(); - bool* do_collect_collected = uncollectable_bool(); - - volatile void* dont_collect = GC_MALLOC_ATOMIC(128); - guard_gc(dont_collect); - { - volatile void* do_collect = GC_MALLOC_ATOMIC(128); - guard_gc(do_collect); - } + // Run testFinalizerCalls inside a coroutine + // this tests that GC works as expected inside a coroutine + TEST(CoroGC, CoroFinalizers) { + initGC(); auto source = sinkToSource([&](Sink& sink) { + testFinalizerCalls(); + + // pass control to main + writeString("foo", sink); + }); + + // pass control to coroutine + std::string foo = readString(*source); + ASSERT_EQ(foo, "foo"); + } #if __APPLE__ + // This test tests that GC is disabled on darwin + // to work around the patch not being sufficient there, + // causing crashes whenever gc is invoked inside a coroutine + TEST(CoroGC, AppleCoroDisablesGC) { + initGC(); + auto source = sinkToSource([&](Sink& sink) { + ASSERT_TRUE(GC_is_disabled()); + // pass control to main + writeString("foo", sink); + ASSERT_TRUE(GC_is_disabled()); + + // pass control to main + writeString("bar", sink); + }); + + // pass control to coroutine + std::string foo = readString(*source); + ASSERT_EQ(foo, "foo"); + ASSERT_FALSE(GC_is_disabled()); + // pass control to coroutine + std::string bar = readString(*source); + ASSERT_EQ(bar, "bar"); + + ASSERT_FALSE(GC_is_disabled()); + } #endif - testFinalizerCalls(); - bool* dont_collect_inner_collected = uncollectable_bool(); - bool* do_collect_inner_collected = uncollectable_bool(); + // This test tests that boehm handles coroutine stacks correctly + // This test tests that coroutine stacks are registered to the GC, + // even when the coroutine is not running. It also tests that + // the main stack is still registered to the GC when the coroutine is running. + TEST(CoroGC, CoroutineStackNotGCd) { + initGC(); + + volatile void* do_collect = GC_MALLOC_ATOMIC(128); + volatile void* dont_collect = GC_MALLOC_ATOMIC(128); + + bool* do_collect_witness = make_witness(do_collect); + bool* dont_collect_witness = make_witness(dont_collect); + do_collect = nullptr; + + auto source = sinkToSource([&](Sink& sink) { volatile void* dont_collect_inner = GC_MALLOC_ATOMIC(128); - guard_gc(dont_collect_inner); - { - volatile void* do_collect_inner = GC_MALLOC_ATOMIC(128); - guard_gc(do_collect_inner); - } + volatile void* do_collect_inner = GC_MALLOC_ATOMIC(128); + + bool* do_collect_inner_witness = make_witness(do_collect_inner); + bool* dont_collect_inner_witness = make_witness(dont_collect_inner); + + do_collect_inner = nullptr; + // pass control to main writeString("foo", sink); -#if __APPLE__ - ASSERT_TRUE(GC_is_disabled()); -#endif - ASSERT_TRUE(*do_collect_inner_collected); - ASSERT_FALSE(*dont_collect_inner_collected); + ASSERT_FALSE(*dont_collect_inner_witness); + ASSERT_TRUE(*do_collect_inner_witness); ASSERT_NE(nullptr, dont_collect_inner); // pass control to main @@ -102,8 +139,8 @@ namespace nix { std::string bar = readString(*source); ASSERT_EQ(bar, "bar"); - ASSERT_FALSE(*dont_collect_collected); - ASSERT_TRUE(*do_collect_collected); + ASSERT_FALSE(*dont_collect_witness); + ASSERT_TRUE(*do_collect_witness); ASSERT_NE(nullptr, dont_collect); } #endif -- cgit v1.2.3 From 4c73eab92351e0942dc27f84fec1bf8c94619d02 Mon Sep 17 00:00:00 2001 From: Yorick van Pelt Date: Fri, 10 Feb 2023 15:04:17 +0100 Subject: DisableGC: replace by CoroutineContext, std::shared_ptr --- src/libexpr/eval.cc | 20 +++++++++++--------- src/libutil/serialise.cc | 38 ++++++++++++++++++++------------------ src/libutil/serialise.hh | 10 +++------- 3 files changed, 34 insertions(+), 34 deletions(-) (limited to 'src') diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index a5a198926..01680afff 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -325,20 +325,22 @@ static Symbol getName(const AttrName & name, EvalState & state, Env & env) } } - -class BoehmDisableGC : public DisableGC { +#if HAVE_BOEHMGC +/* Disable GC while this object lives. Used by CoroutineContext. + * + * Boehm keeps a count of GC_disable() and GC_enable() calls, + * and only enables GC when the count matches. + */ +class BoehmDisableGC { public: BoehmDisableGC() { -#if HAVE_BOEHMGC GC_disable(); -#endif }; - virtual ~BoehmDisableGC() override { -#if HAVE_BOEHMGC + ~BoehmDisableGC() { GC_enable(); -#endif }; }; +#endif static bool gcInitialised = false; @@ -365,8 +367,8 @@ void initGC() /* Used to disable GC when entering coroutines on macOS */ - DisableGC::create = []() { - return std::dynamic_pointer_cast(std::make_shared()); + create_disable_gc = []() -> std::shared_ptr { + return std::make_shared(); }; /* Set the initial heap size to something fairly big (25% of diff --git a/src/libutil/serialise.cc b/src/libutil/serialise.cc index 531100c01..7ef24d458 100644 --- a/src/libutil/serialise.cc +++ b/src/libutil/serialise.cc @@ -186,8 +186,21 @@ static DefaultStackAllocator defaultAllocatorSingleton; StackAllocator *StackAllocator::defaultAllocator = &defaultAllocatorSingleton; -std::shared_ptr (*DisableGC::create)() = []() { - return std::dynamic_pointer_cast(std::make_shared()); +std::shared_ptr (*create_disable_gc)() = []() -> std::shared_ptr { + return {}; +}; + +/* This class is used for entry and exit hooks on coroutines */ +class CoroutineContext { +#if __APPLE__ + /* Disable GC when entering the coroutine on macOS, since it doesn't find the main thread stack in this case. + * std::shared_ptr performs type-erasure, so it will call the right + * deleter. */ + const std::shared_ptr disable_gc = create_disable_gc(); +#endif +public: + CoroutineContext() {}; + ~CoroutineContext() {}; }; std::unique_ptr sourceToSink(std::function fun) @@ -211,10 +224,7 @@ std::unique_ptr sourceToSink(std::function fun) cur = in; if (!coro) { -#if __APPLE__ - /* Disable GC when entering the coroutine on macOS, since it doesn't find the main thread stack in this case */ - auto disablegc = DisableGC::create(); -#endif + CoroutineContext ctx; coro = coro_t::push_type(VirtualStackAllocator{}, [&](coro_t::pull_type & yield) { LambdaSource source([&](char *out, size_t out_len) { if (cur.empty()) { @@ -236,9 +246,7 @@ std::unique_ptr sourceToSink(std::function fun) if (!*coro) { abort(); } if (!cur.empty()) { -#if __APPLE__ - auto disablegc = DisableGC::create(); -#endif + CoroutineContext ctx; (*coro)(false); } } @@ -248,9 +256,7 @@ std::unique_ptr sourceToSink(std::function fun) if (!coro) return; if (!*coro) abort(); { -#if __APPLE__ - auto disablegc = DisableGC::create(); -#endif + CoroutineContext ctx; (*coro)(true); } if (*coro) abort(); @@ -284,9 +290,7 @@ std::unique_ptr sinkToSource( size_t read(char * data, size_t len) override { if (!coro) { -#if __APPLE__ - auto disablegc = DisableGC::create(); -#endif + CoroutineContext ctx; coro = coro_t::pull_type(VirtualStackAllocator{}, [&](coro_t::push_type & yield) { LambdaSink sink([&](std::string_view data) { if (!data.empty()) yield(std::string(data)); @@ -299,9 +303,7 @@ std::unique_ptr sinkToSource( if (pos == cur.size()) { if (!cur.empty()) { -#if __APPLE__ - auto disablegc = DisableGC::create(); -#endif + CoroutineContext ctx; (*coro)(); } cur = coro->get(); diff --git a/src/libutil/serialise.hh b/src/libutil/serialise.hh index fb1f14a3b..58b9499db 100644 --- a/src/libutil/serialise.hh +++ b/src/libutil/serialise.hh @@ -502,13 +502,9 @@ struct StackAllocator { }; /* Disabling GC when entering a coroutine (on macos). - ::create is to avoid boehm gc dependency in libutil. + mutable to avoid boehm gc dependency in libutil. */ -class DisableGC { -public: - DisableGC() {}; - virtual ~DisableGC() {}; - static std::shared_ptr (*create)(); -}; +extern std::shared_ptr (*create_disable_gc)(); + } -- cgit v1.2.3 From 176005749cbc413514cc7fca31587de184720b62 Mon Sep 17 00:00:00 2001 From: Yorick van Pelt Date: Wed, 1 Mar 2023 15:07:00 +0100 Subject: Always disable GC in a coroutine unless the patch is applied --- src/libexpr/eval.cc | 4 +++- src/libutil/serialise.cc | 9 ++++----- src/libutil/serialise.hh | 4 ++-- 3 files changed, 9 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 01680afff..9667136e8 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -366,10 +366,12 @@ void initGC() StackAllocator::defaultAllocator = &boehmGCStackAllocator; +#if NIX_BOEHM_PATCH_VERSION != 1 /* Used to disable GC when entering coroutines on macOS */ - create_disable_gc = []() -> std::shared_ptr { + create_coro_gc_hook = []() -> std::shared_ptr { return std::make_shared(); }; +#endif /* Set the initial heap size to something fairly big (25% of physical RAM, up to a maximum of 384 MiB) so that in most cases diff --git a/src/libutil/serialise.cc b/src/libutil/serialise.cc index 7ef24d458..f83bb2848 100644 --- a/src/libutil/serialise.cc +++ b/src/libutil/serialise.cc @@ -186,18 +186,17 @@ static DefaultStackAllocator defaultAllocatorSingleton; StackAllocator *StackAllocator::defaultAllocator = &defaultAllocatorSingleton; -std::shared_ptr (*create_disable_gc)() = []() -> std::shared_ptr { +std::shared_ptr (*create_coro_gc_hook)() = []() -> std::shared_ptr { return {}; }; /* This class is used for entry and exit hooks on coroutines */ class CoroutineContext { -#if __APPLE__ - /* Disable GC when entering the coroutine on macOS, since it doesn't find the main thread stack in this case. + /* Disable GC when entering the coroutine without the boehm patch, + * since it doesn't find the main thread stack in this case. * std::shared_ptr performs type-erasure, so it will call the right * deleter. */ - const std::shared_ptr disable_gc = create_disable_gc(); -#endif + const std::shared_ptr coro_gc_hook = create_coro_gc_hook(); public: CoroutineContext() {}; ~CoroutineContext() {}; diff --git a/src/libutil/serialise.hh b/src/libutil/serialise.hh index 58b9499db..e99c5fcc7 100644 --- a/src/libutil/serialise.hh +++ b/src/libutil/serialise.hh @@ -501,10 +501,10 @@ struct StackAllocator { static StackAllocator *defaultAllocator; }; -/* Disabling GC when entering a coroutine (on macos). +/* Disabling GC when entering a coroutine (without the boehm patch). mutable to avoid boehm gc dependency in libutil. */ -extern std::shared_ptr (*create_disable_gc)(); +extern std::shared_ptr (*create_coro_gc_hook)(); } -- cgit v1.2.3 From b7f01a82a9b0b2c66bb5d58e1518a113d20e79c5 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Wed, 1 Mar 2023 11:09:47 -0500 Subject: Remove dead code `RemoteStore::sameMachine` It has been dead code since 9747ea84b43c75f719d719673b9d0a7cb50d34e5. --- src/libstore/remote-store.hh | 2 -- src/libstore/ssh-store.cc | 3 --- src/libstore/uds-remote-store.hh | 3 --- 3 files changed, 8 deletions(-) (limited to 'src') diff --git a/src/libstore/remote-store.hh b/src/libstore/remote-store.hh index 11d089cd2..8cd7cc822 100644 --- a/src/libstore/remote-store.hh +++ b/src/libstore/remote-store.hh @@ -38,8 +38,6 @@ class RemoteStore : public virtual RemoteStoreConfig, { public: - virtual bool sameMachine() = 0; - RemoteStore(const Params & params); /* Implementations of abstract store API methods. */ diff --git a/src/libstore/ssh-store.cc b/src/libstore/ssh-store.cc index a1d4daafd..cfa063803 100644 --- a/src/libstore/ssh-store.cc +++ b/src/libstore/ssh-store.cc @@ -49,9 +49,6 @@ public: return *uriSchemes().begin() + "://" + host; } - bool sameMachine() override - { return false; } - // FIXME extend daemon protocol, move implementation to RemoteStore std::optional getBuildLogExact(const StorePath & path) override { unsupported("getBuildLogExact"); } diff --git a/src/libstore/uds-remote-store.hh b/src/libstore/uds-remote-store.hh index f8dfcca70..d31a4d592 100644 --- a/src/libstore/uds-remote-store.hh +++ b/src/libstore/uds-remote-store.hh @@ -29,9 +29,6 @@ public: static std::set uriSchemes() { return {"unix"}; } - bool sameMachine() override - { return true; } - ref getFSAccessor() override { return LocalFSStore::getFSAccessor(); } -- cgit v1.2.3 From 25300c0ecdedcd8720b996b41e78dfe1037bfcff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Baylac=20Jacqu=C3=A9?= Date: Wed, 1 Mar 2023 20:01:36 +0100 Subject: Treat empty env var paths as unset We make sure the env var paths are actually set (ie. not "") before sending them to the canonicalization function. If we forget to do so, the user will end up facing a puzzled failed assertion internal error. We issue a non-failing warning as a stop-gap measure. We could want to revisit this to issue a detailed failing error message in the future. --- src/libstore/build/local-derivation-goal.cc | 2 +- src/libstore/globals.cc | 14 +++++++------- src/libutil/util.cc | 12 ++++++++++++ src/libutil/util.hh | 4 ++++ 4 files changed, 24 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/libstore/build/local-derivation-goal.cc b/src/libstore/build/local-derivation-goal.cc index a961d8eed..1c0860993 100644 --- a/src/libstore/build/local-derivation-goal.cc +++ b/src/libstore/build/local-derivation-goal.cc @@ -2063,7 +2063,7 @@ void LocalDerivationGoal::runChild() /* The tmpDir in scope points at the temporary build directory for our derivation. Some packages try different mechanisms to find temporary directories, so we want to open up a broader place for them to dump their files, if needed. */ - Path globalTmpDir = canonPath(getEnv("TMPDIR").value_or("/tmp"), true); + Path globalTmpDir = canonPath(getEnvNonEmpty("TMPDIR").value_or("/tmp"), true); /* They don't like trailing slashes on subpath directives */ if (globalTmpDir.back() == '/') globalTmpDir.pop_back(); diff --git a/src/libstore/globals.cc b/src/libstore/globals.cc index 8e33a3dec..fae79c1a0 100644 --- a/src/libstore/globals.cc +++ b/src/libstore/globals.cc @@ -30,15 +30,15 @@ static GlobalConfig::Register rSettings(&settings); Settings::Settings() : nixPrefix(NIX_PREFIX) - , nixStore(canonPath(getEnv("NIX_STORE_DIR").value_or(getEnv("NIX_STORE").value_or(NIX_STORE_DIR)))) - , nixDataDir(canonPath(getEnv("NIX_DATA_DIR").value_or(NIX_DATA_DIR))) - , nixLogDir(canonPath(getEnv("NIX_LOG_DIR").value_or(NIX_LOG_DIR))) - , nixStateDir(canonPath(getEnv("NIX_STATE_DIR").value_or(NIX_STATE_DIR))) - , nixConfDir(canonPath(getEnv("NIX_CONF_DIR").value_or(NIX_CONF_DIR))) + , nixStore(canonPath(getEnvNonEmpty("NIX_STORE_DIR").value_or(getEnvNonEmpty("NIX_STORE").value_or(NIX_STORE_DIR)))) + , nixDataDir(canonPath(getEnvNonEmpty("NIX_DATA_DIR").value_or(NIX_DATA_DIR))) + , nixLogDir(canonPath(getEnvNonEmpty("NIX_LOG_DIR").value_or(NIX_LOG_DIR))) + , nixStateDir(canonPath(getEnvNonEmpty("NIX_STATE_DIR").value_or(NIX_STATE_DIR))) + , nixConfDir(canonPath(getEnvNonEmpty("NIX_CONF_DIR").value_or(NIX_CONF_DIR))) , nixUserConfFiles(getUserConfigFiles()) - , nixBinDir(canonPath(getEnv("NIX_BIN_DIR").value_or(NIX_BIN_DIR))) + , nixBinDir(canonPath(getEnvNonEmpty("NIX_BIN_DIR").value_or(NIX_BIN_DIR))) , nixManDir(canonPath(NIX_MAN_DIR)) - , nixDaemonSocketFile(canonPath(getEnv("NIX_DAEMON_SOCKET_PATH").value_or(nixStateDir + DEFAULT_SOCKET_PATH))) + , nixDaemonSocketFile(canonPath(getEnvNonEmpty("NIX_DAEMON_SOCKET_PATH").value_or(nixStateDir + DEFAULT_SOCKET_PATH))) { buildUsersGroup = getuid() == 0 ? "nixbld" : ""; lockCPU = getEnv("NIX_AFFINITY_HACK") == "1"; diff --git a/src/libutil/util.cc b/src/libutil/util.cc index 885bae69c..5377f093b 100644 --- a/src/libutil/util.cc +++ b/src/libutil/util.cc @@ -54,6 +54,18 @@ std::optional getEnv(const std::string & key) return std::string(value); } +std::optional getEnvNonEmpty(const std::string & key) { + auto value = getEnv(key); + if (value == "") { + // TODO: determine whether this should be a warning or an error. + logWarning({ + .msg = hintfmt("ignoring the '%1%' env variable, its value has been set to \"\"", key) + }); + return std::nullopt; + } else { + return value; + } +} std::map getEnv() { diff --git a/src/libutil/util.hh b/src/libutil/util.hh index b5625ecef..3293c758f 100644 --- a/src/libutil/util.hh +++ b/src/libutil/util.hh @@ -39,6 +39,10 @@ extern const std::string nativeSystem; /* Return an environment variable. */ std::optional getEnv(const std::string & key); +/* Return a non empty environment variable. Returns nullopt if the env +variable is set to "" */ +std::optional getEnvNonEmpty(const std::string & key); + /* Get the entire environment. */ std::map getEnv(); -- cgit v1.2.3 From c13cbd20ab97e46eeb39939494d137a2f831b5e1 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 2 Mar 2023 14:46:28 +0100 Subject: Logger::writeToStdout(): Use writeFull() This ensures that write errors do not get ignored. --- src/libutil/logging.cc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/libutil/logging.cc b/src/libutil/logging.cc index 904ba6ebe..56bdaf87a 100644 --- a/src/libutil/logging.cc +++ b/src/libutil/logging.cc @@ -32,7 +32,8 @@ void Logger::warn(const std::string & msg) void Logger::writeToStdout(std::string_view s) { - std::cout << s << "\n"; + writeFull(STDOUT_FILENO, s); + writeFull(STDOUT_FILENO, "\n"); } class SimpleLogger : public Logger @@ -84,7 +85,7 @@ public: void startActivity(ActivityId act, Verbosity lvl, ActivityType type, const std::string & s, const Fields & fields, ActivityId parent) - override + override { if (lvl <= verbosity && !s.empty()) log(lvl, s + "..."); -- cgit v1.2.3 From 989b823ac5265a7738396566fa0792f3cb55160e Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 2 Mar 2023 14:51:14 +0100 Subject: nix store cat: Use writeFull() Fixes #7939. --- src/nix/cat.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/nix/cat.cc b/src/nix/cat.cc index 6420a0f79..60aa66ce0 100644 --- a/src/nix/cat.cc +++ b/src/nix/cat.cc @@ -17,7 +17,7 @@ struct MixCat : virtual Args if (st.type != FSAccessor::Type::tRegular) throw Error("path '%1%' is not a regular file", path); - std::cout << accessor->readFile(path); + writeFull(STDOUT_FILENO, accessor->readFile(path)); } }; -- cgit v1.2.3 From 09f5975c6a0d8706b22f15e4c5998018e56484ae Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 2 Mar 2023 14:52:37 +0100 Subject: Logger::cout: Use fmt() This ensures that in cout(s), 's' does not get interpreted as a format string. --- src/libutil/logging.hh | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/libutil/logging.hh b/src/libutil/logging.hh index 4642c49f7..a68edd15a 100644 --- a/src/libutil/logging.hh +++ b/src/libutil/logging.hh @@ -102,11 +102,9 @@ public: virtual void writeToStdout(std::string_view s); template - inline void cout(const std::string & fs, const Args & ... args) + inline void cout(const Args & ... args) { - boost::format f(fs); - formatHelper(f, args...); - writeToStdout(f.str()); + writeToStdout(fmt(args...)); } virtual std::optional ask(std::string_view s) -- cgit v1.2.3 From 1a825b6919e9a3a98caec58192bb89dfb4e5bcfa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophane=20Hufschmitt?= Date: Thu, 2 Mar 2023 14:59:15 +0100 Subject: Log the decompressed body in case of http errors Don't show the users the raw (possibly compressed) error message as everyone isn't able to decompress brotli in their brain. --- src/libstore/filetransfer.cc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/libstore/filetransfer.cc b/src/libstore/filetransfer.cc index 1c8676a59..6a4778d1f 100644 --- a/src/libstore/filetransfer.cc +++ b/src/libstore/filetransfer.cc @@ -88,6 +88,10 @@ struct curlFileTransfer : public FileTransfer {request.uri}, request.parentAct) , callback(std::move(callback)) , finalSink([this](std::string_view data) { + if (errorSink) { + (*errorSink)(data); + } + if (this->request.dataCallback) { auto httpStatus = getHTTPStatus(); @@ -163,8 +167,6 @@ struct curlFileTransfer : public FileTransfer } } - if (errorSink) - (*errorSink)({(char *) contents, realSize}); (*decompressionSink)({(char *) contents, realSize}); return realSize; -- cgit v1.2.3 From b69a73a2305aaee2a4da73da601dbde3f1ddd7a6 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 2 Mar 2023 15:02:24 +0100 Subject: Get rid of some unchecked calls to std::cout --- src/nix/build.cc | 4 ++-- src/nix/describe-stores.cc | 2 +- src/nix/diff-closures.cc | 2 +- src/nix/eval.cc | 4 ++-- src/nix/flake.cc | 2 +- src/nix/log.cc | 2 +- src/nix/ls.cc | 2 +- src/nix/main.cc | 12 ++++++------ src/nix/make-content-addressed.cc | 2 +- src/nix/prefetch.cc | 4 ++-- src/nix/profile.cc | 16 ++++++++-------- src/nix/realisation.cc | 12 +++++------- src/nix/search.cc | 5 ++--- src/nix/show-derivation.cc | 2 +- src/nix/sigs.cc | 4 ++-- 15 files changed, 36 insertions(+), 39 deletions(-) (limited to 'src') diff --git a/src/nix/build.cc b/src/nix/build.cc index 12b22d999..f4f2ec81d 100644 --- a/src/nix/build.cc +++ b/src/nix/build.cc @@ -139,11 +139,11 @@ struct CmdBuild : InstallablesCommand, MixDryRun, MixJSON, MixProfile for (auto & buildable : buildables) { std::visit(overloaded { [&](const BuiltPath::Opaque & bo) { - std::cout << store->printStorePath(bo.path) << std::endl; + logger->cout(store->printStorePath(bo.path)); }, [&](const BuiltPath::Built & bfd) { for (auto & output : bfd.outputs) { - std::cout << store->printStorePath(output.second) << std::endl; + logger->cout(store->printStorePath(output.second)); } }, }, buildable.path.raw()); diff --git a/src/nix/describe-stores.cc b/src/nix/describe-stores.cc index 1dd384c0e..eafcedd1f 100644 --- a/src/nix/describe-stores.cc +++ b/src/nix/describe-stores.cc @@ -25,7 +25,7 @@ struct CmdDescribeStores : Command, MixJSON res[storeName] = storeConfig->toJSON(); } if (json) { - std::cout << res; + logger->cout("%s", res); } else { for (auto & [storeName, storeConfig] : res.items()) { std::cout << "## " << storeName << std::endl << std::endl; diff --git a/src/nix/diff-closures.cc b/src/nix/diff-closures.cc index 3489cc132..c7c37b66f 100644 --- a/src/nix/diff-closures.cc +++ b/src/nix/diff-closures.cc @@ -97,7 +97,7 @@ void printClosureDiff( items.push_back(fmt("%s → %s", showVersions(removed), showVersions(added))); if (showDelta) items.push_back(fmt("%s%+.1f KiB" ANSI_NORMAL, sizeDelta > 0 ? ANSI_RED : ANSI_GREEN, sizeDelta / 1024.0)); - std::cout << fmt("%s%s: %s\n", indent, name, concatStringsSep(", ", items)); + logger->cout("%s%s: %s", indent, name, concatStringsSep(", ", items)); } } } diff --git a/src/nix/eval.cc b/src/nix/eval.cc index a579213fd..209fd3ed2 100644 --- a/src/nix/eval.cc +++ b/src/nix/eval.cc @@ -112,11 +112,11 @@ struct CmdEval : MixJSON, InstallableCommand, MixReadOnlyOption else if (raw) { stopProgressBar(); - std::cout << *state->coerceToString(noPos, *v, context, "while generating the eval command output"); + writeFull(STDOUT_FILENO, *state->coerceToString(noPos, *v, context, "while generating the eval command output")); } else if (json) { - std::cout << printValueAsJSON(*state, true, *v, pos, context, false).dump() << std::endl; + logger->cout("%s", printValueAsJSON(*state, true, *v, pos, context, false)); } else { diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 053a9c9e1..3fe093fc7 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -952,7 +952,7 @@ struct CmdFlakeArchive : FlakeCommand, MixJSON, MixDryRun {"path", store->printStorePath(flake.flake.sourceInfo->storePath)}, {"inputs", traverse(*flake.lockFile.root)}, }; - std::cout << jsonRoot.dump() << std::endl; + logger->cout("%s", jsonRoot); } else { traverse(*flake.lockFile.root); } diff --git a/src/nix/log.cc b/src/nix/log.cc index a0598ca13..0c9f778f0 100644 --- a/src/nix/log.cc +++ b/src/nix/log.cc @@ -53,7 +53,7 @@ struct CmdLog : InstallableCommand if (!log) continue; stopProgressBar(); printInfo("got build log for '%s' from '%s'", installable->what(), logSub.getUri()); - std::cout << *log; + writeFull(STDOUT_FILENO, *log); return; } diff --git a/src/nix/ls.cc b/src/nix/ls.cc index e964b01b3..c990a303c 100644 --- a/src/nix/ls.cc +++ b/src/nix/ls.cc @@ -93,7 +93,7 @@ struct MixLs : virtual Args, MixJSON if (json) { if (showDirectory) throw UsageError("'--directory' is useless with '--json'"); - std::cout << listNar(accessor, path, recursive); + logger->cout("%s", listNar(accessor, path, recursive)); } else listText(accessor); } diff --git a/src/nix/main.cc b/src/nix/main.cc index d3d2f5b16..53bf649d4 100644 --- a/src/nix/main.cc +++ b/src/nix/main.cc @@ -292,7 +292,7 @@ void mainWrapped(int argc, char * * argv) NixArgs args; if (argc == 2 && std::string(argv[1]) == "__dump-args") { - std::cout << args.toJSON().dump() << "\n"; + logger->cout("%s", args.toJSON()); return; } @@ -312,7 +312,7 @@ void mainWrapped(int argc, char * * argv) b["doc"] = trim(stripIndentation(primOp->doc)); res[state.symbols[builtin.name]] = std::move(b); } - std::cout << res.dump() << "\n"; + logger->cout("%s", res); return; } @@ -321,14 +321,14 @@ void mainWrapped(int argc, char * * argv) if (completions) { switch (completionType) { case ctNormal: - std::cout << "normal\n"; break; + logger->cout("normal"); break; case ctFilenames: - std::cout << "filenames\n"; break; + logger->cout("filenames"); break; case ctAttrs: - std::cout << "attrs\n"; break; + logger->cout("attrs"); break; } for (auto & s : *completions) - std::cout << s.completion << "\t" << trim(s.description) << "\n"; + logger->cout(s.completion + "\t" + trim(s.description)); } }); diff --git a/src/nix/make-content-addressed.cc b/src/nix/make-content-addressed.cc index d86b90fc7..6693c55ac 100644 --- a/src/nix/make-content-addressed.cc +++ b/src/nix/make-content-addressed.cc @@ -45,7 +45,7 @@ struct CmdMakeContentAddressed : virtual CopyCommand, virtual StorePathsCommand, } auto json = json::object(); json["rewrites"] = jsonRewrites; - std::cout << json.dump(); + logger->cout("%s", json); } else { for (auto & path : storePaths) { auto i = remappings.find(path); diff --git a/src/nix/prefetch.cc b/src/nix/prefetch.cc index fc3823406..51c8a3319 100644 --- a/src/nix/prefetch.cc +++ b/src/nix/prefetch.cc @@ -234,9 +234,9 @@ static int main_nix_prefetch_url(int argc, char * * argv) if (!printPath) printInfo("path is '%s'", store->printStorePath(storePath)); - std::cout << printHash16or32(hash) << std::endl; + logger->cout(printHash16or32(hash)); if (printPath) - std::cout << store->printStorePath(storePath) << std::endl; + logger->cout(store->printStorePath(storePath)); return 0; } diff --git a/src/nix/profile.cc b/src/nix/profile.cc index 711fbe2f0..eef33b3d9 100644 --- a/src/nix/profile.cc +++ b/src/nix/profile.cc @@ -228,12 +228,12 @@ struct ProfileManifest while (i != prevElems.end() || j != curElems.end()) { if (j != curElems.end() && (i == prevElems.end() || i->describe() > j->describe())) { - std::cout << fmt("%s%s: ∅ -> %s\n", indent, j->describe(), j->versions()); + logger->cout("%s%s: ∅ -> %s", indent, j->describe(), j->versions()); changes = true; ++j; } else if (i != prevElems.end() && (j == curElems.end() || i->describe() < j->describe())) { - std::cout << fmt("%s%s: %s -> ∅\n", indent, i->describe(), i->versions()); + logger->cout("%s%s: %s -> ∅", indent, i->describe(), i->versions()); changes = true; ++i; } @@ -241,7 +241,7 @@ struct ProfileManifest auto v1 = i->versions(); auto v2 = j->versions(); if (v1 != v2) { - std::cout << fmt("%s%s: %s -> %s\n", indent, i->describe(), v1, v2); + logger->cout("%s%s: %s -> %s", indent, i->describe(), v1, v2); changes = true; } ++i; @@ -250,7 +250,7 @@ struct ProfileManifest } if (!changes) - std::cout << fmt("%sNo changes.\n", indent); + logger->cout("%sNo changes.", indent); } }; @@ -640,9 +640,9 @@ struct CmdProfileDiffClosures : virtual StoreCommand, MixDefaultProfile for (auto & gen : gens) { if (prevGen) { - if (!first) std::cout << "\n"; + if (!first) logger->cout(""); first = false; - std::cout << fmt("Version %d -> %d:\n", prevGen->number, gen.number); + logger->cout("Version %d -> %d:", prevGen->number, gen.number); printClosureDiff(store, store->followLinksToStorePath(prevGen->path), store->followLinksToStorePath(gen.path), @@ -678,10 +678,10 @@ struct CmdProfileHistory : virtual StoreCommand, EvalCommand, MixDefaultProfile for (auto & gen : gens) { ProfileManifest manifest(*getEvalState(), gen.path); - if (!first) std::cout << "\n"; + if (!first) logger->cout(""); first = false; - std::cout << fmt("Version %s%d" ANSI_NORMAL " (%s)%s:\n", + logger->cout("Version %s%d" ANSI_NORMAL " (%s)%s:", gen.number == curGen ? ANSI_GREEN : ANSI_BOLD, gen.number, std::put_time(std::gmtime(&gen.creationTime), "%Y-%m-%d"), diff --git a/src/nix/realisation.cc b/src/nix/realisation.cc index c9a7157cd..0d3466515 100644 --- a/src/nix/realisation.cc +++ b/src/nix/realisation.cc @@ -65,18 +65,16 @@ struct CmdRealisationInfo : BuiltPathsCommand, MixJSON res.push_back(currentPath); } - std::cout << res.dump(); + logger->cout("%s", res); } else { for (auto & path : realisations) { if (auto realisation = std::get_if(&path.raw)) { - std::cout << - realisation->id.to_string() << " " << - store->printStorePath(realisation->outPath); + logger->cout("%s %s", + realisation->id.to_string(), + store->printStorePath(realisation->outPath)); } else - std::cout << store->printStorePath(path.path()); - - std::cout << std::endl; + logger->cout("%s", store->printStorePath(path.path())); } } } diff --git a/src/nix/search.cc b/src/nix/search.cc index 4fa1e7837..2e38f7e4b 100644 --- a/src/nix/search.cc +++ b/src/nix/search.cc @@ -196,9 +196,8 @@ struct CmdSearch : InstallableCommand, MixJSON for (auto & cursor : installable->getCursors(*state)) visit(*cursor, cursor->getAttrPath(), true); - if (json) { - std::cout << jsonOut->dump() << std::endl; - } + if (json) + logger->cout("%s", *jsonOut); if (!json && !results) throw Error("no results for the given search term(s)!"); diff --git a/src/nix/show-derivation.cc b/src/nix/show-derivation.cc index d1a516cad..520e8b1ce 100644 --- a/src/nix/show-derivation.cc +++ b/src/nix/show-derivation.cc @@ -57,7 +57,7 @@ struct CmdShowDerivation : InstallablesCommand jsonRoot[store->printStorePath(drvPath)] = store->readDerivation(drvPath).toJSON(*store); } - std::cout << jsonRoot.dump(2) << std::endl; + logger->cout(jsonRoot.dump(2)); } }; diff --git a/src/nix/sigs.cc b/src/nix/sigs.cc index 3d659d6d2..ee27e3725 100644 --- a/src/nix/sigs.cc +++ b/src/nix/sigs.cc @@ -173,7 +173,7 @@ struct CmdKeyGenerateSecret : Command if (!keyName) throw UsageError("required argument '--key-name' is missing"); - std::cout << SecretKey::generate(*keyName).to_string(); + writeFull(STDOUT_FILENO, SecretKey::generate(*keyName).to_string()); } }; @@ -194,7 +194,7 @@ struct CmdKeyConvertSecretToPublic : Command void run() override { SecretKey secretKey(drainFD(STDIN_FILENO)); - std::cout << secretKey.toPublicKey().to_string(); + writeFull(STDOUT_FILENO, secretKey.toPublicKey().to_string()); } }; -- cgit v1.2.3 From 29abc8e7647cd9ec6ef5a1df3fcb1dcf4a4495a2 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 2 Mar 2023 15:44:19 +0100 Subject: Remove FormatOrString and remaining uses of format() --- src/build-remote/build-remote.cc | 2 +- src/libexpr/eval.cc | 4 ++-- src/libexpr/parser.y | 4 ++-- src/libexpr/tests/primops.cc | 4 ++-- src/libexpr/value-to-xml.cc | 8 ++++---- src/libmain/progress-bar.cc | 6 +++--- src/libmain/shared.cc | 2 +- src/libstore/build/derivation-goal.cc | 2 +- src/libstore/build/goal.cc | 4 ++-- src/libstore/build/goal.hh | 2 +- src/libstore/build/local-derivation-goal.cc | 7 +++---- src/libstore/daemon.cc | 4 ++-- src/libstore/export-import.cc | 4 ++-- src/libstore/filetransfer.cc | 4 ++-- src/libstore/gc.cc | 9 ++++----- src/libstore/local-store.cc | 6 +++--- src/libstore/optimise-store.cc | 17 ++++++++--------- src/libstore/pathlocks.cc | 8 ++++---- src/libstore/profiles.cc | 4 ++-- src/libstore/references.cc | 3 +-- src/libstore/s3-binary-cache-store.cc | 12 ++++++------ src/libstore/store-api.cc | 4 ++-- src/libutil/archive.cc | 4 ++-- src/libutil/filesystem.cc | 4 ++-- src/libutil/fmt.hh | 15 --------------- src/libutil/logging.cc | 8 ++++---- src/libutil/logging.hh | 6 +++--- src/libutil/serialise.cc | 4 ++-- src/libutil/util.cc | 10 +++++----- src/libutil/util.hh | 2 +- src/nix-build/nix-build.cc | 4 ++-- src/nix-collect-garbage/nix-collect-garbage.cc | 2 +- src/nix-copy-closure/nix-copy-closure.cc | 2 +- src/nix-env/nix-env.cc | 22 +++++++++++----------- src/nix-env/user-env.cc | 4 ++-- src/nix-store/nix-store.cc | 2 +- src/nix/daemon.cc | 6 +++--- src/nix/doctor.cc | 2 +- src/nix/sigs.cc | 2 +- .../resolve-system-dependencies.cc | 8 ++------ 40 files changed, 102 insertions(+), 125 deletions(-) (limited to 'src') diff --git a/src/build-remote/build-remote.cc b/src/build-remote/build-remote.cc index 174435e7c..63e3e3fa9 100644 --- a/src/build-remote/build-remote.cc +++ b/src/build-remote/build-remote.cc @@ -219,7 +219,7 @@ static int main_build_remote(int argc, char * * argv) % concatStringsSep(", ", m.supportedFeatures) % concatStringsSep(", ", m.mandatoryFeatures); - printMsg(couldBuildLocally ? lvlChatty : lvlWarn, error); + printMsg(couldBuildLocally ? lvlChatty : lvlWarn, error.str()); std::cerr << "# decline\n"; } diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 21fc4d0fe..2721b6733 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -368,7 +368,7 @@ void initGC() size = (pageSize * pages) / 4; // 25% of RAM if (size > maxSize) size = maxSize; #endif - debug(format("setting initial heap size to %1% bytes") % size); + debug("setting initial heap size to %1% bytes", size); GC_expand_hp(size); } @@ -609,7 +609,7 @@ Path EvalState::checkSourcePath(const Path & path_) } /* Resolve symlinks. */ - debug(format("checking access to '%s'") % abspath); + debug("checking access to '%s'", abspath); Path path = canonPath(abspath, true); for (auto & i : *allowedPaths) { diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index dec5818fc..0f75ed9a0 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -732,7 +732,7 @@ Expr * EvalState::parseExprFromString(std::string s, const Path & basePath) Expr * EvalState::parseStdin() { - //Activity act(*logger, lvlTalkative, format("parsing standard input")); + //Activity act(*logger, lvlTalkative, "parsing standard input"); auto buffer = drainFD(0); // drainFD should have left some extra space for terminators buffer.append("\0\0", 2); @@ -835,7 +835,7 @@ std::pair EvalState::resolveSearchPathElem(const SearchPathEl } } - debug(format("resolved search path element '%s' to '%s'") % elem.second % res.second); + debug("resolved search path element '%s' to '%s'", elem.second, res.second); searchPathResolved[elem.second] = res; return res; diff --git a/src/libexpr/tests/primops.cc b/src/libexpr/tests/primops.cc index e1d3ac503..ce3b5d11f 100644 --- a/src/libexpr/tests/primops.cc +++ b/src/libexpr/tests/primops.cc @@ -15,8 +15,8 @@ namespace nix { return oss.str(); } - void log(Verbosity lvl, const FormatOrString & fs) override { - oss << fs.s << std::endl; + void log(Verbosity lvl, std::string_view s) override { + oss << s << std::endl; } void logEI(const ErrorInfo & ei) override { diff --git a/src/libexpr/value-to-xml.cc b/src/libexpr/value-to-xml.cc index 3f6222768..341c8922f 100644 --- a/src/libexpr/value-to-xml.cc +++ b/src/libexpr/value-to-xml.cc @@ -26,8 +26,8 @@ static void posToXML(EvalState & state, XMLAttrs & xmlAttrs, const Pos & pos) { if (auto path = std::get_if(&pos.origin)) xmlAttrs["path"] = *path; - xmlAttrs["line"] = (format("%1%") % pos.line).str(); - xmlAttrs["column"] = (format("%1%") % pos.column).str(); + xmlAttrs["line"] = fmt("%1%", pos.line); + xmlAttrs["column"] = fmt("%1%", pos.column); } @@ -64,7 +64,7 @@ static void printValueAsXML(EvalState & state, bool strict, bool location, switch (v.type()) { case nInt: - doc.writeEmptyElement("int", singletonAttrs("value", (format("%1%") % v.integer).str())); + doc.writeEmptyElement("int", singletonAttrs("value", fmt("%1%", v.integer))); break; case nBool: @@ -156,7 +156,7 @@ static void printValueAsXML(EvalState & state, bool strict, bool location, break; case nFloat: - doc.writeEmptyElement("float", singletonAttrs("value", (format("%1%") % v.fpoint).str())); + doc.writeEmptyElement("float", singletonAttrs("value", fmt("%1%", v.fpoint))); break; case nThunk: diff --git a/src/libmain/progress-bar.cc b/src/libmain/progress-bar.cc index e9205a5e5..024259584 100644 --- a/src/libmain/progress-bar.cc +++ b/src/libmain/progress-bar.cc @@ -125,11 +125,11 @@ public: return printBuildLogs; } - void log(Verbosity lvl, const FormatOrString & fs) override + void log(Verbosity lvl, std::string_view s) override { if (lvl > verbosity) return; auto state(state_.lock()); - log(*state, lvl, fs.s); + log(*state, lvl, s); } void logEI(const ErrorInfo & ei) override @@ -142,7 +142,7 @@ public: log(*state, ei.level, oss.str()); } - void log(State & state, Verbosity lvl, const std::string & s) + void log(State & state, Verbosity lvl, std::string_view s) { if (state.active) { writeToStderr("\r\e[K" + filterANSIEscapes(s, !isTTY) + ANSI_NORMAL "\n"); diff --git a/src/libmain/shared.cc b/src/libmain/shared.cc index d4871a8e2..27552d5bf 100644 --- a/src/libmain/shared.cc +++ b/src/libmain/shared.cc @@ -347,7 +347,7 @@ void parseCmdLine(const std::string & programName, const Strings & args, void printVersion(const std::string & programName) { - std::cout << format("%1% (Nix) %2%") % programName % nixVersion << std::endl; + std::cout << fmt("%1% (Nix) %2%", programName, nixVersion) << std::endl; if (verbosity > lvlInfo) { Strings cfg; #if HAVE_BOEHMGC diff --git a/src/libstore/build/derivation-goal.cc b/src/libstore/build/derivation-goal.cc index 2021d0023..38b73d531 100644 --- a/src/libstore/build/derivation-goal.cc +++ b/src/libstore/build/derivation-goal.cc @@ -732,7 +732,7 @@ void replaceValidPath(const Path & storePath, const Path & tmpPath) tmpPath (the replacement), so we have to move it out of the way first. We'd better not be interrupted here, because if we're repairing (say) Glibc, we end up with a broken system. */ - Path oldPath = (format("%1%.old-%2%-%3%") % storePath % getpid() % random()).str(); + Path oldPath = fmt("%1%.old-%2%-%3%", storePath, getpid(), random()); if (pathExists(storePath)) movePath(storePath, oldPath); diff --git a/src/libstore/build/goal.cc b/src/libstore/build/goal.cc index 58e805f55..d59b94797 100644 --- a/src/libstore/build/goal.cc +++ b/src/libstore/build/goal.cc @@ -78,9 +78,9 @@ void Goal::amDone(ExitCode result, std::optional ex) } -void Goal::trace(const FormatOrString & fs) +void Goal::trace(std::string_view s) { - debug("%1%: %2%", name, fs.s); + debug("%1%: %2%", name, s); } } diff --git a/src/libstore/build/goal.hh b/src/libstore/build/goal.hh index 35121c5d9..776eb86bc 100644 --- a/src/libstore/build/goal.hh +++ b/src/libstore/build/goal.hh @@ -88,7 +88,7 @@ struct Goal : public std::enable_shared_from_this abort(); } - void trace(const FormatOrString & fs); + void trace(std::string_view s); std::string getName() { diff --git a/src/libstore/build/local-derivation-goal.cc b/src/libstore/build/local-derivation-goal.cc index a961d8eed..d17715efd 100644 --- a/src/libstore/build/local-derivation-goal.cc +++ b/src/libstore/build/local-derivation-goal.cc @@ -650,7 +650,7 @@ void LocalDerivationGoal::startBuilder() /* Clean up the chroot directory automatically. */ autoDelChroot = std::make_shared(chrootRootDir); - printMsg(lvlChatty, format("setting up chroot environment in '%1%'") % chrootRootDir); + printMsg(lvlChatty, "setting up chroot environment in '%1%'", chrootRootDir); // FIXME: make this 0700 if (mkdir(chrootRootDir.c_str(), buildUser && buildUser->getUIDCount() != 1 ? 0755 : 0750) == -1) @@ -753,8 +753,7 @@ void LocalDerivationGoal::startBuilder() throw Error("home directory '%1%' exists; please remove it to assure purity of builds without sandboxing", homeDir); if (useChroot && settings.preBuildHook != "" && dynamic_cast(drv.get())) { - printMsg(lvlChatty, format("executing pre-build hook '%1%'") - % settings.preBuildHook); + printMsg(lvlChatty, "executing pre-build hook '%1%'", settings.preBuildHook); auto args = useChroot ? Strings({worker.store.printStorePath(drvPath), chrootRootDir}) : Strings({ worker.store.printStorePath(drvPath) }); enum BuildHookState { @@ -1104,7 +1103,7 @@ void LocalDerivationGoal::initEnv() env["NIX_STORE"] = worker.store.storeDir; /* The maximum number of cores to utilize for parallel building. */ - env["NIX_BUILD_CORES"] = (format("%d") % settings.buildCores).str(); + env["NIX_BUILD_CORES"] = fmt("%d", settings.buildCores); initTmpDir(); diff --git a/src/libstore/daemon.cc b/src/libstore/daemon.cc index 5e6fd011f..7f8b0f905 100644 --- a/src/libstore/daemon.cc +++ b/src/libstore/daemon.cc @@ -67,12 +67,12 @@ struct TunnelLogger : public Logger state->pendingMsgs.push_back(s); } - void log(Verbosity lvl, const FormatOrString & fs) override + void log(Verbosity lvl, std::string_view s) override { if (lvl > verbosity) return; StringSink buf; - buf << STDERR_NEXT << (fs.s + "\n"); + buf << STDERR_NEXT << (s + "\n"); enqueueMsg(buf.s); } diff --git a/src/libstore/export-import.cc b/src/libstore/export-import.cc index 9875da909..4eb838b68 100644 --- a/src/libstore/export-import.cc +++ b/src/libstore/export-import.cc @@ -16,7 +16,7 @@ void Store::exportPaths(const StorePathSet & paths, Sink & sink) //logger->incExpected(doneLabel, sorted.size()); for (auto & path : sorted) { - //Activity act(*logger, lvlInfo, format("exporting path '%s'") % path); + //Activity act(*logger, lvlInfo, "exporting path '%s'", path); sink << 1; exportPath(path, sink); //logger->incProgress(doneLabel); @@ -71,7 +71,7 @@ StorePaths Store::importPaths(Source & source, CheckSigsFlag checkSigs) auto path = parseStorePath(readString(source)); - //Activity act(*logger, lvlInfo, format("importing path '%s'") % info.path); + //Activity act(*logger, lvlInfo, "importing path '%s'", info.path); auto references = worker_proto::read(*this, source, Phantom {}); auto deriver = readString(source); diff --git a/src/libstore/filetransfer.cc b/src/libstore/filetransfer.cc index 1c8676a59..404f980b6 100644 --- a/src/libstore/filetransfer.cc +++ b/src/libstore/filetransfer.cc @@ -183,7 +183,7 @@ struct curlFileTransfer : public FileTransfer { size_t realSize = size * nmemb; std::string line((char *) contents, realSize); - printMsg(lvlVomit, format("got header for '%s': %s") % request.uri % trim(line)); + printMsg(lvlVomit, "got header for '%s': %s", request.uri, trim(line)); static std::regex statusLine("HTTP/[^ ]+ +[0-9]+(.*)", std::regex::extended | std::regex::icase); std::smatch match; if (std::regex_match(line, match, statusLine)) { @@ -207,7 +207,7 @@ struct curlFileTransfer : public FileTransfer long httpStatus = 0; curl_easy_getinfo(req, CURLINFO_RESPONSE_CODE, &httpStatus); if (result.etag == request.expectedETag && httpStatus == 200) { - debug(format("shutting down on 200 HTTP response with expected ETag")); + debug("shutting down on 200 HTTP response with expected ETag"); return 0; } } else if (name == "content-encoding") diff --git a/src/libstore/gc.cc b/src/libstore/gc.cc index 996f26a95..0aecc2d3b 100644 --- a/src/libstore/gc.cc +++ b/src/libstore/gc.cc @@ -34,8 +34,7 @@ static void makeSymlink(const Path & link, const Path & target) createDirs(dirOf(link)); /* Create the new symlink. */ - Path tempLink = (format("%1%.tmp-%2%-%3%") - % link % getpid() % random()).str(); + Path tempLink = fmt("%1%.tmp-%2%-%3%", link, getpid(), random()); createSymlink(target, tempLink); /* Atomically replace the old one. */ @@ -197,7 +196,7 @@ void LocalStore::findTempRoots(Roots & tempRoots, bool censor) pid_t pid = std::stoi(i.name); - debug(format("reading temporary root file '%1%'") % path); + debug("reading temporary root file '%1%'", path); AutoCloseFD fd(open(path.c_str(), O_CLOEXEC | O_RDWR, 0666)); if (!fd) { /* It's okay if the file has disappeared. */ @@ -263,7 +262,7 @@ void LocalStore::findRoots(const Path & path, unsigned char type, Roots & roots) target = absPath(target, dirOf(path)); if (!pathExists(target)) { if (isInDir(path, stateDir + "/" + gcRootsDir + "/auto")) { - printInfo(format("removing stale link from '%1%' to '%2%'") % path % target); + printInfo("removing stale link from '%1%' to '%2%'", path, target); unlink(path.c_str()); } } else { @@ -863,7 +862,7 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results) continue; } - printMsg(lvlTalkative, format("deleting unused link '%1%'") % path); + printMsg(lvlTalkative, "deleting unused link '%1%'", path); if (unlink(path.c_str()) == -1) throw SysError("deleting '%1%'", path); diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index 82edaa9bf..c9a466ee8 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -280,7 +280,7 @@ LocalStore::LocalStore(const Params & params) else if (curSchema == 0) { /* new store */ curSchema = nixSchemaVersion; openDB(*state, true); - writeFile(schemaPath, (format("%1%") % nixSchemaVersion).str(), 0666, true); + writeFile(schemaPath, fmt("%1%", nixSchemaVersion), 0666, true); } else if (curSchema < nixSchemaVersion) { @@ -329,7 +329,7 @@ LocalStore::LocalStore(const Params & params) txn.commit(); } - writeFile(schemaPath, (format("%1%") % nixSchemaVersion).str(), 0666, true); + writeFile(schemaPath, fmt("%1%", nixSchemaVersion), 0666, true); lockFile(globalLock.get(), ltRead, true); } @@ -1560,7 +1560,7 @@ void LocalStore::invalidatePathChecked(const StorePath & path) bool LocalStore::verifyStore(bool checkContents, RepairFlag repair) { - printInfo(format("reading the Nix store...")); + printInfo("reading the Nix store..."); bool errors = false; diff --git a/src/libstore/optimise-store.cc b/src/libstore/optimise-store.cc index 4d2781180..4a79cf4a1 100644 --- a/src/libstore/optimise-store.cc +++ b/src/libstore/optimise-store.cc @@ -55,7 +55,7 @@ LocalStore::InodeHash LocalStore::loadInodeHash() } if (errno) throw SysError("reading directory '%1%'", linksDir); - printMsg(lvlTalkative, format("loaded %1% hash inodes") % inodeHash.size()); + printMsg(lvlTalkative, "loaded %1% hash inodes", inodeHash.size()); return inodeHash; } @@ -73,7 +73,7 @@ Strings LocalStore::readDirectoryIgnoringInodes(const Path & path, const InodeHa checkInterrupt(); if (inodeHash.count(dirent->d_ino)) { - debug(format("'%1%' is already linked") % dirent->d_name); + debug("'%1%' is already linked", dirent->d_name); continue; } @@ -102,7 +102,7 @@ void LocalStore::optimisePath_(Activity * act, OptimiseStats & stats, if (std::regex_search(path, std::regex("\\.app/Contents/.+$"))) { - debug(format("'%1%' is not allowed to be linked in macOS") % path); + debug("'%1%' is not allowed to be linked in macOS", path); return; } #endif @@ -146,7 +146,7 @@ void LocalStore::optimisePath_(Activity * act, OptimiseStats & stats, contents of the symlink (i.e. the result of readlink()), not the contents of the target (which may not even exist). */ Hash hash = hashPath(htSHA256, path).first; - debug(format("'%1%' has hash '%2%'") % path % hash.to_string(Base32, true)); + debug("'%1%' has hash '%2%'", path, hash.to_string(Base32, true)); /* Check if this is a known hash. */ Path linkPath = linksDir + "/" + hash.to_string(Base32, false); @@ -196,11 +196,11 @@ void LocalStore::optimisePath_(Activity * act, OptimiseStats & stats, auto stLink = lstat(linkPath); if (st.st_ino == stLink.st_ino) { - debug(format("'%1%' is already linked to '%2%'") % path % linkPath); + debug("'%1%' is already linked to '%2%'", path, linkPath); return; } - printMsg(lvlTalkative, format("linking '%1%' to '%2%'") % path % linkPath); + printMsg(lvlTalkative, "linking '%1%' to '%2%'", path, linkPath); /* Make the containing directory writable, but only if it's not the store itself (we don't want or need to mess with its @@ -213,8 +213,7 @@ void LocalStore::optimisePath_(Activity * act, OptimiseStats & stats, its timestamp back to 0. */ MakeReadOnly makeReadOnly(mustToggle ? dirOfPath : ""); - Path tempLink = (format("%1%/.tmp-link-%2%-%3%") - % realStoreDir % getpid() % random()).str(); + Path tempLink = fmt("%1%/.tmp-link-%2%-%3%", realStoreDir, getpid(), random()); if (link(linkPath.c_str(), tempLink.c_str()) == -1) { if (errno == EMLINK) { @@ -222,7 +221,7 @@ void LocalStore::optimisePath_(Activity * act, OptimiseStats & stats, systems). This is likely to happen with empty files. Just shrug and ignore. */ if (st.st_size) - printInfo(format("'%1%' has maximum number of links") % linkPath); + printInfo("'%1%' has maximum number of links", linkPath); return; } throw SysError("cannot link '%1%' to '%2%'", tempLink, linkPath); diff --git a/src/libstore/pathlocks.cc b/src/libstore/pathlocks.cc index 42023cd0a..adc763e6a 100644 --- a/src/libstore/pathlocks.cc +++ b/src/libstore/pathlocks.cc @@ -96,7 +96,7 @@ bool PathLocks::lockPaths(const PathSet & paths, checkInterrupt(); Path lockPath = path + ".lock"; - debug(format("locking path '%1%'") % path); + debug("locking path '%1%'", path); AutoCloseFD fd; @@ -118,7 +118,7 @@ bool PathLocks::lockPaths(const PathSet & paths, } } - debug(format("lock acquired on '%1%'") % lockPath); + debug("lock acquired on '%1%'", lockPath); /* Check that the lock file hasn't become stale (i.e., hasn't been unlinked). */ @@ -130,7 +130,7 @@ bool PathLocks::lockPaths(const PathSet & paths, a lock on a deleted file. This means that other processes may create and acquire a lock on `lockPath', and proceed. So we must retry. */ - debug(format("open lock file '%1%' has become stale") % lockPath); + debug("open lock file '%1%' has become stale", lockPath); else break; } @@ -163,7 +163,7 @@ void PathLocks::unlock() "error (ignored): cannot close lock file on '%1%'", i.second); - debug(format("lock released on '%1%'") % i.second); + debug("lock released on '%1%'", i.second); } fds.clear(); diff --git a/src/libstore/profiles.cc b/src/libstore/profiles.cc index c551c5f3e..179161ff7 100644 --- a/src/libstore/profiles.cc +++ b/src/libstore/profiles.cc @@ -64,7 +64,7 @@ std::pair> findGenerations(Path pro static void makeName(const Path & profile, GenerationNumber num, Path & outLink) { - Path prefix = (format("%1%-%2%") % profile % num).str(); + Path prefix = fmt("%1%-%2%", profile, num); outLink = prefix + "-link"; } @@ -269,7 +269,7 @@ void switchGeneration( void lockProfile(PathLocks & lock, const Path & profile) { - lock.lockPaths({profile}, (format("waiting for lock on profile '%1%'") % profile).str()); + lock.lockPaths({profile}, fmt("waiting for lock on profile '%1%'", profile)); lock.setDeletion(true); } diff --git a/src/libstore/references.cc b/src/libstore/references.cc index 3bb297fc8..345f4528b 100644 --- a/src/libstore/references.cc +++ b/src/libstore/references.cc @@ -39,8 +39,7 @@ static void search( if (!match) continue; std::string ref(s.substr(i, refLength)); if (hashes.erase(ref)) { - debug(format("found reference to '%1%' at offset '%2%'") - % ref % i); + debug("found reference to '%1%' at offset '%2%'", ref, i); seen.insert(ref); } ++i; diff --git a/src/libstore/s3-binary-cache-store.cc b/src/libstore/s3-binary-cache-store.cc index 8d76eee99..8006bd733 100644 --- a/src/libstore/s3-binary-cache-store.cc +++ b/src/libstore/s3-binary-cache-store.cc @@ -40,12 +40,12 @@ struct S3Error : public Error /* Helper: given an Outcome, return R in case of success, or throw an exception in case of an error. */ template -R && checkAws(const FormatOrString & fs, Aws::Utils::Outcome && outcome) +R && checkAws(std::string_view s, Aws::Utils::Outcome && outcome) { if (!outcome.IsSuccess()) throw S3Error( outcome.GetError().GetErrorType(), - fs.s + ": " + outcome.GetError().GetMessage()); + s + ": " + outcome.GetError().GetMessage()); return outcome.GetResultWithOwnership(); } @@ -430,9 +430,9 @@ struct S3BinaryCacheStoreImpl : virtual S3BinaryCacheStoreConfig, public virtual std::string marker; do { - debug(format("listing bucket 's3://%s' from key '%s'...") % bucketName % marker); + debug("listing bucket 's3://%s' from key '%s'...", bucketName, marker); - auto res = checkAws(format("AWS error listing bucket '%s'") % bucketName, + auto res = checkAws(fmt("AWS error listing bucket '%s'", bucketName), s3Helper.client->ListObjects( Aws::S3::Model::ListObjectsRequest() .WithBucket(bucketName) @@ -441,8 +441,8 @@ struct S3BinaryCacheStoreImpl : virtual S3BinaryCacheStoreConfig, public virtual auto & contents = res.GetContents(); - debug(format("got %d keys, next marker '%s'") - % contents.size() % res.GetNextMarker()); + debug("got %d keys, next marker '%s'", + contents.size(), res.GetNextMarker()); for (auto object : contents) { auto & key = object.GetKey(); diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc index f32c2d30c..226eb9113 100644 --- a/src/libstore/store-api.cc +++ b/src/libstore/store-api.cc @@ -790,13 +790,13 @@ std::string Store::makeValidityRegistration(const StorePathSet & paths, if (showHash) { s += info->narHash.to_string(Base16, false) + "\n"; - s += (format("%1%\n") % info->narSize).str(); + s += fmt("%1%\n", info->narSize); } auto deriver = showDerivers && info->deriver ? printStorePath(*info->deriver) : ""; s += deriver + "\n"; - s += (format("%1%\n") % info->references.size()).str(); + s += fmt("%1%\n", info->references.size()); for (auto & j : info->references) s += printStorePath(j) + "\n"; diff --git a/src/libutil/archive.cc b/src/libutil/archive.cc index 0e2b9d12c..268a798d9 100644 --- a/src/libutil/archive.cc +++ b/src/libutil/archive.cc @@ -87,7 +87,7 @@ static time_t dump(const Path & path, Sink & sink, PathFilter & filter) std::string name(i.name); size_t pos = i.name.find(caseHackSuffix); if (pos != std::string::npos) { - debug(format("removing case hack suffix from '%1%'") % (path + "/" + i.name)); + debug("removing case hack suffix from '%1%'", path + "/" + i.name); name.erase(pos); } if (!unhacked.emplace(name, i.name).second) @@ -262,7 +262,7 @@ static void parse(ParseSink & sink, Source & source, const Path & path) if (archiveSettings.useCaseHack) { auto i = names.find(name); if (i != names.end()) { - debug(format("case collision between '%1%' and '%2%'") % i->first % name); + debug("case collision between '%1%' and '%2%'", i->first, name); name += caseHackSuffix; name += std::to_string(++i->second); } else diff --git a/src/libutil/filesystem.cc b/src/libutil/filesystem.cc index 3a732cff8..56be76ecc 100644 --- a/src/libutil/filesystem.cc +++ b/src/libutil/filesystem.cc @@ -15,9 +15,9 @@ static Path tempName(Path tmpRoot, const Path & prefix, bool includePid, { tmpRoot = canonPath(tmpRoot.empty() ? getEnv("TMPDIR").value_or("/tmp") : tmpRoot, true); if (includePid) - return (format("%1%/%2%-%3%-%4%") % tmpRoot % prefix % getpid() % counter++).str(); + return fmt("%1%/%2%-%3%-%4%", tmpRoot, prefix, getpid(), counter++); else - return (format("%1%/%2%-%3%") % tmpRoot % prefix % counter++).str(); + return fmt("%1%/%2%-%3%", tmpRoot, prefix, counter++); } Path createTempDir(const Path & tmpRoot, const Path & prefix, diff --git a/src/libutil/fmt.hh b/src/libutil/fmt.hh index e879fd3b8..e11426b88 100644 --- a/src/libutil/fmt.hh +++ b/src/libutil/fmt.hh @@ -17,16 +17,6 @@ using boost::format; struct nop { template nop(T...) {} }; -struct FormatOrString -{ - std::string s; - FormatOrString(std::string s) : s(std::move(s)) { }; - template - FormatOrString(const F & f) : s(f.str()) { }; - FormatOrString(const char * s) : s(s) { }; -}; - - /* A helper for formatting strings. ‘fmt(format, a_0, ..., a_n)’ is equivalent to ‘boost::format(format) % a_0 % ... % ... a_n’. However, ‘fmt(s)’ is equivalent to ‘s’ (so no %-expansion @@ -53,11 +43,6 @@ inline std::string fmt(const char * s) return s; } -inline std::string fmt(const FormatOrString & fs) -{ - return fs.s; -} - template inline std::string fmt(const std::string & fs, const Args & ... args) { diff --git a/src/libutil/logging.cc b/src/libutil/logging.cc index 56bdaf87a..7cac75ce1 100644 --- a/src/libutil/logging.cc +++ b/src/libutil/logging.cc @@ -54,7 +54,7 @@ public: return printBuildLogs; } - void log(Verbosity lvl, const FormatOrString & fs) override + void log(Verbosity lvl, std::string_view s) override { if (lvl > verbosity) return; @@ -72,7 +72,7 @@ public: prefix = std::string("<") + c + ">"; } - writeToStderr(prefix + filterANSIEscapes(fs.s, !tty) + "\n"); + writeToStderr(prefix + filterANSIEscapes(s, !tty) + "\n"); } void logEI(const ErrorInfo & ei) override @@ -174,12 +174,12 @@ struct JSONLogger : Logger { prevLogger.log(lvlError, "@nix " + json.dump(-1, ' ', false, nlohmann::json::error_handler_t::replace)); } - void log(Verbosity lvl, const FormatOrString & fs) override + void log(Verbosity lvl, std::string_view s) override { nlohmann::json json; json["action"] = "msg"; json["level"] = lvl; - json["msg"] = fs.s; + json["msg"] = s; write(json); } diff --git a/src/libutil/logging.hh b/src/libutil/logging.hh index a68edd15a..59a707eef 100644 --- a/src/libutil/logging.hh +++ b/src/libutil/logging.hh @@ -75,11 +75,11 @@ public: // Whether the logger prints the whole build log virtual bool isVerbose() { return false; } - virtual void log(Verbosity lvl, const FormatOrString & fs) = 0; + virtual void log(Verbosity lvl, std::string_view s) = 0; - void log(const FormatOrString & fs) + void log(std::string_view s) { - log(lvlInfo, fs); + log(lvlInfo, s); } virtual void logEI(const ErrorInfo & ei) = 0; diff --git a/src/libutil/serialise.cc b/src/libutil/serialise.cc index c653db9d0..7476e6f6c 100644 --- a/src/libutil/serialise.cc +++ b/src/libutil/serialise.cc @@ -415,7 +415,7 @@ Error readError(Source & source) auto msg = readString(source); ErrorInfo info { .level = level, - .msg = hintformat(std::move(format("%s") % msg)), + .msg = hintformat(fmt("%s", msg)), }; auto havePos = readNum(source); assert(havePos == 0); @@ -424,7 +424,7 @@ Error readError(Source & source) havePos = readNum(source); assert(havePos == 0); info.traces.push_back(Trace { - .hint = hintformat(std::move(format("%s") % readString(source))) + .hint = hintformat(fmt("%s", readString(source))) }); } return Error(std::move(info)); diff --git a/src/libutil/util.cc b/src/libutil/util.cc index 885bae69c..e4af702a3 100644 --- a/src/libutil/util.cc +++ b/src/libutil/util.cc @@ -523,7 +523,7 @@ void deletePath(const Path & path) void deletePath(const Path & path, uint64_t & bytesFreed) { - //Activity act(*logger, lvlDebug, format("recursively deleting path '%1%'") % path); + //Activity act(*logger, lvlDebug, "recursively deleting path '%1%'", path); bytesFreed = 0; _deletePath(path, bytesFreed); } @@ -1394,14 +1394,14 @@ std::string statusToString(int status) { if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { if (WIFEXITED(status)) - return (format("failed with exit code %1%") % WEXITSTATUS(status)).str(); + return fmt("failed with exit code %1%", WEXITSTATUS(status)); else if (WIFSIGNALED(status)) { int sig = WTERMSIG(status); #if HAVE_STRSIGNAL const char * description = strsignal(sig); - return (format("failed due to signal %1% (%2%)") % sig % description).str(); + return fmt("failed due to signal %1% (%2%)", sig, description); #else - return (format("failed due to signal %1%") % sig).str(); + return fmt("failed due to signal %1%", sig); #endif } else @@ -1470,7 +1470,7 @@ bool shouldANSI() && !getEnv("NO_COLOR").has_value(); } -std::string filterANSIEscapes(const std::string & s, bool filterAll, unsigned int width) +std::string filterANSIEscapes(std::string_view s, bool filterAll, unsigned int width) { std::string t, e; size_t w = 0; diff --git a/src/libutil/util.hh b/src/libutil/util.hh index b5625ecef..75959d7e3 100644 --- a/src/libutil/util.hh +++ b/src/libutil/util.hh @@ -569,7 +569,7 @@ bool shouldANSI(); some escape sequences (such as colour setting) are copied but not included in the character count. Also, tabs are expanded to spaces. */ -std::string filterANSIEscapes(const std::string & s, +std::string filterANSIEscapes(std::string_view s, bool filterAll = false, unsigned int width = std::numeric_limits::max()); diff --git a/src/nix-build/nix-build.cc b/src/nix-build/nix-build.cc index da76c2ace..a4b3b1f96 100644 --- a/src/nix-build/nix-build.cc +++ b/src/nix-build/nix-build.cc @@ -219,9 +219,9 @@ static void main_nix_build(int argc, char * * argv) // read the shebang to understand which packages to read from. Since // this is handled via nix-shell -p, we wrap our ruby script execution // in ruby -e 'load' which ignores the shebangs. - envCommand = (format("exec %1% %2% -e 'load(ARGV.shift)' -- %3% %4%") % execArgs % interpreter % shellEscape(script) % joined.str()).str(); + envCommand = fmt("exec %1% %2% -e 'load(ARGV.shift)' -- %3% %4%", execArgs, interpreter, shellEscape(script), joined.str()); } else { - envCommand = (format("exec %1% %2% %3% %4%") % execArgs % interpreter % shellEscape(script) % joined.str()).str(); + envCommand = fmt("exec %1% %2% %3% %4%", execArgs, interpreter, shellEscape(script), joined.str()); } } diff --git a/src/nix-collect-garbage/nix-collect-garbage.cc b/src/nix-collect-garbage/nix-collect-garbage.cc index e413faffe..8a4fdcb93 100644 --- a/src/nix-collect-garbage/nix-collect-garbage.cc +++ b/src/nix-collect-garbage/nix-collect-garbage.cc @@ -40,7 +40,7 @@ void removeOldGenerations(std::string dir) throw; } if (link.find("link") != std::string::npos) { - printInfo(format("removing old generations of profile %1%") % path); + printInfo("removing old generations of profile %s", path); if (deleteOlderThan != "") deleteGenerationsOlderThan(path, deleteOlderThan, dryRun); else diff --git a/src/nix-copy-closure/nix-copy-closure.cc b/src/nix-copy-closure/nix-copy-closure.cc index 841d50fd3..7f2bb93b6 100755 --- a/src/nix-copy-closure/nix-copy-closure.cc +++ b/src/nix-copy-closure/nix-copy-closure.cc @@ -22,7 +22,7 @@ static int main_nix_copy_closure(int argc, char ** argv) printVersion("nix-copy-closure"); else if (*arg == "--gzip" || *arg == "--bzip2" || *arg == "--xz") { if (*arg != "--gzip") - printMsg(lvlError, format("Warning: '%1%' is not implemented, falling back to gzip") % *arg); + warn("'%1%' is not implemented, falling back to gzip", *arg); gzip = true; } else if (*arg == "--from") toMode = false; diff --git a/src/nix-env/nix-env.cc b/src/nix-env/nix-env.cc index 0daf374de..3a012638b 100644 --- a/src/nix-env/nix-env.cc +++ b/src/nix-env/nix-env.cc @@ -500,7 +500,7 @@ static bool keep(DrvInfo & drv) static void installDerivations(Globals & globals, const Strings & args, const Path & profile) { - debug(format("installing derivations")); + debug("installing derivations"); /* Get the set of user environment elements to be installed. */ DrvInfos newElems, newElemsTmp; @@ -579,7 +579,7 @@ typedef enum { utLt, utLeq, utEq, utAlways } UpgradeType; static void upgradeDerivations(Globals & globals, const Strings & args, UpgradeType upgradeType) { - debug(format("upgrading derivations")); + debug("upgrading derivations"); /* Upgrade works as follows: we take all currently installed derivations, and for any derivation matching any selector, look @@ -768,7 +768,7 @@ static void opSet(Globals & globals, Strings opFlags, Strings opArgs) if (globals.dryRun) return; globals.state->store->buildPaths(paths, globals.state->repair ? bmRepair : bmNormal); - debug(format("switching to new user environment")); + debug("switching to new user environment"); Path generation = createGeneration( ref(store2), globals.profile, @@ -1093,7 +1093,7 @@ static void opQuery(Globals & globals, Strings opFlags, Strings opArgs) try { if (i.hasFailed()) continue; - //Activity act(*logger, lvlDebug, format("outputting query result '%1%'") % i.attrPath); + //Activity act(*logger, lvlDebug, "outputting query result '%1%'", i.attrPath); if (globals.prebuiltOnly && !validPaths.count(i.queryOutPath()) && @@ -1229,11 +1229,11 @@ static void opQuery(Globals & globals, Strings opFlags, Strings opArgs) xml.writeEmptyElement("meta", attrs2); } else if (v->type() == nInt) { attrs2["type"] = "int"; - attrs2["value"] = (format("%1%") % v->integer).str(); + attrs2["value"] = fmt("%1%", v->integer); xml.writeEmptyElement("meta", attrs2); } else if (v->type() == nFloat) { attrs2["type"] = "float"; - attrs2["value"] = (format("%1%") % v->fpoint).str(); + attrs2["value"] = fmt("%1%", v->fpoint); xml.writeEmptyElement("meta", attrs2); } else if (v->type() == nBool) { attrs2["type"] = "bool"; @@ -1337,11 +1337,11 @@ static void opListGenerations(Globals & globals, Strings opFlags, Strings opArgs for (auto & i : gens) { tm t; if (!localtime_r(&i.creationTime, &t)) throw Error("cannot convert time"); - cout << format("%|4| %|4|-%|02|-%|02| %|02|:%|02|:%|02| %||\n") - % i.number - % (t.tm_year + 1900) % (t.tm_mon + 1) % t.tm_mday - % t.tm_hour % t.tm_min % t.tm_sec - % (i.number == curGen ? "(current)" : ""); + logger->cout("%|4| %|4|-%|02|-%|02| %|02|:%|02|:%|02| %||", + i.number, + t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, + t.tm_hour, t.tm_min, t.tm_sec, + i.number == curGen ? "(current)" : ""); } } diff --git a/src/nix-env/user-env.cc b/src/nix-env/user-env.cc index cad7f9c88..745e9e174 100644 --- a/src/nix-env/user-env.cc +++ b/src/nix-env/user-env.cc @@ -41,7 +41,7 @@ bool createUserEnv(EvalState & state, DrvInfos & elems, if (auto drvPath = i.queryDrvPath()) drvsToBuild.push_back({*drvPath}); - debug(format("building user environment dependencies")); + debug("building user environment dependencies"); state.store->buildPaths( toDerivedPaths(drvsToBuild), state.repair ? bmRepair : bmNormal); @@ -159,7 +159,7 @@ bool createUserEnv(EvalState & state, DrvInfos & elems, return false; } - debug(format("switching to new user environment")); + debug("switching to new user environment"); Path generation = createGeneration(ref(store2), profile, topLevelOut); switchLink(profile, generation); } diff --git a/src/nix-store/nix-store.cc b/src/nix-store/nix-store.cc index 3bbefedbe..52211e341 100644 --- a/src/nix-store/nix-store.cc +++ b/src/nix-store/nix-store.cc @@ -457,7 +457,7 @@ static void opPrintEnv(Strings opFlags, Strings opArgs) /* Print each environment variable in the derivation in a format * that can be sourced by the shell. */ for (auto & i : drv.env) - cout << format("export %1%; %1%=%2%\n") % i.first % shellEscape(i.second); + logger->cout("export %1%; %1%=%2%\n", i.first, shellEscape(i.second)); /* Also output the arguments. This doesn't preserve whitespace in arguments. */ diff --git a/src/nix/daemon.cc b/src/nix/daemon.cc index a22bccba1..7e4a7ba86 100644 --- a/src/nix/daemon.cc +++ b/src/nix/daemon.cc @@ -249,9 +249,9 @@ static void daemonLoop() if ((!trusted && !matchUser(user, group, allowedUsers)) || group == settings.buildUsersGroup) throw Error("user '%1%' is not allowed to connect to the Nix daemon", user); - printInfo(format((std::string) "accepted connection from pid %1%, user %2%" + (trusted ? " (trusted)" : "")) - % (peer.pidKnown ? std::to_string(peer.pid) : "") - % (peer.uidKnown ? user : "")); + printInfo((std::string) "accepted connection from pid %1%, user %2%" + (trusted ? " (trusted)" : ""), + peer.pidKnown ? std::to_string(peer.pid) : "", + peer.uidKnown ? user : ""); // Fork a child to handle the connection. ProcessOptions options; diff --git a/src/nix/doctor.cc b/src/nix/doctor.cc index ea87e3d87..7da4549a1 100644 --- a/src/nix/doctor.cc +++ b/src/nix/doctor.cc @@ -18,7 +18,7 @@ std::string formatProtocol(unsigned int proto) if (proto) { auto major = GET_PROTOCOL_MAJOR(proto) >> 8; auto minor = GET_PROTOCOL_MINOR(proto); - return (format("%1%.%2%") % major % minor).str(); + return fmt("%1%.%2%", major, minor); } return "unknown"; } diff --git a/src/nix/sigs.cc b/src/nix/sigs.cc index ee27e3725..1431652e0 100644 --- a/src/nix/sigs.cc +++ b/src/nix/sigs.cc @@ -45,7 +45,7 @@ struct CmdCopySigs : StorePathsCommand //logger->setExpected(doneLabel, storePaths.size()); auto doPath = [&](const Path & storePathS) { - //Activity act(*logger, lvlInfo, format("getting signatures for '%s'") % storePath); + //Activity act(*logger, lvlInfo, "getting signatures for '%s'", storePath); checkInterrupt(); diff --git a/src/resolve-system-dependencies/resolve-system-dependencies.cc b/src/resolve-system-dependencies/resolve-system-dependencies.cc index c6023eb03..4ea268d24 100644 --- a/src/resolve-system-dependencies/resolve-system-dependencies.cc +++ b/src/resolve-system-dependencies/resolve-system-dependencies.cc @@ -157,13 +157,9 @@ int main(int argc, char ** argv) uname(&_uname); - auto cacheParentDir = (format("%1%/dependency-maps") % settings.nixStateDir).str(); + auto cacheParentDir = fmt("%1%/dependency-maps", settings.nixStateDir); - cacheDir = (format("%1%/%2%-%3%-%4%") - % cacheParentDir - % _uname.machine - % _uname.sysname - % _uname.release).str(); + cacheDir = fmt("%1%/%2%-%3%-%4%", cacheParentDir, _uname.machine, _uname.sysname, _uname.release); mkdir(cacheParentDir.c_str(), 0755); mkdir(cacheDir.c_str(), 0755); -- cgit v1.2.3 From 72e1e230517b1e774d2db97cf9d4750e31ebcaa3 Mon Sep 17 00:00:00 2001 From: Jonas Chevalier Date: Thu, 2 Mar 2023 16:17:20 +0100 Subject: Update src/libutil/util.cc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Théophane Hufschmitt <7226587+thufschmitt@users.noreply.github.com> --- src/libutil/util.cc | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'src') diff --git a/src/libutil/util.cc b/src/libutil/util.cc index 5377f093b..c8965baa8 100644 --- a/src/libutil/util.cc +++ b/src/libutil/util.cc @@ -58,9 +58,7 @@ std::optional getEnvNonEmpty(const std::string & key) { auto value = getEnv(key); if (value == "") { // TODO: determine whether this should be a warning or an error. - logWarning({ - .msg = hintfmt("ignoring the '%1%' env variable, its value has been set to \"\"", key) - }); + warn("ignoring the '%1%' env variable, its value has been set to \"\"", key); return std::nullopt; } else { return value; -- cgit v1.2.3 From b96d9c1687066045ce1a045798b64e914cc51fd4 Mon Sep 17 00:00:00 2001 From: zimbatm Date: Fri, 3 Mar 2023 11:32:04 +0100 Subject: fixup: remove warning entirely fixes https://github.com/NixOS/nix/pull/7918/files/72e1e230517b1e774d2db97cf9d4750e31ebcaa3#r1124211067 --- src/libutil/util.cc | 2 -- 1 file changed, 2 deletions(-) (limited to 'src') diff --git a/src/libutil/util.cc b/src/libutil/util.cc index c8965baa8..b70723634 100644 --- a/src/libutil/util.cc +++ b/src/libutil/util.cc @@ -57,8 +57,6 @@ std::optional getEnv(const std::string & key) std::optional getEnvNonEmpty(const std::string & key) { auto value = getEnv(key); if (value == "") { - // TODO: determine whether this should be a warning or an error. - warn("ignoring the '%1%' env variable, its value has been set to \"\"", key); return std::nullopt; } else { return value; -- cgit v1.2.3 From dc8820c71f841df49568099bf3889c7cfb2d92a9 Mon Sep 17 00:00:00 2001 From: zimbatm Date: Fri, 3 Mar 2023 11:34:36 +0100 Subject: fixup: use same style as getEnv --- src/libutil/util.cc | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/libutil/util.cc b/src/libutil/util.cc index b70723634..c0b8f77b0 100644 --- a/src/libutil/util.cc +++ b/src/libutil/util.cc @@ -56,11 +56,8 @@ std::optional getEnv(const std::string & key) std::optional getEnvNonEmpty(const std::string & key) { auto value = getEnv(key); - if (value == "") { - return std::nullopt; - } else { - return value; - } + if (value == "") return {}; + return value; } std::map getEnv() -- cgit v1.2.3 From 2683734936760dad87a33710d0264266aea96ca4 Mon Sep 17 00:00:00 2001 From: Yorick van Pelt Date: Fri, 3 Mar 2023 11:43:47 +0100 Subject: Add talkative msg for coro gc debug --- src/libexpr/eval.cc | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 9667136e8..0d2a3c815 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -367,6 +367,7 @@ void initGC() #if NIX_BOEHM_PATCH_VERSION != 1 + printTalkative("Unpatched BoehmGC, disabling GC inside coroutines"); /* Used to disable GC when entering coroutines on macOS */ create_coro_gc_hook = []() -> std::shared_ptr { return std::make_shared(); -- cgit v1.2.3 From 427555861b871aba02753bdb88a45b3b710a213b Mon Sep 17 00:00:00 2001 From: Sidharth Kshatriya Date: Thu, 23 Feb 2023 11:33:30 +0530 Subject: Print the store paths to be fetched sorted by StorePath name() and not baseName Presently when nix says something like: ``` these 486 paths will be fetched (511.54 MiB download, 6458.64 MiB unpacked): ...path1 ...path2 ...path3 ... ... ...path486 ``` It sorts path1, path2, path3, ..., path486 in lexicographic order of the store path. After this commit, nix will show path1, path2, path3, ..., path486 sorted by StorePath name() (basically everything after the hash) rather than the store path. This makes it easier to review what exactly is being downloaded at a glance, especially when many paths need to be fetched. --- src/libmain/shared.cc | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/libmain/shared.cc b/src/libmain/shared.cc index d4871a8e2..19715ccfa 100644 --- a/src/libmain/shared.cc +++ b/src/libmain/shared.cc @@ -84,8 +84,18 @@ void printMissing(ref store, const StorePathSet & willBuild, downloadSizeMiB, narSizeMiB); } - for (auto & i : willSubstitute) - printMsg(lvl, " %s", store->printStorePath(i)); + std::vector willSubstituteSorted = {}; + std::for_each(willSubstitute.begin(), willSubstitute.end(), + [&](const StorePath &p) { willSubstituteSorted.push_back(&p); }); + std::sort(willSubstituteSorted.begin(), willSubstituteSorted.end(), + [](const StorePath *lhs, const StorePath *rhs) { + if (lhs->name() == rhs->name()) + return lhs->to_string() < rhs->to_string(); + else + return lhs->name() < rhs->name(); + }); + for (auto p : willSubstituteSorted) + printMsg(lvl, " %s", store->printStorePath(*p)); } if (!unknown.empty()) { -- cgit v1.2.3 From 77bb950566d4e3b0d98be73a270805ba543f9432 Mon Sep 17 00:00:00 2001 From: amesgen Date: Sat, 4 Mar 2023 12:39:19 +0100 Subject: InstallableFlake: Apply nix config in `getCursors` --- src/libcmd/installable-flake.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src') diff --git a/src/libcmd/installable-flake.cc b/src/libcmd/installable-flake.cc index 60a97deaf..7b0cc376d 100644 --- a/src/libcmd/installable-flake.cc +++ b/src/libcmd/installable-flake.cc @@ -178,8 +178,7 @@ std::pair InstallableFlake::toValue(EvalState & state) std::vector> InstallableFlake::getCursors(EvalState & state) { - auto evalCache = openEvalCache(state, - std::make_shared(lockFlake(state, flakeRef, lockFlags))); + auto evalCache = openEvalCache(state, getLockedFlake()); auto root = evalCache->getRoot(); -- cgit v1.2.3 From 1e87d5f1ea43be0daa42e9be17184b15f5fbdf07 Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Thu, 19 Jan 2023 12:39:07 +0100 Subject: make descriptions of each installable type an own subsection this is easier to edit, provides anchors for free, and renders correctly on the terminal without additional effort. --- src/nix/nix.md | 166 +++++++++++++++++++++++++++++++-------------------------- 1 file changed, 89 insertions(+), 77 deletions(-) (limited to 'src') diff --git a/src/nix/nix.md b/src/nix/nix.md index db60c59ff..14407428e 100644 --- a/src/nix/nix.md +++ b/src/nix/nix.md @@ -50,100 +50,112 @@ manual](https://nixos.org/manual/nix/stable/). Many `nix` subcommands operate on one or more *installables*. These are command line arguments that represent something that can be built in -the Nix store. Here are the recognised types of installables: - -* **Flake output attributes**: `nixpkgs#hello` - - These have the form *flakeref*[`#`*attrpath*], where *flakeref* is a - flake reference and *attrpath* is an optional attribute path. For - more information on flakes, see [the `nix flake` manual - page](./nix3-flake.md). Flake references are most commonly a flake - identifier in the flake registry (e.g. `nixpkgs`), or a raw path - (e.g. `/path/to/my-flake` or `.` or `../foo`), or a full URL - (e.g. `github:nixos/nixpkgs` or `path:.`) - - When the flake reference is a raw path (a path without any URL - scheme), it is interpreted as a `path:` or `git+file:` url in the following - way: - - - If the path is within a Git repository, then the url will be of the form - `git+file://[GIT_REPO_ROOT]?dir=[RELATIVE_FLAKE_DIR_PATH]` - where `GIT_REPO_ROOT` is the path to the root of the git repository, - and `RELATIVE_FLAKE_DIR_PATH` is the path (relative to the directory - root) of the closest parent of the given path that contains a `flake.nix` within - the git repository. - If no such directory exists, then Nix will error-out. - - Note that the search will only include files indexed by git. In particular, files - which are matched by `.gitignore` or have never been `git add`-ed will not be - available in the flake. If this is undesirable, specify `path:` explicitly; - - For example, if `/foo/bar` is a git repository with the following structure: - ``` - . - └── baz - ├── blah - │  └── file.txt - └── flake.nix - ``` +the Nix store. + +For most commands, if no installable is specified, the default is `.`, +i.e. Nix will operate on the default flake output attribute of the +flake in the current directory. + +Here are the recognised types of installables: + +## Flake output attributes + +Example: `nixpkgs#hello` + +These have the form *flakeref*[`#`*attrpath*], where *flakeref* is a +flake reference and *attrpath* is an optional attribute path. For +more information on flakes, see [the `nix flake` manual +page](./nix3-flake.md). Flake references are most commonly a flake +identifier in the flake registry (e.g. `nixpkgs`), or a raw path +(e.g. `/path/to/my-flake` or `.` or `../foo`), or a full URL +(e.g. `github:nixos/nixpkgs` or `path:.`) + +When the flake reference is a raw path (a path without any URL +scheme), it is interpreted as a `path:` or `git+file:` url in the following +way: + +- If the path is within a Git repository, then the url will be of the form + `git+file://[GIT_REPO_ROOT]?dir=[RELATIVE_FLAKE_DIR_PATH]` + where `GIT_REPO_ROOT` is the path to the root of the git repository, + and `RELATIVE_FLAKE_DIR_PATH` is the path (relative to the directory + root) of the closest parent of the given path that contains a `flake.nix` within + the git repository. + If no such directory exists, then Nix will error-out. + + Note that the search will only include files indexed by git. In particular, files + which are matched by `.gitignore` or have never been `git add`-ed will not be + available in the flake. If this is undesirable, specify `path:` explicitly; + + For example, if `/foo/bar` is a git repository with the following structure: + ``` + . + └── baz + ├── blah + │  └── file.txt + └── flake.nix + ``` Then `/foo/bar/baz/blah` will resolve to `git+file:///foo/bar?dir=baz` - - If the supplied path is not a git repository, then the url will have the form - `path:FLAKE_DIR_PATH` where `FLAKE_DIR_PATH` is the closest parent - of the supplied path that contains a `flake.nix` file (within the same file-system). - If no such directory exists, then Nix will error-out. - - For example, if `/foo/bar/flake.nix` exists, then `/foo/bar/baz/` will resolve to - `path:/foo/bar` +- If the supplied path is not a git repository, then the url will have the form + `path:FLAKE_DIR_PATH` where `FLAKE_DIR_PATH` is the closest parent + of the supplied path that contains a `flake.nix` file (within the same file-system). + If no such directory exists, then Nix will error-out. - If *attrpath* is omitted, Nix tries some default values; for most - subcommands, the default is `packages.`*system*`.default` - (e.g. `packages.x86_64-linux.default`), but some subcommands have - other defaults. If *attrpath* *is* specified, *attrpath* is - interpreted as relative to one or more prefixes; for most - subcommands, these are `packages.`*system*, - `legacyPackages.*system*` and the empty prefix. Thus, on - `x86_64-linux` `nix build nixpkgs#hello` will try to build the - attributes `packages.x86_64-linux.hello`, - `legacyPackages.x86_64-linux.hello` and `hello`. + For example, if `/foo/bar/flake.nix` exists, then `/foo/bar/baz/` will resolve to + `path:/foo/bar` -* **Store paths**: `/nix/store/v5sv61sszx301i0x6xysaqzla09nksnd-hello-2.10` +If *attrpath* is omitted, Nix tries some default values; for most +subcommands, the default is `packages.`*system*`.default` +(e.g. `packages.x86_64-linux.default`), but some subcommands have +other defaults. If *attrpath* *is* specified, *attrpath* is +interpreted as relative to one or more prefixes; for most +subcommands, these are `packages.`*system*, +`legacyPackages.*system*` and the empty prefix. Thus, on +`x86_64-linux` `nix build nixpkgs#hello` will try to build the +attributes `packages.x86_64-linux.hello`, +`legacyPackages.x86_64-linux.hello` and `hello`. - These are paths inside the Nix store, or symlinks that resolve to a - path in the Nix store. +## Store paths -* **Store derivations**: `/nix/store/p7gp6lxdg32h4ka1q398wd9r2zkbbz2v-hello-2.10.drv` +Example: `/nix/store/v5sv61sszx301i0x6xysaqzla09nksnd-hello-2.10` - By default, if you pass a [store derivation] path to a `nix` subcommand, the command will operate on the [output path]s of the derivation. +These are paths inside the Nix store, or symlinks that resolve to a +path in the Nix store. - [output path]: ../../glossary.md#gloss-output-path +## Store derivations - For example, `nix path-info` prints information about the output paths: +Example: `/nix/store/p7gp6lxdg32h4ka1q398wd9r2zkbbz2v-hello-2.10.drv` - ```console - # nix path-info --json /nix/store/p7gp6lxdg32h4ka1q398wd9r2zkbbz2v-hello-2.10.drv - [{"path":"/nix/store/v5sv61sszx301i0x6xysaqzla09nksnd-hello-2.10",…}] - ``` +By default, if you pass a [store derivation] path to a `nix` subcommand, the command will operate on the [output path]s of the derivation. - If you want to operate on the store derivation itself, pass the - `--derivation` flag. +[output path]: ../../glossary.md#gloss-output-path -* **Nix attributes**: `--file /path/to/nixpkgs hello` +For example, `nix path-info` prints information about the output paths: - When the `-f` / `--file` *path* option is given, installables are - interpreted as attribute paths referencing a value returned by - evaluating the Nix file *path*. +```console +# nix path-info --json /nix/store/p7gp6lxdg32h4ka1q398wd9r2zkbbz2v-hello-2.10.drv +[{"path":"/nix/store/v5sv61sszx301i0x6xysaqzla09nksnd-hello-2.10",…}] +``` -* **Nix expressions**: `--expr '(import {}).hello.overrideDerivation (prev: { name = "my-hello"; })'`. +If you want to operate on the store derivation itself, pass the +`--derivation` flag. - When the `--expr` option is given, all installables are interpreted - as Nix expressions. You may need to specify `--impure` if the - expression references impure inputs (such as ``). +## Nix attributes -For most commands, if no installable is specified, the default is `.`, -i.e. Nix will operate on the default flake output attribute of the -flake in the current directory. +Example: `--file /path/to/nixpkgs hello` + +When the `-f` / `--file` *path* option is given, installables are +interpreted as attribute paths referencing a value returned by +evaluating the Nix file *path*. + +## Nix expressions + +Example: `--expr '(import {}).hello.overrideDerivation (prev: { name = "my-hello"; })'`. + +When the `--expr` option is given, all installables are interpreted +as Nix expressions. You may need to specify `--impure` if the +expression references impure inputs (such as ``). ## Derivation output selection -- cgit v1.2.3 From 2af9fd20c62a964ae50bd6c31ee30d57e5be15e8 Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Thu, 1 Dec 2022 01:57:02 +0100 Subject: clarify definition of "installable" the term was hard to discover, as its definition and explanation were in a very long document lacking an overview section. search did not help because it occurs so often. - clarify wording in the definition - add an overview of installable types - add "installable" to glossary - link to definition from occurrences of the term - be more precise about where store derivation outputs are processed - installable Nix expressions must evaluate to a derivation Co-authored-by: Adam Joseph <54836058+amjoseph-nixpkgs@users.noreply.github.com> --- src/libcmd/command.hh | 2 +- src/libcmd/installables.cc | 4 +-- src/nix/build.md | 2 +- src/nix/bundle.md | 2 +- src/nix/develop.md | 2 +- src/nix/eval.md | 2 +- src/nix/flake.md | 4 +-- src/nix/log.md | 3 +-- src/nix/make-content-addressed.md | 4 ++- src/nix/nix.md | 56 +++++++++++++++++++++------------------ src/nix/path-info.md | 2 +- src/nix/print-dev-env.md | 2 +- src/nix/profile-install.md | 2 +- src/nix/run.md | 2 +- src/nix/search.md | 6 ++--- src/nix/shell.md | 2 +- src/nix/show-derivation.md | 2 +- src/nix/store-delete.md | 2 +- src/nix/store-dump-path.md | 2 +- src/nix/store-repair.md | 2 +- src/nix/verify.md | 2 +- 21 files changed, 56 insertions(+), 51 deletions(-) (limited to 'src') diff --git a/src/libcmd/command.hh b/src/libcmd/command.hh index 49c7b4f9b..b8116b151 100644 --- a/src/libcmd/command.hh +++ b/src/libcmd/command.hh @@ -22,7 +22,7 @@ static constexpr Command::Category catSecondary = 100; static constexpr Command::Category catUtility = 101; static constexpr Command::Category catNixInstallation = 102; -static constexpr auto installablesCategory = "Options that change the interpretation of installables"; +static constexpr auto installablesCategory = "Options that change the interpretation of [installables](@docroot@/command-ref/new-cli/nix.md#installables)"; struct NixMultiCommand : virtual MultiCommand, virtual Command { diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc index 7d444aac0..5ecf6293f 100644 --- a/src/libcmd/installables.cc +++ b/src/libcmd/installables.cc @@ -153,7 +153,7 @@ SourceExprCommand::SourceExprCommand() .longName = "file", .shortName = 'f', .description = - "Interpret installables as attribute paths relative to the Nix expression stored in *file*. " + "Interpret [*installables*](@docroot@/command-ref/new-cli/nix.md#installables) as attribute paths relative to the Nix expression stored in *file*. " "If *file* is the character -, then a Nix expression will be read from standard input. " "Implies `--impure`.", .category = installablesCategory, @@ -164,7 +164,7 @@ SourceExprCommand::SourceExprCommand() addFlag({ .longName = "expr", - .description = "Interpret installables as attribute paths relative to the Nix expression *expr*.", + .description = "Interpret [*installables*](@docroot@/command-ref/new-cli/nix.md#installables) as attribute paths relative to the Nix expression *expr*.", .category = installablesCategory, .labels = {"expr"}, .handler = {&expr} diff --git a/src/nix/build.md b/src/nix/build.md index 6a79f308c..ee414dc86 100644 --- a/src/nix/build.md +++ b/src/nix/build.md @@ -82,7 +82,7 @@ R""( # Description -`nix build` builds the specified *installables*. Installables that +`nix build` builds the specified *installables*. [Installables](./nix.md#installables) that resolve to derivations are built (or substituted if possible). Store path installables are substituted. diff --git a/src/nix/bundle.md b/src/nix/bundle.md index a18161a3c..89458aaaa 100644 --- a/src/nix/bundle.md +++ b/src/nix/bundle.md @@ -29,7 +29,7 @@ R""( # Description -`nix bundle`, by default, packs the closure of the *installable* into a single +`nix bundle`, by default, packs the closure of the [*installable*](./nix.md#installables) into a single self-extracting executable. See the [`bundlers` homepage](https://github.com/NixOS/bundlers) for more details. diff --git a/src/nix/develop.md b/src/nix/develop.md index 4e8542d1b..c49b39669 100644 --- a/src/nix/develop.md +++ b/src/nix/develop.md @@ -76,7 +76,7 @@ R""( `nix develop` starts a `bash` shell that provides an interactive build environment nearly identical to what Nix would use to build -*installable*. Inside this shell, environment variables and shell +[*installable*](./nix.md#installables). Inside this shell, environment variables and shell functions are set up so that you can interactively and incrementally build your package. diff --git a/src/nix/eval.md b/src/nix/eval.md index 61334cde1..3b510737a 100644 --- a/src/nix/eval.md +++ b/src/nix/eval.md @@ -50,7 +50,7 @@ R""( # Description -This command evaluates the Nix expression *installable* and prints the +This command evaluates the given Nix expression and prints the result on standard output. # Output format diff --git a/src/nix/flake.md b/src/nix/flake.md index 810e9ebea..9073d0c3b 100644 --- a/src/nix/flake.md +++ b/src/nix/flake.md @@ -275,8 +275,8 @@ Currently the `type` attribute can be one of the following: # Flake format As an example, here is a simple `flake.nix` that depends on the -Nixpkgs flake and provides a single package (i.e. an installable -derivation): +Nixpkgs flake and provides a single package (i.e. an +[installable](./nix.md#installables) derivation): ```nix { diff --git a/src/nix/log.md b/src/nix/log.md index 1c76226a3..01e9801df 100644 --- a/src/nix/log.md +++ b/src/nix/log.md @@ -22,8 +22,7 @@ R""( # Description -This command prints the log of a previous build of the derivation -*installable* on standard output. +This command prints the log of a previous build of the [*installable*](./nix.md#installables) on standard output. Nix looks for build logs in two places: diff --git a/src/nix/make-content-addressed.md b/src/nix/make-content-addressed.md index 32eecc880..b1f7da525 100644 --- a/src/nix/make-content-addressed.md +++ b/src/nix/make-content-addressed.md @@ -35,7 +35,9 @@ R""( # Description This command converts the closure of the store paths specified by -*installables* to content-addressed form. Nix store paths are usually +[*installables*](./nix.md#installables) to content-addressed form. + +Nix store paths are usually *input-addressed*, meaning that the hash part of the store path is computed from the contents of the derivation (i.e., the build-time dependency graph). Input-addressed paths need to be signed by a diff --git a/src/nix/nix.md b/src/nix/nix.md index 14407428e..5da146fc6 100644 --- a/src/nix/nix.md +++ b/src/nix/nix.md @@ -48,22 +48,26 @@ manual](https://nixos.org/manual/nix/stable/). # Installables -Many `nix` subcommands operate on one or more *installables*. These are -command line arguments that represent something that can be built in -the Nix store. +Many `nix` subcommands operate on one or more *installables*. +These are command line arguments that represent something that can be realised in the Nix store. -For most commands, if no installable is specified, the default is `.`, -i.e. Nix will operate on the default flake output attribute of the -flake in the current directory. +The following types of installable are supported by most commands: -Here are the recognised types of installables: +- [Flake output attribute](#flake-output-attribute) +- [Store path](#store-path) +- [Store derivation](#store-derivation) +- [Nix file](#nix-file), optionally qualified by an attribute path +- [Nix expression](#nix-expression), optionally qualified by an attribute path -## Flake output attributes +For most commands, if no installable is specified, `.` as assumed. +That is, Nix will operate on the default flake output attribute of the flake in the current directory. + +### Flake output attribute Example: `nixpkgs#hello` These have the form *flakeref*[`#`*attrpath*], where *flakeref* is a -flake reference and *attrpath* is an optional attribute path. For +[flake reference](./nix3-flake.md#flake-references) and *attrpath* is an optional attribute path. For more information on flakes, see [the `nix flake` manual page](./nix3-flake.md). Flake references are most commonly a flake identifier in the flake registry (e.g. `nixpkgs`), or a raw path @@ -116,46 +120,46 @@ subcommands, these are `packages.`*system*, attributes `packages.x86_64-linux.hello`, `legacyPackages.x86_64-linux.hello` and `hello`. -## Store paths +### Store path Example: `/nix/store/v5sv61sszx301i0x6xysaqzla09nksnd-hello-2.10` -These are paths inside the Nix store, or symlinks that resolve to a -path in the Nix store. +These are paths inside the Nix store, or symlinks that resolve to a path in the Nix store. -## Store derivations +### Store derivation Example: `/nix/store/p7gp6lxdg32h4ka1q398wd9r2zkbbz2v-hello-2.10.drv` -By default, if you pass a [store derivation] path to a `nix` subcommand, the command will operate on the [output path]s of the derivation. +By default, if you pass a [store derivation] path to a `nix` subcommand other than [`show-derivation`](./nix3-show-derivation.md), the command will operate on the [output path]s of the derivation. [output path]: ../../glossary.md#gloss-output-path -For example, `nix path-info` prints information about the output paths: +For example, [`nix path-info`](./nix3-path-info.md) prints information about the output paths: ```console # nix path-info --json /nix/store/p7gp6lxdg32h4ka1q398wd9r2zkbbz2v-hello-2.10.drv [{"path":"/nix/store/v5sv61sszx301i0x6xysaqzla09nksnd-hello-2.10",…}] ``` -If you want to operate on the store derivation itself, pass the -`--derivation` flag. +If you want to operate on the store derivation itself, pass the `--derivation` flag. -## Nix attributes +### Nix file Example: `--file /path/to/nixpkgs hello` -When the `-f` / `--file` *path* option is given, installables are -interpreted as attribute paths referencing a value returned by -evaluating the Nix file *path*. +When the option `-f` / `--file` *path* \[*attrpath*...\] is given, installables are interpreted as the value of the expression in the Nix file at *path*. +If attribute paths are provided, commands will operate on the corresponding values accessible at these paths. +The Nix expression in that file, or any selected attribute, must evaluate to a derivation. + +### Nix expression -## Nix expressions +Example: `--expr 'import {}' hello` -Example: `--expr '(import {}).hello.overrideDerivation (prev: { name = "my-hello"; })'`. +When the option `--expr` *expression* \[*attrpath*...\] is given, installables are interpreted as the value of the of the Nix expression. +If attribute paths are provided, commands will operate on the corresponding values accessible at these paths. +The Nix expression, or any selected attribute, must evaluate to a derivation. -When the `--expr` option is given, all installables are interpreted -as Nix expressions. You may need to specify `--impure` if the -expression references impure inputs (such as ``). +You may need to specify `--impure` if the expression references impure inputs (such as ``). ## Derivation output selection diff --git a/src/nix/path-info.md b/src/nix/path-info.md index b30898ac0..6ad23a02e 100644 --- a/src/nix/path-info.md +++ b/src/nix/path-info.md @@ -80,7 +80,7 @@ R""( # Description This command shows information about the store paths produced by -*installables*, or about all paths in the store if you pass `--all`. +[*installables*](./nix.md#installables), or about all paths in the store if you pass `--all`. By default, this command only prints the store paths. You can get additional information by passing flags such as `--closure-size`, diff --git a/src/nix/print-dev-env.md b/src/nix/print-dev-env.md index 2aad491de..a8ce9d36a 100644 --- a/src/nix/print-dev-env.md +++ b/src/nix/print-dev-env.md @@ -40,7 +40,7 @@ R""( This command prints a shell script that can be sourced by `bash` and that sets the variables and shell functions defined by the build -process of *installable*. This allows you to get a similar build +process of [*installable*](./nix.md#installables). This allows you to get a similar build environment in your current shell rather than in a subshell (as with `nix develop`). diff --git a/src/nix/profile-install.md b/src/nix/profile-install.md index aed414963..4c0f82c09 100644 --- a/src/nix/profile-install.md +++ b/src/nix/profile-install.md @@ -29,6 +29,6 @@ R""( # Description -This command adds *installables* to a Nix profile. +This command adds [*installables*](./nix.md#installables) to a Nix profile. )"" diff --git a/src/nix/run.md b/src/nix/run.md index a0f362076..250ea65aa 100644 --- a/src/nix/run.md +++ b/src/nix/run.md @@ -35,7 +35,7 @@ R""( # Description -`nix run` builds and runs *installable*, which must evaluate to an +`nix run` builds and runs [*installable*](./nix.md#installables), which must evaluate to an *app* or a regular Nix derivation. If *installable* evaluates to an *app* (see below), it executes the diff --git a/src/nix/search.md b/src/nix/search.md index 5a5b5ae05..4caa90654 100644 --- a/src/nix/search.md +++ b/src/nix/search.md @@ -62,10 +62,10 @@ R""( # Description -`nix search` searches *installable* (which must be evaluatable, e.g. a -flake) for packages whose name or description matches all of the +`nix search` searches [*installable*](./nix.md#installables) (which can be evaluated, that is, a +flake or Nix expression, but not a store path or store derivation path) for packages whose name or description matches all of the regular expressions *regex*. For each matching package, It prints the -full attribute name (from the root of the installable), the version +full attribute name (from the root of the [installable](./nix.md#installables)), the version and the `meta.description` field, highlighting the substrings that were matched by the regular expressions. If no regular expressions are specified, all packages are shown. diff --git a/src/nix/shell.md b/src/nix/shell.md index 9fa1031f5..13a389103 100644 --- a/src/nix/shell.md +++ b/src/nix/shell.md @@ -48,7 +48,7 @@ R""( # Description `nix shell` runs a command in an environment in which the `$PATH` variable -provides the specified *installables*. If no command is specified, it starts the +provides the specified [*installables*](./nix.md#installable). If no command is specified, it starts the default shell of your user account specified by `$SHELL`. )"" diff --git a/src/nix/show-derivation.md b/src/nix/show-derivation.md index 2cd93aa62..1d37c6f5a 100644 --- a/src/nix/show-derivation.md +++ b/src/nix/show-derivation.md @@ -39,7 +39,7 @@ R""( # Description This command prints on standard output a JSON representation of the -[store derivation]s to which *installables* evaluate. Store derivations +[store derivation]s to which [*installables*](./nix.md#installables) evaluate. Store derivations are used internally by Nix. They are store paths with extension `.drv` that represent the build-time dependency graph to which a Nix expression evaluates. diff --git a/src/nix/store-delete.md b/src/nix/store-delete.md index db535f87c..431bc5f5e 100644 --- a/src/nix/store-delete.md +++ b/src/nix/store-delete.md @@ -10,7 +10,7 @@ R""( # Description -This command deletes the store paths specified by *installables*. , +This command deletes the store paths specified by [*installables*](./nix.md#installables), but only if it is safe to do so; that is, when the path is not reachable from a root of the garbage collector. This means that you can only delete paths that would also be deleted by `nix store diff --git a/src/nix/store-dump-path.md b/src/nix/store-dump-path.md index 4ef563526..56e2174b6 100644 --- a/src/nix/store-dump-path.md +++ b/src/nix/store-dump-path.md @@ -18,6 +18,6 @@ R""( # Description This command generates a NAR file containing the serialisation of the -store path *installable*. The NAR is written to standard output. +store path [*installable*](./nix.md#installables). The NAR is written to standard output. )"" diff --git a/src/nix/store-repair.md b/src/nix/store-repair.md index 92d2205a9..180c577ac 100644 --- a/src/nix/store-repair.md +++ b/src/nix/store-repair.md @@ -17,7 +17,7 @@ R""( # Description This command attempts to "repair" the store paths specified by -*installables* by redownloading them using the available +[*installables*](./nix.md#installables) by redownloading them using the available substituters. If no substitutes are available, then repair is not possible. diff --git a/src/nix/verify.md b/src/nix/verify.md index 1c43792e7..cc1122c02 100644 --- a/src/nix/verify.md +++ b/src/nix/verify.md @@ -24,7 +24,7 @@ R""( # Description -This command verifies the integrity of the store paths *installables*, +This command verifies the integrity of the store paths [*installables*](./nix.md#installables), or, if `--all` is given, the entire Nix store. For each path, it checks that -- cgit v1.2.3 From f69d453c3eb8355739e21a44680c4c49342cefe2 Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Sun, 5 Mar 2023 04:44:11 +0100 Subject: remove indentation in `fetchGit` attribute listing also reword a confusing sentence and add links to Git terminology --- src/libexpr/primops/fetchTree.cc | 54 +++++++++++++++++++++++----------------- 1 file changed, 31 insertions(+), 23 deletions(-) (limited to 'src') diff --git a/src/libexpr/primops/fetchTree.cc b/src/libexpr/primops/fetchTree.cc index c9faf3ffb..fd51dfb90 100644 --- a/src/libexpr/primops/fetchTree.cc +++ b/src/libexpr/primops/fetchTree.cc @@ -353,36 +353,44 @@ static RegisterPrimOp primop_fetchGit({ of the repo at that URL is fetched. Otherwise, it can be an attribute with the following attributes (all except `url` optional): - - url\ - The URL of the repo. + - `url` - - name\ - The name of the directory the repo should be exported to in the - store. Defaults to the basename of the URL. + The URL of the repo. - - rev\ - The git revision to fetch. Defaults to the tip of `ref`. + - `name` (default: *basename of the URL*) - - ref\ - The git ref to look for the requested revision under. This is - often a branch or tag name. Defaults to `HEAD`. + The name of the directory the repo should be exported to in the store. - By default, the `ref` value is prefixed with `refs/heads/`. As - of Nix 2.3.0 Nix will not prefix `refs/heads/` if `ref` starts - with `refs/`. + - `rev` (default: *the tip of `ref`*) - - submodules\ - A Boolean parameter that specifies whether submodules should be - checked out. Defaults to `false`. + The [Git revision] to fetch. + This is typically a commit hash. - - shallow\ - A Boolean parameter that specifies whether fetching a shallow clone - is allowed. Defaults to `false`. + [Git revision]: https://git-scm.com/docs/git-rev-parse#_specifying_revisions - - allRefs\ - Whether to fetch all refs of the repository. With this argument being - true, it's possible to load a `rev` from *any* `ref` (by default only - `rev`s from the specified `ref` are supported). + - `ref` (default: `HEAD`) + + The [Git reference] under which to look for the requested revision. + This is often a branch or tag name. + + [Git reference]: https://git-scm.com/book/en/v2/Git-Internals-Git-References + + By default, the `ref` value is prefixed with `refs/heads/`. + As of 2.3.0, Nix will not prefix `refs/heads/` if `ref` starts with `refs/`. + + - `submodules` (default: `false`) + + A Boolean parameter that specifies whether submodules should be checked out. + + - `shallow` (default: `false`) + + A Boolean parameter that specifies whether fetching a shallow clone is allowed. + + - `allRefs` + + Whether to fetch all references of the repository. + With this argument being true, it's possible to load a `rev` from *any* `ref` + (by default only `rev`s from the specified `ref` are supported). Here are some examples of how to use `fetchGit`. -- cgit v1.2.3 From dfeb83cac17c1fbc77dc9e4d3f8ea23901d28f8f Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Mon, 6 Mar 2023 03:32:14 +0100 Subject: output paths of store derivations are now addressed explicitly --- src/nix/nix.md | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/nix/nix.md b/src/nix/nix.md index 5da146fc6..0a90fa6c9 100644 --- a/src/nix/nix.md +++ b/src/nix/nix.md @@ -55,7 +55,6 @@ The following types of installable are supported by most commands: - [Flake output attribute](#flake-output-attribute) - [Store path](#store-path) -- [Store derivation](#store-derivation) - [Nix file](#nix-file), optionally qualified by an attribute path - [Nix expression](#nix-expression), optionally qualified by an attribute path @@ -126,22 +125,17 @@ Example: `/nix/store/v5sv61sszx301i0x6xysaqzla09nksnd-hello-2.10` These are paths inside the Nix store, or symlinks that resolve to a path in the Nix store. -### Store derivation +A [store derivation] is also addressed by store path. Example: `/nix/store/p7gp6lxdg32h4ka1q398wd9r2zkbbz2v-hello-2.10.drv` -By default, if you pass a [store derivation] path to a `nix` subcommand other than [`show-derivation`](./nix3-show-derivation.md), the command will operate on the [output path]s of the derivation. +If you want to refer to an output path of that store derivation, add the output name preceded by a caret (`^`). -[output path]: ../../glossary.md#gloss-output-path +Example: `/nix/store/p7gp6lxdg32h4ka1q398wd9r2zkbbz2v-hello-2.10.drv^out` -For example, [`nix path-info`](./nix3-path-info.md) prints information about the output paths: +All outputs can be referred to at once with the special syntax `^*`. -```console -# nix path-info --json /nix/store/p7gp6lxdg32h4ka1q398wd9r2zkbbz2v-hello-2.10.drv -[{"path":"/nix/store/v5sv61sszx301i0x6xysaqzla09nksnd-hello-2.10",…}] -``` - -If you want to operate on the store derivation itself, pass the `--derivation` flag. +Example: `/nix/store/p7gp6lxdg32h4ka1q398wd9r2zkbbz2v-hello-2.10.drv^*` ### Nix file -- cgit v1.2.3 From 762cf7f2144334a0a66c462444595eab0739f044 Mon Sep 17 00:00:00 2001 From: mupdt <25388474+mupdt@users.noreply.github.com> Date: Mon, 6 Mar 2023 10:20:21 -0500 Subject: no-op refactor: extract outLink generation into a function --- src/nix/build.cc | 42 ++++++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 18 deletions(-) (limited to 'src') diff --git a/src/nix/build.cc b/src/nix/build.cc index f4f2ec81d..7240f402b 100644 --- a/src/nix/build.cc +++ b/src/nix/build.cc @@ -41,6 +41,29 @@ nlohmann::json builtPathsWithResultToJSON(const std::vector return res; } +// TODO deduplicate with other code also setting such out links. +static void createOutLinks(const Path& outLink, const std::vector& buildables, LocalFSStore& store2) +{ + for (const auto & [_i, buildable] : enumerate(buildables)) { + auto i = _i; + std::visit(overloaded { + [&](const BuiltPath::Opaque & bo) { + std::string symlink = outLink; + if (i) symlink += fmt("-%d", i); + store2.addPermRoot(bo.path, absPath(symlink)); + }, + [&](const BuiltPath::Built & bfd) { + for (auto & output : bfd.outputs) { + std::string symlink = outLink; + if (i) symlink += fmt("-%d", i); + if (output.first != "out") symlink += fmt("-%s", output.first); + store2.addPermRoot(output.second, absPath(symlink)); + } + }, + }, buildable.path.raw()); + } +} + struct CmdBuild : InstallablesCommand, MixDryRun, MixJSON, MixProfile { Path outLink = "result"; @@ -115,24 +138,7 @@ struct CmdBuild : InstallablesCommand, MixDryRun, MixJSON, MixProfile if (outLink != "") if (auto store2 = store.dynamic_pointer_cast()) - for (const auto & [_i, buildable] : enumerate(buildables)) { - auto i = _i; - std::visit(overloaded { - [&](const BuiltPath::Opaque & bo) { - std::string symlink = outLink; - if (i) symlink += fmt("-%d", i); - store2->addPermRoot(bo.path, absPath(symlink)); - }, - [&](const BuiltPath::Built & bfd) { - for (auto & output : bfd.outputs) { - std::string symlink = outLink; - if (i) symlink += fmt("-%d", i); - if (output.first != "out") symlink += fmt("-%s", output.first); - store2->addPermRoot(output.second, absPath(symlink)); - } - }, - }, buildable.path.raw()); - } + createOutLinks(outLink, buildables, *store2); if (printOutputPaths) { stopProgressBar(); -- cgit v1.2.3 From 2272bc6049e04f98d93f82f22497dc805fbe00cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophane=20Hufschmitt?= Date: Tue, 7 Mar 2023 10:33:21 +0100 Subject: Fix `nix-collect-garbage -d` with the new profile location Low-hanging fix for https://github.com/NixOS/nix/pull/5226#issuecomment-1454669399 --- src/nix-collect-garbage/nix-collect-garbage.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src') diff --git a/src/nix-collect-garbage/nix-collect-garbage.cc b/src/nix-collect-garbage/nix-collect-garbage.cc index 8a4fdcb93..3cc57af4e 100644 --- a/src/nix-collect-garbage/nix-collect-garbage.cc +++ b/src/nix-collect-garbage/nix-collect-garbage.cc @@ -77,8 +77,7 @@ static int main_nix_collect_garbage(int argc, char * * argv) return true; }); - auto profilesDir = settings.nixStateDir + "/profiles"; - if (removeOld) removeOldGenerations(profilesDir); + if (removeOld) removeOldGenerations(profilesDir()); // Run the actual garbage collector. if (!dryRun) { -- cgit v1.2.3 From 7bfed34367f49ea2d4fe52e5861e5969fc9f4b59 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 7 Mar 2023 13:51:02 +0100 Subject: Fix crash/hang with CA derivations The curl download can outlive DrvOutputSubstitutionGoal (if some other error occurs), so at shutdown setting the promise to an exception will fail because 'this' is no longer valid in the callback. This can manifest itself as a segfault, "corrupted double-linked list" or hang. --- src/libstore/build/drv-output-substitution-goal.cc | 23 +++++++++++++--------- src/libstore/build/drv-output-substitution-goal.hh | 12 +++++++---- 2 files changed, 22 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/src/libstore/build/drv-output-substitution-goal.cc b/src/libstore/build/drv-output-substitution-goal.cc index b7f7b5ab1..b30957c84 100644 --- a/src/libstore/build/drv-output-substitution-goal.cc +++ b/src/libstore/build/drv-output-substitution-goal.cc @@ -61,20 +61,25 @@ void DrvOutputSubstitutionGoal::tryNext() // FIXME: Make async // outputInfo = sub->queryRealisation(id); - outPipe.create(); - promise = decltype(promise)(); + + /* The callback of the curl download below can outlive `this` (if + some other error occurs), so it must not touch `this`. So put + the shared state in a separate refcounted object. */ + downloadState = std::make_shared(); + downloadState->outPipe.create(); sub->queryRealisation( - id, { [&](std::future> res) { + id, + { [downloadState(downloadState)](std::future> res) { try { - Finally updateStats([this]() { outPipe.writeSide.close(); }); - promise.set_value(res.get()); + Finally updateStats([&]() { downloadState->outPipe.writeSide.close(); }); + downloadState->promise.set_value(res.get()); } catch (...) { - promise.set_exception(std::current_exception()); + downloadState->promise.set_exception(std::current_exception()); } } }); - worker.childStarted(shared_from_this(), {outPipe.readSide.get()}, true, false); + worker.childStarted(shared_from_this(), {downloadState->outPipe.readSide.get()}, true, false); state = &DrvOutputSubstitutionGoal::realisationFetched; } @@ -84,7 +89,7 @@ void DrvOutputSubstitutionGoal::realisationFetched() worker.childTerminated(this); try { - outputInfo = promise.get_future().get(); + outputInfo = downloadState->promise.get_future().get(); } catch (std::exception & e) { printError(e.what()); substituterFailed = true; @@ -155,7 +160,7 @@ void DrvOutputSubstitutionGoal::work() void DrvOutputSubstitutionGoal::handleEOF(int fd) { - if (fd == outPipe.readSide.get()) worker.wakeUp(shared_from_this()); + if (fd == downloadState->outPipe.readSide.get()) worker.wakeUp(shared_from_this()); } diff --git a/src/libstore/build/drv-output-substitution-goal.hh b/src/libstore/build/drv-output-substitution-goal.hh index 948dbda8f..e4b044790 100644 --- a/src/libstore/build/drv-output-substitution-goal.hh +++ b/src/libstore/build/drv-output-substitution-goal.hh @@ -16,7 +16,7 @@ class Worker; // 2. Substitute the corresponding output path // 3. Register the output info class DrvOutputSubstitutionGoal : public Goal { -private: + // The drv output we're trying to substitue DrvOutput id; @@ -30,9 +30,13 @@ private: /* The current substituter. */ std::shared_ptr sub; - Pipe outPipe; - std::thread thr; - std::promise> promise; + struct DownloadState + { + Pipe outPipe; + std::promise> promise; + }; + + std::shared_ptr downloadState; /* Whether a substituter failed. */ bool substituterFailed = false; -- cgit v1.2.3 From 1607ad5076186bbfbf85a55d222aed09e66dc3fe Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 8 Mar 2023 13:53:20 +0100 Subject: Fix uninitialized readFromStdIn variable This was causing random failures in tests/ca/substitute.ca: 'nix copy --file ./content-addressed.nix' wouldn't get the default installable '.' applied in InstallablesCommand::load(), so it would do nothing. --- src/libcmd/command.hh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/libcmd/command.hh b/src/libcmd/command.hh index b8116b151..0d84c8395 100644 --- a/src/libcmd/command.hh +++ b/src/libcmd/command.hh @@ -128,7 +128,7 @@ struct InstallablesCommand : virtual Args, SourceExprCommand virtual bool useDefaultInstallables() { return true; } - bool readFromStdIn; + bool readFromStdIn = false; std::vector getFlakesForCompletion() override; -- cgit v1.2.3 From 39700c5cbeeb8005bfbe052ea79ababe46d7f072 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophane=20Hufschmitt?= <7226587+thufschmitt@users.noreply.github.com> Date: Wed, 8 Mar 2023 20:47:52 +0100 Subject: Revert "Disable GC during coroutine execution + test" --- src/libexpr/eval.cc | 25 -------- src/libexpr/tests/coro-gc.cc | 147 ------------------------------------------- src/libexpr/tests/local.mk | 2 +- src/libutil/serialise.cc | 39 ++---------- src/libutil/serialise.hh | 6 -- 5 files changed, 6 insertions(+), 213 deletions(-) delete mode 100644 src/libexpr/tests/coro-gc.cc (limited to 'src') diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 3e8857fc8..2721b6733 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -325,22 +325,6 @@ static Symbol getName(const AttrName & name, EvalState & state, Env & env) } } -#if HAVE_BOEHMGC -/* Disable GC while this object lives. Used by CoroutineContext. - * - * Boehm keeps a count of GC_disable() and GC_enable() calls, - * and only enables GC when the count matches. - */ -class BoehmDisableGC { -public: - BoehmDisableGC() { - GC_disable(); - }; - ~BoehmDisableGC() { - GC_enable(); - }; -}; -#endif static bool gcInitialised = false; @@ -365,15 +349,6 @@ void initGC() StackAllocator::defaultAllocator = &boehmGCStackAllocator; - -#if NIX_BOEHM_PATCH_VERSION != 1 - printTalkative("Unpatched BoehmGC, disabling GC inside coroutines"); - /* Used to disable GC when entering coroutines on macOS */ - create_coro_gc_hook = []() -> std::shared_ptr { - return std::make_shared(); - }; -#endif - /* Set the initial heap size to something fairly big (25% of physical RAM, up to a maximum of 384 MiB) so that in most cases we don't need to garbage collect at all. (Collection has a diff --git a/src/libexpr/tests/coro-gc.cc b/src/libexpr/tests/coro-gc.cc deleted file mode 100644 index f848bc2f0..000000000 --- a/src/libexpr/tests/coro-gc.cc +++ /dev/null @@ -1,147 +0,0 @@ -#include -#if HAVE_BOEHMGC -#include - -#include "eval.hh" -#include "serialise.hh" - -#endif - - -namespace nix { -#if HAVE_BOEHMGC - - static void finalizer(void *obj, void *data) { - *((bool*)data) = true; - } - - static bool* make_witness(volatile void* obj) { - /* We can't store the witnesses on the stack, - since they might be collected long afterwards */ - bool* res = (bool*)GC_MALLOC_UNCOLLECTABLE(1); - *res = false; - GC_register_finalizer((void*)obj, finalizer, res, nullptr, nullptr); - return res; - } - - // Generate 2 objects, discard one, run gc, - // see if one got collected and the other didn't - // GC is disabled inside coroutines on __APPLE__ - static void testFinalizerCalls() { - volatile void* do_collect = GC_MALLOC_ATOMIC(128); - volatile void* dont_collect = GC_MALLOC_ATOMIC(128); - - bool* do_collect_witness = make_witness(do_collect); - bool* dont_collect_witness = make_witness(dont_collect); - GC_gcollect(); - GC_invoke_finalizers(); - - ASSERT_TRUE(GC_is_disabled() || *do_collect_witness); - ASSERT_FALSE(*dont_collect_witness); - ASSERT_NE(nullptr, dont_collect); - } - - TEST(CoroGC, BasicFinalizers) { - initGC(); - testFinalizerCalls(); - } - - // Run testFinalizerCalls inside a coroutine - // this tests that GC works as expected inside a coroutine - TEST(CoroGC, CoroFinalizers) { - initGC(); - - auto source = sinkToSource([&](Sink& sink) { - testFinalizerCalls(); - - // pass control to main - writeString("foo", sink); - }); - - // pass control to coroutine - std::string foo = readString(*source); - ASSERT_EQ(foo, "foo"); - } - -#if __APPLE__ - // This test tests that GC is disabled on darwin - // to work around the patch not being sufficient there, - // causing crashes whenever gc is invoked inside a coroutine - TEST(CoroGC, AppleCoroDisablesGC) { - initGC(); - auto source = sinkToSource([&](Sink& sink) { - ASSERT_TRUE(GC_is_disabled()); - // pass control to main - writeString("foo", sink); - - ASSERT_TRUE(GC_is_disabled()); - - // pass control to main - writeString("bar", sink); - }); - - // pass control to coroutine - std::string foo = readString(*source); - ASSERT_EQ(foo, "foo"); - ASSERT_FALSE(GC_is_disabled()); - // pass control to coroutine - std::string bar = readString(*source); - ASSERT_EQ(bar, "bar"); - - ASSERT_FALSE(GC_is_disabled()); - } -#endif - - // This test tests that boehm handles coroutine stacks correctly - // This test tests that coroutine stacks are registered to the GC, - // even when the coroutine is not running. It also tests that - // the main stack is still registered to the GC when the coroutine is running. - TEST(CoroGC, CoroutineStackNotGCd) { - initGC(); - - volatile void* do_collect = GC_MALLOC_ATOMIC(128); - volatile void* dont_collect = GC_MALLOC_ATOMIC(128); - - bool* do_collect_witness = make_witness(do_collect); - bool* dont_collect_witness = make_witness(dont_collect); - - do_collect = nullptr; - - auto source = sinkToSource([&](Sink& sink) { - volatile void* dont_collect_inner = GC_MALLOC_ATOMIC(128); - volatile void* do_collect_inner = GC_MALLOC_ATOMIC(128); - - bool* do_collect_inner_witness = make_witness(do_collect_inner); - bool* dont_collect_inner_witness = make_witness(dont_collect_inner); - - do_collect_inner = nullptr; - - // pass control to main - writeString("foo", sink); - - ASSERT_FALSE(*dont_collect_inner_witness); - ASSERT_TRUE(*do_collect_inner_witness); - ASSERT_NE(nullptr, dont_collect_inner); - - // pass control to main - writeString("bar", sink); - }); - - // pass control to coroutine - std::string foo = readString(*source); - ASSERT_EQ(foo, "foo"); - - ASSERT_FALSE(GC_is_disabled()); - GC_gcollect(); - GC_invoke_finalizers(); - - // pass control to coroutine - std::string bar = readString(*source); - ASSERT_EQ(bar, "bar"); - - ASSERT_FALSE(*dont_collect_witness); - ASSERT_TRUE(*do_collect_witness); - ASSERT_NE(nullptr, dont_collect); - } -#endif -} diff --git a/src/libexpr/tests/local.mk b/src/libexpr/tests/local.mk index c36d21bfe..3e5504f71 100644 --- a/src/libexpr/tests/local.mk +++ b/src/libexpr/tests/local.mk @@ -16,4 +16,4 @@ libexpr-tests_CXXFLAGS += -I src/libexpr -I src/libutil -I src/libstore -I src/l libexpr-tests_LIBS = libstore-tests libutils-tests libexpr libutil libstore libfetchers -libexpr-tests_LDFLAGS := $(GTEST_LIBS) -lgmock -lboost_context +libexpr-tests_LDFLAGS := $(GTEST_LIBS) -lgmock diff --git a/src/libutil/serialise.cc b/src/libutil/serialise.cc index 6e53239f5..7476e6f6c 100644 --- a/src/libutil/serialise.cc +++ b/src/libutil/serialise.cc @@ -186,22 +186,6 @@ static DefaultStackAllocator defaultAllocatorSingleton; StackAllocator *StackAllocator::defaultAllocator = &defaultAllocatorSingleton; -std::shared_ptr (*create_coro_gc_hook)() = []() -> std::shared_ptr { - return {}; -}; - -/* This class is used for entry and exit hooks on coroutines */ -class CoroutineContext { - /* Disable GC when entering the coroutine without the boehm patch, - * since it doesn't find the main thread stack in this case. - * std::shared_ptr performs type-erasure, so it will call the right - * deleter. */ - const std::shared_ptr coro_gc_hook = create_coro_gc_hook(); -public: - CoroutineContext() {}; - ~CoroutineContext() {}; -}; - std::unique_ptr sourceToSink(std::function fun) { struct SourceToSink : FinishSink @@ -222,8 +206,7 @@ std::unique_ptr sourceToSink(std::function fun) if (in.empty()) return; cur = in; - if (!coro) { - CoroutineContext ctx; + if (!coro) coro = coro_t::push_type(VirtualStackAllocator{}, [&](coro_t::pull_type & yield) { LambdaSource source([&](char *out, size_t out_len) { if (cur.empty()) { @@ -240,24 +223,17 @@ std::unique_ptr sourceToSink(std::function fun) }); fun(source); }); - } if (!*coro) { abort(); } - if (!cur.empty()) { - CoroutineContext ctx; - (*coro)(false); - } + if (!cur.empty()) (*coro)(false); } void finish() override { if (!coro) return; if (!*coro) abort(); - { - CoroutineContext ctx; - (*coro)(true); - } + (*coro)(true); if (*coro) abort(); } }; @@ -288,23 +264,18 @@ std::unique_ptr sinkToSource( size_t read(char * data, size_t len) override { - if (!coro) { - CoroutineContext ctx; + if (!coro) coro = coro_t::pull_type(VirtualStackAllocator{}, [&](coro_t::push_type & yield) { LambdaSink sink([&](std::string_view data) { if (!data.empty()) yield(std::string(data)); }); fun(sink); }); - } if (!*coro) { eof(); abort(); } if (pos == cur.size()) { - if (!cur.empty()) { - CoroutineContext ctx; - (*coro)(); - } + if (!cur.empty()) (*coro)(); cur = coro->get(); pos = 0; } diff --git a/src/libutil/serialise.hh b/src/libutil/serialise.hh index e99c5fcc7..7da5b07fd 100644 --- a/src/libutil/serialise.hh +++ b/src/libutil/serialise.hh @@ -501,10 +501,4 @@ struct StackAllocator { static StackAllocator *defaultAllocator; }; -/* Disabling GC when entering a coroutine (without the boehm patch). - mutable to avoid boehm gc dependency in libutil. - */ -extern std::shared_ptr (*create_coro_gc_hook)(); - - } -- cgit v1.2.3 From 4bef2016a18e64e378982db914e73d1a8eaa8855 Mon Sep 17 00:00:00 2001 From: Alexander Bantyev Date: Thu, 9 Mar 2023 16:03:48 +0400 Subject: Display progress when running copyPaths (nix copy) `nix copy` operations did not show progress. This is quite confusing. Add a `progressSink` which displays the progress during `copyPaths`, pretty much copied from `copyStorePath`. Fixes https://github.com/NixOS/nix/issues/8000 --- src/libstore/store-api.cc | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc index f32c2d30c..9ef641a3d 100644 --- a/src/libstore/store-api.cc +++ b/src/libstore/store-api.cc @@ -1101,6 +1101,8 @@ std::map copyPaths( return storePathForDst; }; + uint64_t total = 0; + for (auto & missingPath : sortedMissing) { auto info = srcStore.queryPathInfo(missingPath); @@ -1121,7 +1123,13 @@ std::map copyPaths( {storePathS, srcUri, dstUri}); PushActivity pact(act.id); - srcStore.narFromPath(missingPath, sink); + LambdaSink progressSink([&](std::string_view data) { + total += data.size(); + act.progress(total, info->narSize); + }); + TeeSink tee { sink, progressSink }; + + srcStore.narFromPath(missingPath, tee); }); pathsToCopy.push_back(std::pair{infoForDst, std::move(source)}); } -- cgit v1.2.3 From 8f96720b9f7b23c3c34694be11d20a8ce3b619f5 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 9 Mar 2023 15:30:53 +0100 Subject: Fix another uninitialized variable https://hydra.nixos.org/build/211811494 --- src/nix-store/nix-store.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/nix-store/nix-store.cc b/src/nix-store/nix-store.cc index 2f754c961..54479489f 100644 --- a/src/nix-store/nix-store.cc +++ b/src/nix-store/nix-store.cc @@ -1023,7 +1023,7 @@ static int main_nix_store(int argc, char * * argv) { Strings opFlags, opArgs; Operation op = 0; - bool readFromStdIn; + bool readFromStdIn = false; parseCmdLine(argc, argv, [&](Strings::iterator & arg, const Strings::iterator & end) { Operation oldOp = op; -- cgit v1.2.3 From 4f3a4b732cdc8a0179b0da9cccafef0b215a4e86 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 9 Mar 2023 16:44:51 +0100 Subject: Make findRuntimeRoots() more resilient to disappearing processes I saw this random failure in https://hydra.nixos.org/build/211811692: error: opening /proc/15307/fd: No such process while running nix-collect-garbage in a readfile-context.sh. This is because we're not handling ESRCH errors reading /proc//fd. So just move the read inside the try/catch where we do handle it. --- src/libstore/gc.cc | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) (limited to 'src') diff --git a/src/libstore/gc.cc b/src/libstore/gc.cc index 0aecc2d3b..0038ec802 100644 --- a/src/libstore/gc.cc +++ b/src/libstore/gc.cc @@ -371,29 +371,29 @@ void LocalStore::findRuntimeRoots(Roots & roots, bool censor) while (errno = 0, ent = readdir(procDir.get())) { checkInterrupt(); if (std::regex_match(ent->d_name, digitsRegex)) { - readProcLink(fmt("/proc/%s/exe" ,ent->d_name), unchecked); - readProcLink(fmt("/proc/%s/cwd", ent->d_name), unchecked); - - auto fdStr = fmt("/proc/%s/fd", ent->d_name); - auto fdDir = AutoCloseDir(opendir(fdStr.c_str())); - if (!fdDir) { - if (errno == ENOENT || errno == EACCES) - continue; - throw SysError("opening %1%", fdStr); - } - struct dirent * fd_ent; - while (errno = 0, fd_ent = readdir(fdDir.get())) { - if (fd_ent->d_name[0] != '.') - readProcLink(fmt("%s/%s", fdStr, fd_ent->d_name), unchecked); - } - if (errno) { - if (errno == ESRCH) - continue; - throw SysError("iterating /proc/%1%/fd", ent->d_name); - } - fdDir.reset(); - try { + readProcLink(fmt("/proc/%s/exe" ,ent->d_name), unchecked); + readProcLink(fmt("/proc/%s/cwd", ent->d_name), unchecked); + + auto fdStr = fmt("/proc/%s/fd", ent->d_name); + auto fdDir = AutoCloseDir(opendir(fdStr.c_str())); + if (!fdDir) { + if (errno == ENOENT || errno == EACCES) + continue; + throw SysError("opening %1%", fdStr); + } + struct dirent * fd_ent; + while (errno = 0, fd_ent = readdir(fdDir.get())) { + if (fd_ent->d_name[0] != '.') + readProcLink(fmt("%s/%s", fdStr, fd_ent->d_name), unchecked); + } + if (errno) { + if (errno == ESRCH) + continue; + throw SysError("iterating /proc/%1%/fd", ent->d_name); + } + fdDir.reset(); + auto mapFile = fmt("/proc/%s/maps", ent->d_name); auto mapLines = tokenizeString>(readFile(mapFile), "\n"); for (const auto & line : mapLines) { -- cgit v1.2.3 From 526bdbda3cfa396808a12e6598df50dacb04e6dd Mon Sep 17 00:00:00 2001 From: lbodor Date: Sun, 12 Mar 2023 22:40:47 +1100 Subject: print-dev-env: stop inadvertently adding . to PATH --- src/nix/develop.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/nix/develop.cc b/src/nix/develop.cc index 9d07a7a85..0ee533e85 100644 --- a/src/nix/develop.cc +++ b/src/nix/develop.cc @@ -313,7 +313,7 @@ struct Common : InstallableCommand, MixProfile buildEnvironment.toBash(out, ignoreVars); for (auto & var : savedVars) - out << fmt("%s=\"$%s:$nix_saved_%s\"\n", var, var, var); + out << fmt("%s=\"$%s${nix_saved_%s:+:$nix_saved_%s}\"\n", var, var, var, var); out << "export NIX_BUILD_TOP=\"$(mktemp -d -t nix-shell.XXXXXX)\"\n"; for (auto & i : {"TMP", "TMPDIR", "TEMP", "TEMPDIR"}) -- cgit v1.2.3 From fcd0b0fbd5449c665d6ae80e4ae6393e80fd6d1a Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 13 Mar 2023 05:31:03 -0700 Subject: Fix macOS warning --- src/libutil/util.cc | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/libutil/util.cc b/src/libutil/util.cc index 17ccf3554..c1de4fb33 100644 --- a/src/libutil/util.cc +++ b/src/libutil/util.cc @@ -1070,12 +1070,14 @@ static pid_t doFork(bool allowVfork, std::function fun) } +#if __linux__ static int childEntry(void * arg) { auto main = (std::function *) arg; (*main)(); return 1; } +#endif pid_t startProcess(std::function fun, const ProcessOptions & options) -- cgit v1.2.3 From 19326ac2979f0d989835105a5d816a943a6bc7f2 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 14 Mar 2023 18:58:12 +0100 Subject: Simplify commonChildInit() --- src/libstore/build/hook-instance.cc | 2 +- src/libstore/build/local-derivation-goal.cc | 2 +- src/libutil/util.cc | 4 ++-- src/libutil/util.hh | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/libstore/build/hook-instance.cc b/src/libstore/build/hook-instance.cc index cb58a1f02..ea2ae210e 100644 --- a/src/libstore/build/hook-instance.cc +++ b/src/libstore/build/hook-instance.cc @@ -35,7 +35,7 @@ HookInstance::HookInstance() /* Fork the hook. */ pid = startProcess([&]() { - commonChildInit(fromHook); + commonChildInit(fromHook.writeSide.get()); if (chdir("/") == -1) throw SysError("changing into /"); diff --git a/src/libstore/build/local-derivation-goal.cc b/src/libstore/build/local-derivation-goal.cc index 6fb9b86e0..61c1cbccf 100644 --- a/src/libstore/build/local-derivation-goal.cc +++ b/src/libstore/build/local-derivation-goal.cc @@ -1649,7 +1649,7 @@ void LocalDerivationGoal::runChild() try { /* child */ - commonChildInit(builderOut); + commonChildInit(builderOut.writeSide.get()); try { setupSeccomp(); diff --git a/src/libutil/util.cc b/src/libutil/util.cc index c1de4fb33..c605a33e6 100644 --- a/src/libutil/util.cc +++ b/src/libutil/util.cc @@ -1968,7 +1968,7 @@ std::string showBytes(uint64_t bytes) // FIXME: move to libstore/build -void commonChildInit(Pipe & logPipe) +void commonChildInit(int stderrFd) { logger = makeSimpleLogger(); @@ -1983,7 +1983,7 @@ void commonChildInit(Pipe & logPipe) throw SysError("creating a new session"); /* Dup the write side of the logger pipe into stderr. */ - if (dup2(logPipe.writeSide.get(), STDERR_FILENO) == -1) + if (dup2(stderrFd, STDERR_FILENO) == -1) throw SysError("cannot pipe standard error into log file"); /* Dup stderr to stdout. */ diff --git a/src/libutil/util.hh b/src/libutil/util.hh index 326c6b143..52ca36fd1 100644 --- a/src/libutil/util.hh +++ b/src/libutil/util.hh @@ -704,7 +704,7 @@ typedef std::function PathFilter; extern PathFilter defaultPathFilter; /* Common initialisation performed in child processes. */ -void commonChildInit(Pipe & logPipe); +void commonChildInit(int stderrFd); /* Create a Unix domain socket. */ AutoCloseFD createUnixDomainSocket(); -- cgit v1.2.3 From c536e00c9deeac58bc4b3299dbc702604c32adbe Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 14 Mar 2023 19:10:59 +0100 Subject: Move pseudoterminal slave open to child Hopefully this fixes "unexpected EOF" failures on macOS (#3137, #3605, #7242, #7702). The problem appears to be that under some circumstances, macOS discards the output written to the slave side of the pseudoterminal. Hence the parent never sees the "sandbox initialized" message from the child, even though it succeeded. The conditions are: * The child finishes very quickly. That's why this bug is likely to trigger in nix-env tests, since that uses a builtin builder. Adding a short sleep before the child exits makes the problem go away. * The parent has closed its duplicate of the slave file descriptor. This shouldn't matter, since the child has a duplicate as well, but it does. E.g. moving the close to the bottom of startBuilder() makes the problem go away. However, that's not a solution because it would make Nix hang if the child dies before sending the "sandbox initialized" message. * The system is under high load. E.g. "make installcheck -j16" makes the issue pretty reproducible, while it's very rare under "make installcheck -j1". As a fix/workaround, we now open the pseudoterminal slave in the child, rather than the parent. This removes the second condition (i.e. the parent no longer needs to close the slave fd) and I haven't been able to reproduce the "unexpected EOF" with this. --- src/libstore/build/local-derivation-goal.cc | 58 ++++++++++++++--------------- src/libstore/build/local-derivation-goal.hh | 4 ++ 2 files changed, 31 insertions(+), 31 deletions(-) (limited to 'src') diff --git a/src/libstore/build/local-derivation-goal.cc b/src/libstore/build/local-derivation-goal.cc index 61c1cbccf..84a5a79b0 100644 --- a/src/libstore/build/local-derivation-goal.cc +++ b/src/libstore/build/local-derivation-goal.cc @@ -802,15 +802,13 @@ void LocalDerivationGoal::startBuilder() /* Create the log file. */ Path logFile = openLogFile(); - /* Create a pipe to get the output of the builder. */ - //builderOut.create(); - + /* Create a pseudoterminal to get the output of the builder. */ builderOut.readSide = posix_openpt(O_RDWR | O_NOCTTY); if (!builderOut.readSide) throw SysError("opening pseudoterminal master"); // FIXME: not thread-safe, use ptsname_r - std::string slaveName(ptsname(builderOut.readSide.get())); + slaveName = ptsname(builderOut.readSide.get()); if (buildUser) { if (chmod(slaveName.c_str(), 0600)) @@ -826,30 +824,9 @@ void LocalDerivationGoal::startBuilder() } #endif - #if 0 - // Mount the pt in the sandbox so that the "tty" command works. - // FIXME: this doesn't work with the new devpts in the sandbox. - if (useChroot) - dirsInChroot[slaveName] = {slaveName, false}; - #endif - if (unlockpt(builderOut.readSide.get())) throw SysError("unlocking pseudoterminal"); - builderOut.writeSide = open(slaveName.c_str(), O_RDWR | O_NOCTTY); - if (!builderOut.writeSide) - throw SysError("opening pseudoterminal slave"); - - // Put the pt into raw mode to prevent \n -> \r\n translation. - struct termios term; - if (tcgetattr(builderOut.writeSide.get(), &term)) - throw SysError("getting pseudoterminal attributes"); - - cfmakeraw(&term); - - if (tcsetattr(builderOut.writeSide.get(), TCSANOW, &term)) - throw SysError("putting pseudoterminal into raw mode"); - buildResult.startTime = time(0); /* Fork a child to build the package. */ @@ -897,7 +874,11 @@ void LocalDerivationGoal::startBuilder() usingUserNamespace = userNamespacesSupported(); + Pipe sendPid; + sendPid.create(); + Pid helper = startProcess([&]() { + sendPid.readSide.close(); /* Drop additional groups here because we can't do it after we've created the new user namespace. FIXME: @@ -919,11 +900,12 @@ void LocalDerivationGoal::startBuilder() pid_t child = startProcess([&]() { runChild(); }, options); - writeFull(builderOut.writeSide.get(), - fmt("%d %d\n", usingUserNamespace, child)); + writeFull(sendPid.writeSide.get(), fmt("%d\n", child)); _exit(0); }); + sendPid.writeSide.close(); + if (helper.wait() != 0) throw Error("unable to start build process"); @@ -935,10 +917,9 @@ void LocalDerivationGoal::startBuilder() userNamespaceSync.writeSide = -1; }); - auto ss = tokenizeString>(readLine(builderOut.readSide.get())); - assert(ss.size() == 2); - usingUserNamespace = ss[0] == "1"; - pid = string2Int(ss[1]).value(); + auto ss = tokenizeString>(readLine(sendPid.readSide.get())); + assert(ss.size() == 1); + pid = string2Int(ss[0]).value(); if (usingUserNamespace) { /* Set the UID/GID mapping of the builder's user namespace @@ -1649,6 +1630,21 @@ void LocalDerivationGoal::runChild() try { /* child */ + /* Open the slave side of the pseudoterminal. */ + builderOut.writeSide = open(slaveName.c_str(), O_RDWR | O_NOCTTY); + if (!builderOut.writeSide) + throw SysError("opening pseudoterminal slave"); + + // Put the pt into raw mode to prevent \n -> \r\n translation. + struct termios term; + if (tcgetattr(builderOut.writeSide.get(), &term)) + throw SysError("getting pseudoterminal attributes"); + + cfmakeraw(&term); + + if (tcsetattr(builderOut.writeSide.get(), TCSANOW, &term)) + throw SysError("putting pseudoterminal into raw mode"); + commonChildInit(builderOut.writeSide.get()); try { diff --git a/src/libstore/build/local-derivation-goal.hh b/src/libstore/build/local-derivation-goal.hh index 34c4e9187..c8537c7e5 100644 --- a/src/libstore/build/local-derivation-goal.hh +++ b/src/libstore/build/local-derivation-goal.hh @@ -27,6 +27,10 @@ struct LocalDerivationGoal : public DerivationGoal /* Pipe for the builder's standard output/error. */ Pipe builderOut; + /* Slave side of the pseudoterminal used for the builder's + standard output/error. */ + Path slaveName; + /* Pipe for synchronising updates to the builder namespaces. */ Pipe userNamespaceSync; -- cgit v1.2.3 From 6029c763c2c5998dc3265152425c8ff0ce01b1a0 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 15 Mar 2023 10:37:39 +0100 Subject: Change builderOut from Pipe to AutoCloseFD --- src/libstore/build/local-derivation-goal.cc | 31 ++++++++++++++--------------- src/libstore/build/local-derivation-goal.hh | 5 +++-- 2 files changed, 18 insertions(+), 18 deletions(-) (limited to 'src') diff --git a/src/libstore/build/local-derivation-goal.cc b/src/libstore/build/local-derivation-goal.cc index 84a5a79b0..f1ddf753f 100644 --- a/src/libstore/build/local-derivation-goal.cc +++ b/src/libstore/build/local-derivation-goal.cc @@ -292,7 +292,7 @@ void LocalDerivationGoal::closeReadPipes() if (hook) { DerivationGoal::closeReadPipes(); } else - builderOut.readSide = -1; + builderOut.close(); } @@ -803,12 +803,12 @@ void LocalDerivationGoal::startBuilder() Path logFile = openLogFile(); /* Create a pseudoterminal to get the output of the builder. */ - builderOut.readSide = posix_openpt(O_RDWR | O_NOCTTY); - if (!builderOut.readSide) + builderOut = posix_openpt(O_RDWR | O_NOCTTY); + if (!builderOut) throw SysError("opening pseudoterminal master"); // FIXME: not thread-safe, use ptsname_r - slaveName = ptsname(builderOut.readSide.get()); + slaveName = ptsname(builderOut.get()); if (buildUser) { if (chmod(slaveName.c_str(), 0600)) @@ -819,12 +819,12 @@ void LocalDerivationGoal::startBuilder() } #if __APPLE__ else { - if (grantpt(builderOut.readSide.get())) + if (grantpt(builderOut.get())) throw SysError("granting access to pseudoterminal slave"); } #endif - if (unlockpt(builderOut.readSide.get())) + if (unlockpt(builderOut.get())) throw SysError("unlocking pseudoterminal"); buildResult.startTime = time(0); @@ -980,15 +980,14 @@ void LocalDerivationGoal::startBuilder() /* parent */ pid.setSeparatePG(true); - builderOut.writeSide = -1; - worker.childStarted(shared_from_this(), {builderOut.readSide.get()}, true, true); + worker.childStarted(shared_from_this(), {builderOut.get()}, true, true); /* Check if setting up the build environment failed. */ std::vector msgs; while (true) { std::string msg = [&]() { try { - return readLine(builderOut.readSide.get()); + return readLine(builderOut.get()); } catch (Error & e) { auto status = pid.wait(); e.addTrace({}, "while waiting for the build environment for '%s' to initialize (%s, previous messages: %s)", @@ -1000,7 +999,7 @@ void LocalDerivationGoal::startBuilder() }(); if (msg.substr(0, 1) == "\2") break; if (msg.substr(0, 1) == "\1") { - FdSource source(builderOut.readSide.get()); + FdSource source(builderOut.get()); auto ex = readError(source); ex.addTrace({}, "while setting up the build environment"); throw ex; @@ -1631,21 +1630,21 @@ void LocalDerivationGoal::runChild() try { /* child */ /* Open the slave side of the pseudoterminal. */ - builderOut.writeSide = open(slaveName.c_str(), O_RDWR | O_NOCTTY); - if (!builderOut.writeSide) + AutoCloseFD builderOut = open(slaveName.c_str(), O_RDWR | O_NOCTTY); + if (!builderOut) throw SysError("opening pseudoterminal slave"); // Put the pt into raw mode to prevent \n -> \r\n translation. struct termios term; - if (tcgetattr(builderOut.writeSide.get(), &term)) + if (tcgetattr(builderOut.get(), &term)) throw SysError("getting pseudoterminal attributes"); cfmakeraw(&term); - if (tcsetattr(builderOut.writeSide.get(), TCSANOW, &term)) + if (tcsetattr(builderOut.get(), TCSANOW, &term)) throw SysError("putting pseudoterminal into raw mode"); - commonChildInit(builderOut.writeSide.get()); + commonChildInit(builderOut.get()); try { setupSeccomp(); @@ -2887,7 +2886,7 @@ void LocalDerivationGoal::deleteTmpDir(bool force) bool LocalDerivationGoal::isReadDesc(int fd) { return (hook && DerivationGoal::isReadDesc(fd)) || - (!hook && fd == builderOut.readSide.get()); + (!hook && fd == builderOut.get()); } diff --git a/src/libstore/build/local-derivation-goal.hh b/src/libstore/build/local-derivation-goal.hh index c8537c7e5..c3e8562ae 100644 --- a/src/libstore/build/local-derivation-goal.hh +++ b/src/libstore/build/local-derivation-goal.hh @@ -24,8 +24,9 @@ struct LocalDerivationGoal : public DerivationGoal /* The path of the temporary directory in the sandbox. */ Path tmpDirInSandbox; - /* Pipe for the builder's standard output/error. */ - Pipe builderOut; + /* Master side of the pseudoterminal used for the builder's + standard output/error. */ + AutoCloseFD builderOut; /* Slave side of the pseudoterminal used for the builder's standard output/error. */ -- cgit v1.2.3 From 5eb8bfd0f17f950ec181d59fb9fbb2330f778935 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 15 Mar 2023 10:41:38 +0100 Subject: Remove a variable in LocalDerivationGoal --- src/libstore/build/local-derivation-goal.cc | 8 ++++---- src/libstore/build/local-derivation-goal.hh | 6 +----- 2 files changed, 5 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/libstore/build/local-derivation-goal.cc b/src/libstore/build/local-derivation-goal.cc index f1ddf753f..538edef74 100644 --- a/src/libstore/build/local-derivation-goal.cc +++ b/src/libstore/build/local-derivation-goal.cc @@ -808,7 +808,7 @@ void LocalDerivationGoal::startBuilder() throw SysError("opening pseudoterminal master"); // FIXME: not thread-safe, use ptsname_r - slaveName = ptsname(builderOut.get()); + std::string slaveName = ptsname(builderOut.get()); if (buildUser) { if (chmod(slaveName.c_str(), 0600)) @@ -898,7 +898,7 @@ void LocalDerivationGoal::startBuilder() if (usingUserNamespace) options.cloneFlags |= CLONE_NEWUSER; - pid_t child = startProcess([&]() { runChild(); }, options); + pid_t child = startProcess([&]() { runChild(slaveName); }, options); writeFull(sendPid.writeSide.get(), fmt("%d\n", child)); _exit(0); @@ -974,7 +974,7 @@ void LocalDerivationGoal::startBuilder() #endif { pid = startProcess([&]() { - runChild(); + runChild(slaveName); }); } @@ -1620,7 +1620,7 @@ void setupSeccomp() } -void LocalDerivationGoal::runChild() +void LocalDerivationGoal::runChild(const Path & slaveName) { /* Warning: in the child we should absolutely not make any SQLite calls! */ diff --git a/src/libstore/build/local-derivation-goal.hh b/src/libstore/build/local-derivation-goal.hh index c3e8562ae..4d2f1ac28 100644 --- a/src/libstore/build/local-derivation-goal.hh +++ b/src/libstore/build/local-derivation-goal.hh @@ -28,10 +28,6 @@ struct LocalDerivationGoal : public DerivationGoal standard output/error. */ AutoCloseFD builderOut; - /* Slave side of the pseudoterminal used for the builder's - standard output/error. */ - Path slaveName; - /* Pipe for synchronising updates to the builder namespaces. */ Pipe userNamespaceSync; @@ -173,7 +169,7 @@ struct LocalDerivationGoal : public DerivationGoal int getChildStatus() override; /* Run the builder's process. */ - void runChild(); + void runChild(const std::string & slaveName); /* Check that the derivation outputs all exist and register them as valid. */ -- cgit v1.2.3 From 31d588db8b92b69363525a577f5cb6413c8eae15 Mon Sep 17 00:00:00 2001 From: figsoda Date: Wed, 15 Mar 2023 11:29:01 -0400 Subject: respect allRefs=1 when using `nix flake prefetch` --- src/libfetchers/git.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/libfetchers/git.cc b/src/libfetchers/git.cc index 309a143f5..1da8c9609 100644 --- a/src/libfetchers/git.cc +++ b/src/libfetchers/git.cc @@ -266,7 +266,7 @@ struct GitInputScheme : InputScheme for (auto & [name, value] : url.query) { if (name == "rev" || name == "ref") attrs.emplace(name, value); - else if (name == "shallow" || name == "submodules") + else if (name == "shallow" || name == "submodules" || name == "allRefs") attrs.emplace(name, Explicit { value == "1" }); else url2.query.emplace(name, value); -- cgit v1.2.3 From c27d358abba7154f16795d48d2a44fa856ab6b1e Mon Sep 17 00:00:00 2001 From: Yueh-Shun Li <44064051+ShamrockLee@users.noreply.github.com> Date: Thu, 16 Mar 2023 00:33:58 +0800 Subject: nix-hash: support base-64 and SRI format Add the --base64 and --sri flags for the Base64 and SRI format output. Add the --base16 flag to explicitly specify the hexadecimal format. Add the --to-base64 and --to-sri flag to convert a hash to the above mentioned format. --- src/nix/hash.cc | 38 ++++++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/nix/hash.cc b/src/nix/hash.cc index 60d9593a7..e2630505b 100644 --- a/src/nix/hash.cc +++ b/src/nix/hash.cc @@ -161,11 +161,11 @@ static auto rCmdHash = registerCommand("hash"); /* Legacy nix-hash command. */ static int compatNixHash(int argc, char * * argv) { - HashType ht = htMD5; + std::optional ht; bool flat = false; - bool base32 = false; + Base base = Base16; bool truncate = false; - enum { opHash, opTo32, opTo16 } op = opHash; + enum { opHash, opTo } op = opHash; std::vector ss; parseCmdLine(argc, argv, [&](Strings::iterator & arg, const Strings::iterator & end) { @@ -174,14 +174,31 @@ static int compatNixHash(int argc, char * * argv) else if (*arg == "--version") printVersion("nix-hash"); else if (*arg == "--flat") flat = true; - else if (*arg == "--base32") base32 = true; + else if (*arg == "--base16") base = Base16; + else if (*arg == "--base32") base = Base32; + else if (*arg == "--base64") base = Base64; + else if (*arg == "--sri") base = SRI; else if (*arg == "--truncate") truncate = true; else if (*arg == "--type") { std::string s = getArg(*arg, arg, end); ht = parseHashType(s); } - else if (*arg == "--to-base16") op = opTo16; - else if (*arg == "--to-base32") op = opTo32; + else if (*arg == "--to-base16") { + op = opTo; + base = Base16; + } + else if (*arg == "--to-base32") { + op = opTo; + base = Base32; + } + else if (*arg == "--to-base64") { + op = opTo; + base = Base64; + } + else if (*arg == "--to-sri") { + op = opTo; + base = SRI; + } else if (*arg != "" && arg->at(0) == '-') return false; else @@ -191,17 +208,18 @@ static int compatNixHash(int argc, char * * argv) if (op == opHash) { CmdHashBase cmd(flat ? FileIngestionMethod::Flat : FileIngestionMethod::Recursive); - cmd.ht = ht; - cmd.base = base32 ? Base32 : Base16; + if (!ht.has_value()) ht = htMD5; + cmd.ht = ht.value(); + cmd.base = base; cmd.truncate = truncate; cmd.paths = ss; cmd.run(); } else { - CmdToBase cmd(op == opTo32 ? Base32 : Base16); + CmdToBase cmd(base); cmd.args = ss; - cmd.ht = ht; + if (ht.has_value()) cmd.ht = ht; cmd.run(); } -- cgit v1.2.3 From bc23a44c54510c23dcbba030c39e8a1f3169c869 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Sat, 4 Feb 2023 12:03:47 -0500 Subject: Make command infra less stateful and more regular Already, we had classes like `BuiltPathsCommand` and `StorePathsCommand` which provided alternative `run` virtual functions providing the implementation with more arguments. This was a very nice and easy way to make writing command; just fill in the virtual functions and it is fairly clear what to do. However, exception to this pattern were `Installable{,s}Command`. These two classes instead just had a field where the installables would be stored, and various side-effecting `prepare` and `load` machinery too fill them in. Command would wish out those fields. This isn't so clear to use. What this commit does is make those command classes like the others, with richer `run` functions. Not only does this restore the pattern making commands easier to write, it has a number of other benefits: - `prepare` and `load` are gone entirely! One command just hands just hands off to the next. - `useDefaultInstallables` because `defaultInstallables`. This takes over `prepare` for the one case that needs it, and provides enough flexiblity to handle `nix repl`'s idiosyncratic migration. - We can use `ref` instead of `std::shared_ptr`. The former must be initialized (so it is like Rust's `Box` rather than `Option`, This expresses the invariant that the installable are in fact initialized much better. This is possible because since we just have local variables not fields, we can stop worrying about the not-yet-initialized case. - Fewer lines of code! (Finally I have a large refactor that makes the number go down not up...) - `nix repl` is now implemented in a clearer way. The last item deserves further mention. `nix repl` is not like the other installable commands because instead working from once-loaded installables, it needs to be able to load them again and again. To properly support this, we make a new superclass `RawInstallablesCommand`. This class has the argument parsing and completion logic, but does *not* hand off parsed installables but instead just the raw string arguments. This is exactly what `nix repl` needs, and allows us to instead of having the logic awkwardly split between `prepare`, `useDefaultInstallables,` and `load`, have everything right next to each other. I think this will enable future simplifications of that argument defaulting logic, but I am saving those for a future PR --- best to keep code motion and more complicated boolean expression rewriting separate steps. The "diagnostic ignored `-Woverloaded-virtual`" pragma helps because C++ doesn't like our many `run` methods. In our case, we don't mind the shadowing it all --- it is *intentional* that the derived class only provides a `run` method, and doesn't call any of the overridden `run` methods. Helps with https://github.com/NixOS/rfcs/pull/134 --- src/libcmd/command.cc | 6 +-- src/libcmd/command.hh | 54 +++++++++++++------------- src/libcmd/installables.cc | 79 ++++++++++++++++++++++----------------- src/libcmd/installables.hh | 19 +++++----- src/libutil/args.hh | 1 - src/nix/app.cc | 4 +- src/nix/build.cc | 2 +- src/nix/bundle.cc | 2 +- src/nix/copy.cc | 2 - src/nix/develop.cc | 19 +++++----- src/nix/edit.cc | 2 +- src/nix/eval.cc | 2 +- src/nix/flake.cc | 1 - src/nix/hash.cc | 1 - src/nix/log.cc | 2 +- src/nix/main.cc | 1 - src/nix/make-content-addressed.cc | 1 - src/nix/nar.cc | 1 - src/nix/profile.cc | 15 ++++---- src/nix/realisation.cc | 1 - src/nix/registry.cc | 1 - src/nix/repl.cc | 41 ++++++++++---------- src/nix/run.cc | 4 +- src/nix/search.cc | 2 +- src/nix/show-derivation.cc | 2 +- src/nix/sigs.cc | 1 - src/nix/store-copy-log.cc | 2 +- src/nix/store-delete.cc | 2 +- src/nix/store-repair.cc | 2 +- src/nix/store.cc | 1 - 30 files changed, 137 insertions(+), 136 deletions(-) (limited to 'src') diff --git a/src/libcmd/command.cc b/src/libcmd/command.cc index ab51c229d..bedf11e2c 100644 --- a/src/libcmd/command.cc +++ b/src/libcmd/command.cc @@ -165,7 +165,7 @@ BuiltPathsCommand::BuiltPathsCommand(bool recursive) }); } -void BuiltPathsCommand::run(ref store) +void BuiltPathsCommand::run(ref store, Installables && installables) { BuiltPaths paths; if (all) { @@ -211,7 +211,7 @@ void StorePathsCommand::run(ref store, BuiltPaths && paths) run(store, std::move(sorted)); } -void StorePathCommand::run(ref store, std::vector && storePaths) +void StorePathCommand::run(ref store, StorePaths && storePaths) { if (storePaths.size() != 1) throw UsageError("this command requires exactly one store path"); @@ -246,7 +246,7 @@ void MixProfile::updateProfile(const BuiltPaths & buildables) { if (!profile) return; - std::vector result; + StorePaths result; for (auto & buildable : buildables) { std::visit(overloaded { diff --git a/src/libcmd/command.hh b/src/libcmd/command.hh index 0d84c8395..874ca3249 100644 --- a/src/libcmd/command.hh +++ b/src/libcmd/command.hh @@ -29,6 +29,9 @@ struct NixMultiCommand : virtual MultiCommand, virtual Command nlohmann::json toJSON() override; }; +// For the overloaded run methods +#pragma GCC diagnostic ignored "-Woverloaded-virtual" + /* A command that requires a Nix store. */ struct StoreCommand : virtual Command { @@ -97,10 +100,10 @@ struct SourceExprCommand : virtual Args, MixFlakeOptions SourceExprCommand(); - std::vector> parseInstallables( + Installables parseInstallables( ref store, std::vector ss); - std::shared_ptr parseInstallable( + ref parseInstallable( ref store, const std::string & installable); virtual Strings getDefaultFlakeAttrPaths(); @@ -115,36 +118,43 @@ struct MixReadOnlyOption : virtual Args MixReadOnlyOption(); }; -/* A command that operates on a list of "installables", which can be - store paths, attribute paths, Nix expressions, etc. */ -struct InstallablesCommand : virtual Args, SourceExprCommand +/* Like InstallablesCommand but the installables are not loaded */ +struct RawInstallablesCommand : virtual Args, SourceExprCommand { - std::vector> installables; + RawInstallablesCommand(); - InstallablesCommand(); + virtual void run(ref store, std::vector && rawInstallables) = 0; - void prepare() override; - Installables load(); + void run(ref store) override; - virtual bool useDefaultInstallables() { return true; } + // FIXME make const after CmdRepl's override is fixed up + virtual void applyDefaultInstallables(std::vector & rawInstallables); bool readFromStdIn = false; std::vector getFlakesForCompletion() override; -protected: +private: + + std::vector rawInstallables; +}; +/* A command that operates on a list of "installables", which can be + store paths, attribute paths, Nix expressions, etc. */ +struct InstallablesCommand : RawInstallablesCommand +{ + virtual void run(ref store, Installables && installables) = 0; - std::vector _installables; + void run(ref store, std::vector && rawInstallables) override; }; /* A command that operates on exactly one "installable" */ struct InstallableCommand : virtual Args, SourceExprCommand { - std::shared_ptr installable; - InstallableCommand(); - void prepare() override; + virtual void run(ref store, ref installable) = 0; + + void run(ref store) override; std::vector getFlakesForCompletion() override { @@ -179,22 +189,18 @@ public: BuiltPathsCommand(bool recursive = false); - using StoreCommand::run; - virtual void run(ref store, BuiltPaths && paths) = 0; - void run(ref store) override; + void run(ref store, Installables && installables) override; - bool useDefaultInstallables() override { return !all; } + void applyDefaultInstallables(std::vector & rawInstallables) override; }; struct StorePathsCommand : public BuiltPathsCommand { StorePathsCommand(bool recursive = false); - using BuiltPathsCommand::run; - - virtual void run(ref store, std::vector && storePaths) = 0; + virtual void run(ref store, StorePaths && storePaths) = 0; void run(ref store, BuiltPaths && paths) override; }; @@ -202,11 +208,9 @@ struct StorePathsCommand : public BuiltPathsCommand /* A command that operates on exactly one store path. */ struct StorePathCommand : public StorePathsCommand { - using StorePathsCommand::run; - virtual void run(ref store, const StorePath & storePath) = 0; - void run(ref store, std::vector && storePaths) override; + void run(ref store, StorePaths && storePaths) override; }; /* A helper class for registering commands globally. */ diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc index 5ecf6293f..5cbf26b88 100644 --- a/src/libcmd/installables.cc +++ b/src/libcmd/installables.cc @@ -422,10 +422,10 @@ ref openEvalCache( }); } -std::vector> SourceExprCommand::parseInstallables( +Installables SourceExprCommand::parseInstallables( ref store, std::vector ss) { - std::vector> result; + Installables result; if (file || expr) { if (file && expr) @@ -451,7 +451,7 @@ std::vector> SourceExprCommand::parseInstallables( for (auto & s : ss) { auto [prefix, extendedOutputsSpec] = ExtendedOutputsSpec::parse(s); result.push_back( - std::make_shared( + make_ref( InstallableAttrPath::parse( state, *this, vFile, prefix, extendedOutputsSpec))); } @@ -468,7 +468,7 @@ std::vector> SourceExprCommand::parseInstallables( if (prefix.find('/') != std::string::npos) { try { - result.push_back(std::make_shared( + result.push_back(make_ref( InstallableDerivedPath::parse(store, prefix, extendedOutputsSpec))); continue; } catch (BadStorePath &) { @@ -480,7 +480,7 @@ std::vector> SourceExprCommand::parseInstallables( try { auto [flakeRef, fragment] = parseFlakeRefWithFragment(std::string { prefix }, absPath(".")); - result.push_back(std::make_shared( + result.push_back(make_ref( this, getEvalState(), std::move(flakeRef), @@ -501,7 +501,7 @@ std::vector> SourceExprCommand::parseInstallables( return result; } -std::shared_ptr SourceExprCommand::parseInstallable( +ref SourceExprCommand::parseInstallable( ref store, const std::string & installable) { auto installables = parseInstallables(store, {installable}); @@ -513,7 +513,7 @@ std::vector Installable::build( ref evalStore, ref store, Realise mode, - const std::vector> & installables, + const Installables & installables, BuildMode bMode) { std::vector res; @@ -522,11 +522,11 @@ std::vector Installable::build( return res; } -std::vector, BuiltPathWithResult>> Installable::build2( +std::vector, BuiltPathWithResult>> Installable::build2( ref evalStore, ref store, Realise mode, - const std::vector> & installables, + const Installables & installables, BuildMode bMode) { if (mode == Realise::Nothing) @@ -535,7 +535,7 @@ std::vector, BuiltPathWithResult>> Instal struct Aux { ExtraPathInfo info; - std::shared_ptr installable; + ref installable; }; std::vector pathsToBuild; @@ -548,7 +548,7 @@ std::vector, BuiltPathWithResult>> Instal } } - std::vector, BuiltPathWithResult>> res; + std::vector, BuiltPathWithResult>> res; switch (mode) { @@ -620,7 +620,7 @@ BuiltPaths Installable::toBuiltPaths( ref store, Realise mode, OperateOn operateOn, - const std::vector> & installables) + const Installables & installables) { if (operateOn == OperateOn::Output) { BuiltPaths res; @@ -642,7 +642,7 @@ StorePathSet Installable::toStorePaths( ref evalStore, ref store, Realise mode, OperateOn operateOn, - const std::vector> & installables) + const Installables & installables) { StorePathSet outPaths; for (auto & path : toBuiltPaths(evalStore, store, mode, operateOn, installables)) { @@ -656,7 +656,7 @@ StorePath Installable::toStorePath( ref evalStore, ref store, Realise mode, OperateOn operateOn, - std::shared_ptr installable) + ref installable) { auto paths = toStorePaths(evalStore, store, mode, operateOn, {installable}); @@ -668,7 +668,7 @@ StorePath Installable::toStorePath( StorePathSet Installable::toDerivations( ref store, - const std::vector> & installables, + const Installables & installables, bool useDeriver) { StorePathSet drvPaths; @@ -692,9 +692,8 @@ StorePathSet Installable::toDerivations( return drvPaths; } -InstallablesCommand::InstallablesCommand() +RawInstallablesCommand::RawInstallablesCommand() { - addFlag({ .longName = "stdin", .description = "Read installables from the standard input.", @@ -703,40 +702,45 @@ InstallablesCommand::InstallablesCommand() expectArgs({ .label = "installables", - .handler = {&_installables}, + .handler = {&rawInstallables}, .completer = {[&](size_t, std::string_view prefix) { completeInstallable(prefix); }} }); } -void InstallablesCommand::prepare() -{ - installables = load(); -} - -Installables InstallablesCommand::load() +void RawInstallablesCommand::applyDefaultInstallables(std::vector & rawInstallables) { - if (_installables.empty() && useDefaultInstallables() && !readFromStdIn) + if (rawInstallables.empty()) { // FIXME: commands like "nix profile install" should not have a // default, probably. - _installables.push_back("."); + rawInstallables.push_back("."); + } +} +void RawInstallablesCommand::run(ref store) +{ if (readFromStdIn && !isatty(STDIN_FILENO)) { std::string word; while (std::cin >> word) { - _installables.emplace_back(std::move(word)); + rawInstallables.emplace_back(std::move(word)); } } - return parseInstallables(getStore(), _installables); + applyDefaultInstallables(rawInstallables); + run(store, std::move(rawInstallables)); } -std::vector InstallablesCommand::getFlakesForCompletion() +std::vector RawInstallablesCommand::getFlakesForCompletion() { - if (_installables.empty() && useDefaultInstallables()) - return {"."}; - return _installables; + applyDefaultInstallables(rawInstallables); + return rawInstallables; +} + +void InstallablesCommand::run(ref store, std::vector && rawInstallables) +{ + auto installables = parseInstallables(store, rawInstallables); + run(store, std::move(installables)); } InstallableCommand::InstallableCommand() @@ -752,9 +756,16 @@ InstallableCommand::InstallableCommand() }); } -void InstallableCommand::prepare() +void InstallableCommand::run(ref store) +{ + auto installable = parseInstallable(store, _installable); + run(store, std::move(installable)); +} + +void BuiltPathsCommand::applyDefaultInstallables(std::vector & rawInstallables) { - installable = parseInstallable(getStore(), _installable); + if (rawInstallables.empty() && !all) + rawInstallables.push_back("."); } } diff --git a/src/libcmd/installables.hh b/src/libcmd/installables.hh index be77fdc81..6c2922d89 100644 --- a/src/libcmd/installables.hh +++ b/src/libcmd/installables.hh @@ -79,6 +79,9 @@ struct BuiltPathWithResult typedef std::vector DerivedPathsWithInfo; +struct Installable; +typedef std::vector> Installables; + struct Installable { virtual ~Installable() { } @@ -122,14 +125,14 @@ struct Installable ref evalStore, ref store, Realise mode, - const std::vector> & installables, + const Installables & installables, BuildMode bMode = bmNormal); - static std::vector, BuiltPathWithResult>> build2( + static std::vector, BuiltPathWithResult>> build2( ref evalStore, ref store, Realise mode, - const std::vector> & installables, + const Installables & installables, BuildMode bMode = bmNormal); static std::set toStorePaths( @@ -137,18 +140,18 @@ struct Installable ref store, Realise mode, OperateOn operateOn, - const std::vector> & installables); + const Installables & installables); static StorePath toStorePath( ref evalStore, ref store, Realise mode, OperateOn operateOn, - std::shared_ptr installable); + ref installable); static std::set toDerivations( ref store, - const std::vector> & installables, + const Installables & installables, bool useDeriver = false); static BuiltPaths toBuiltPaths( @@ -156,9 +159,7 @@ struct Installable ref store, Realise mode, OperateOn operateOn, - const std::vector> & installables); + const Installables & installables); }; -typedef std::vector> Installables; - } diff --git a/src/libutil/args.hh b/src/libutil/args.hh index 84866f12b..7211ee307 100644 --- a/src/libutil/args.hh +++ b/src/libutil/args.hh @@ -198,7 +198,6 @@ struct Command : virtual public Args virtual ~Command() { } - virtual void prepare() { }; virtual void run() = 0; typedef int Category; diff --git a/src/nix/app.cc b/src/nix/app.cc index 5cd65136f..bfd75e278 100644 --- a/src/nix/app.cc +++ b/src/nix/app.cc @@ -119,11 +119,11 @@ App UnresolvedApp::resolve(ref evalStore, ref store) { auto res = unresolved; - std::vector> installableContext; + Installables installableContext; for (auto & ctxElt : unresolved.context) installableContext.push_back( - std::make_shared(store, DerivedPath { ctxElt })); + make_ref(store, DerivedPath { ctxElt })); auto builtContext = Installable::build(evalStore, store, Realise::Outputs, installableContext); res.program = resolveString(*store, unresolved.program, builtContext); diff --git a/src/nix/build.cc b/src/nix/build.cc index f4f2ec81d..f8593135e 100644 --- a/src/nix/build.cc +++ b/src/nix/build.cc @@ -89,7 +89,7 @@ struct CmdBuild : InstallablesCommand, MixDryRun, MixJSON, MixProfile ; } - void run(ref store) override + void run(ref store, Installables && installables) override { if (dryRun) { std::vector pathsToBuild; diff --git a/src/nix/bundle.cc b/src/nix/bundle.cc index dcf9a6f2d..973bbd423 100644 --- a/src/nix/bundle.cc +++ b/src/nix/bundle.cc @@ -70,7 +70,7 @@ struct CmdBundle : InstallableCommand return res; } - void run(ref store) override + void run(ref store, ref installable) override { auto evalState = getEvalState(); diff --git a/src/nix/copy.cc b/src/nix/copy.cc index 8730a9a5c..151d28277 100644 --- a/src/nix/copy.cc +++ b/src/nix/copy.cc @@ -10,8 +10,6 @@ struct CmdCopy : virtual CopyCommand, virtual BuiltPathsCommand SubstituteFlag substitute = NoSubstitute; - using BuiltPathsCommand::run; - CmdCopy() : BuiltPathsCommand(true) { diff --git a/src/nix/develop.cc b/src/nix/develop.cc index 0ee533e85..f06ade008 100644 --- a/src/nix/develop.cc +++ b/src/nix/develop.cc @@ -374,7 +374,7 @@ struct Common : InstallableCommand, MixProfile return res; } - StorePath getShellOutPath(ref store) + StorePath getShellOutPath(ref store, ref installable) { auto path = installable->getStorePath(); if (path && hasSuffix(path->to_string(), "-env")) @@ -392,9 +392,10 @@ struct Common : InstallableCommand, MixProfile } } - std::pair getBuildEnvironment(ref store) + std::pair + getBuildEnvironment(ref store, ref installable) { - auto shellOutPath = getShellOutPath(store); + auto shellOutPath = getShellOutPath(store, installable); auto strPath = store->printStorePath(shellOutPath); @@ -480,9 +481,9 @@ struct CmdDevelop : Common, MixEnvironment ; } - void run(ref store) override + void run(ref store, ref installable) override { - auto [buildEnvironment, gcroot] = getBuildEnvironment(store); + auto [buildEnvironment, gcroot] = getBuildEnvironment(store, installable); auto [rcFileFd, rcFilePath] = createTempFile("nix-shell"); @@ -537,7 +538,7 @@ struct CmdDevelop : Common, MixEnvironment nixpkgsLockFlags.inputOverrides = {}; nixpkgsLockFlags.inputUpdates = {}; - auto bashInstallable = std::make_shared( + auto bashInstallable = make_ref( this, state, installable->nixpkgsFlakeRef(), @@ -573,7 +574,7 @@ struct CmdDevelop : Common, MixEnvironment // Need to chdir since phases assume in flake directory if (phase) { // chdir if installable is a flake of type git+file or path - auto installableFlake = std::dynamic_pointer_cast(installable); + auto installableFlake = installable.dynamic_pointer_cast(); if (installableFlake) { auto sourcePath = installableFlake->getLockedFlake()->flake.resolvedRef.input.getSourcePath(); if (sourcePath) { @@ -604,9 +605,9 @@ struct CmdPrintDevEnv : Common, MixJSON Category category() override { return catUtility; } - void run(ref store) override + void run(ref store, ref installable) override { - auto buildEnvironment = getBuildEnvironment(store).first; + auto buildEnvironment = getBuildEnvironment(store, installable).first; stopProgressBar(); diff --git a/src/nix/edit.cc b/src/nix/edit.cc index dfe75fbdf..c46c1c23c 100644 --- a/src/nix/edit.cc +++ b/src/nix/edit.cc @@ -25,7 +25,7 @@ struct CmdEdit : InstallableCommand Category category() override { return catSecondary; } - void run(ref store) override + void run(ref store, ref installable) override { auto state = getEvalState(); diff --git a/src/nix/eval.cc b/src/nix/eval.cc index 209fd3ed2..6c2b60427 100644 --- a/src/nix/eval.cc +++ b/src/nix/eval.cc @@ -54,7 +54,7 @@ struct CmdEval : MixJSON, InstallableCommand, MixReadOnlyOption Category category() override { return catSecondary; } - void run(ref store) override + void run(ref store, ref installable) override { if (raw && json) throw UsageError("--raw and --json are mutually exclusive"); diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 3fe093fc7..0a6616396 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -1329,7 +1329,6 @@ struct CmdFlake : NixMultiCommand if (!command) throw UsageError("'nix flake' requires a sub-command."); settings.requireExperimentalFeature(Xp::Flakes); - command->second->prepare(); command->second->run(); } }; diff --git a/src/nix/hash.cc b/src/nix/hash.cc index 60d9593a7..6d95c0455 100644 --- a/src/nix/hash.cc +++ b/src/nix/hash.cc @@ -151,7 +151,6 @@ struct CmdHash : NixMultiCommand { if (!command) throw UsageError("'nix hash' requires a sub-command."); - command->second->prepare(); command->second->run(); } }; diff --git a/src/nix/log.cc b/src/nix/log.cc index 0c9f778f0..aaf829764 100644 --- a/src/nix/log.cc +++ b/src/nix/log.cc @@ -23,7 +23,7 @@ struct CmdLog : InstallableCommand Category category() override { return catSecondary; } - void run(ref store) override + void run(ref store, ref installable) override { settings.readOnlyMode = true; diff --git a/src/nix/main.cc b/src/nix/main.cc index 53bf649d4..7b715f281 100644 --- a/src/nix/main.cc +++ b/src/nix/main.cc @@ -394,7 +394,6 @@ void mainWrapped(int argc, char * * argv) if (args.command->second->forceImpureByDefault() && !evalSettings.pureEval.overridden) { evalSettings.pureEval = false; } - args.command->second->prepare(); args.command->second->run(); } diff --git a/src/nix/make-content-addressed.cc b/src/nix/make-content-addressed.cc index 6693c55ac..d9c988a9f 100644 --- a/src/nix/make-content-addressed.cc +++ b/src/nix/make-content-addressed.cc @@ -28,7 +28,6 @@ struct CmdMakeContentAddressed : virtual CopyCommand, virtual StorePathsCommand, ; } - using StorePathsCommand::run; void run(ref srcStore, StorePaths && storePaths) override { auto dstStore = dstUri.empty() ? openStore() : openStore(dstUri); diff --git a/src/nix/nar.cc b/src/nix/nar.cc index dbb043d9b..9815410cf 100644 --- a/src/nix/nar.cc +++ b/src/nix/nar.cc @@ -25,7 +25,6 @@ struct CmdNar : NixMultiCommand { if (!command) throw UsageError("'nix nar' requires a sub-command."); - command->second->prepare(); command->second->run(); } }; diff --git a/src/nix/profile.cc b/src/nix/profile.cc index eef33b3d9..d72dd1a13 100644 --- a/src/nix/profile.cc +++ b/src/nix/profile.cc @@ -256,11 +256,11 @@ struct ProfileManifest static std::map> builtPathsPerInstallable( - const std::vector, BuiltPathWithResult>> & builtPaths) + const std::vector, BuiltPathWithResult>> & builtPaths) { std::map> res; for (auto & [installable, builtPath] : builtPaths) { - auto & r = res[installable.get()]; + auto & r = res[&*installable]; /* Note that there could be conflicting info (e.g. meta.priority fields) if the installable returned multiple derivations. So pick one arbitrarily. FIXME: @@ -296,7 +296,7 @@ struct CmdProfileInstall : InstallablesCommand, MixDefaultProfile ; } - void run(ref store) override + void run(ref store, Installables && installables) override { ProfileManifest manifest(*getEvalState(), *profile); @@ -307,7 +307,7 @@ struct CmdProfileInstall : InstallablesCommand, MixDefaultProfile for (auto & installable : installables) { ProfileElement element; - auto & [res, info] = builtPaths[installable.get()]; + auto & [res, info] = builtPaths[&*installable]; if (info.originalRef && info.resolvedRef && info.attrPath && info.extendedOutputsSpec) { element.source = ProfileElementSource { @@ -513,7 +513,7 @@ struct CmdProfileUpgrade : virtual SourceExprCommand, MixDefaultProfile, MixProf auto matchers = getMatchers(store); - std::vector> installables; + Installables installables; std::vector indices; auto upgradedCount = 0; @@ -529,7 +529,7 @@ struct CmdProfileUpgrade : virtual SourceExprCommand, MixDefaultProfile, MixProf Activity act(*logger, lvlChatty, actUnknown, fmt("checking '%s' for updates", element.source->attrPath)); - auto installable = std::make_shared( + auto installable = make_ref( this, getEvalState(), FlakeRef(element.source->originalRef), @@ -582,7 +582,7 @@ struct CmdProfileUpgrade : virtual SourceExprCommand, MixDefaultProfile, MixProf for (size_t i = 0; i < installables.size(); ++i) { auto & installable = installables.at(i); auto & element = manifest.elements[indices.at(i)]; - element.updateStorePaths(getEvalStore(), store, builtPaths[installable.get()].first); + element.updateStorePaths(getEvalStore(), store, builtPaths[&*installable].first); } updateProfile(manifest.build(store)); @@ -798,7 +798,6 @@ struct CmdProfile : NixMultiCommand { if (!command) throw UsageError("'nix profile' requires a sub-command."); - command->second->prepare(); command->second->run(); } }; diff --git a/src/nix/realisation.cc b/src/nix/realisation.cc index 0d3466515..13db80282 100644 --- a/src/nix/realisation.cc +++ b/src/nix/realisation.cc @@ -21,7 +21,6 @@ struct CmdRealisation : virtual NixMultiCommand { if (!command) throw UsageError("'nix realisation' requires a sub-command."); - command->second->prepare(); command->second->run(); } }; diff --git a/src/nix/registry.cc b/src/nix/registry.cc index b5bdfba95..1f4f820ac 100644 --- a/src/nix/registry.cc +++ b/src/nix/registry.cc @@ -227,7 +227,6 @@ struct CmdRegistry : virtual NixMultiCommand settings.requireExperimentalFeature(Xp::Flakes); if (!command) throw UsageError("'nix registry' requires a sub-command."); - command->second->prepare(); command->second->run(); } }; diff --git a/src/nix/repl.cc b/src/nix/repl.cc index 679bdea77..51d3074b4 100644 --- a/src/nix/repl.cc +++ b/src/nix/repl.cc @@ -5,26 +5,12 @@ namespace nix { -struct CmdRepl : InstallablesCommand +struct CmdRepl : RawInstallablesCommand { CmdRepl() { evalSettings.pureEval = false; } - void prepare() override - { - if (!settings.isExperimentalFeatureEnabled(Xp::ReplFlake) && !(file) && this->_installables.size() >= 1) { - warn("future versions of Nix will require using `--file` to load a file"); - if (this->_installables.size() > 1) - warn("more than one input file is not currently supported"); - auto filePath = this->_installables[0].data(); - file = std::optional(filePath); - _installables.front() = _installables.back(); - _installables.pop_back(); - } - installables = InstallablesCommand::load(); - } - std::vector files; Strings getDefaultFlakeAttrPaths() override @@ -32,11 +18,6 @@ struct CmdRepl : InstallablesCommand return {""}; } - bool useDefaultInstallables() override - { - return file.has_value() or expr.has_value(); - } - bool forceImpureByDefault() override { return true; @@ -54,11 +35,27 @@ struct CmdRepl : InstallablesCommand ; } - void run(ref store) override + void applyDefaultInstallables(std::vector & rawInstallables) override + { + if (!settings.isExperimentalFeatureEnabled(Xp::ReplFlake) && !(file) && rawInstallables.size() >= 1) { + warn("future versions of Nix will require using `--file` to load a file"); + if (rawInstallables.size() > 1) + warn("more than one input file is not currently supported"); + auto filePath = rawInstallables[0].data(); + file = std::optional(filePath); + rawInstallables.front() = rawInstallables.back(); + rawInstallables.pop_back(); + } + if (rawInstallables.empty() && (file.has_value() || expr.has_value())) { + rawInstallables.push_back("."); + } + } + + void run(ref store, std::vector && rawInstallables) override { auto state = getEvalState(); auto getValues = [&]()->AbstractNixRepl::AnnotatedValues{ - auto installables = load(); + auto installables = parseInstallables(store, rawInstallables); AbstractNixRepl::AnnotatedValues values; for (auto & installable: installables){ auto what = installable->what(); diff --git a/src/nix/run.cc b/src/nix/run.cc index 6fca68047..56605d9d5 100644 --- a/src/nix/run.cc +++ b/src/nix/run.cc @@ -97,7 +97,7 @@ struct CmdShell : InstallablesCommand, MixEnvironment ; } - void run(ref store) override + void run(ref store, Installables && installables) override { auto outPaths = Installable::toStorePaths(getEvalStore(), store, Realise::Outputs, OperateOn::Output, installables); @@ -183,7 +183,7 @@ struct CmdRun : InstallableCommand return res; } - void run(ref store) override + void run(ref store, ref installable) override { auto state = getEvalState(); diff --git a/src/nix/search.cc b/src/nix/search.cc index 2e38f7e4b..994ec44c2 100644 --- a/src/nix/search.cc +++ b/src/nix/search.cc @@ -61,7 +61,7 @@ struct CmdSearch : InstallableCommand, MixJSON }; } - void run(ref store) override + void run(ref store, ref installable) override { settings.readOnlyMode = true; evalSettings.enableImportFromDerivation.setDefault(false); diff --git a/src/nix/show-derivation.cc b/src/nix/show-derivation.cc index 520e8b1ce..4a406ae08 100644 --- a/src/nix/show-derivation.cc +++ b/src/nix/show-derivation.cc @@ -39,7 +39,7 @@ struct CmdShowDerivation : InstallablesCommand Category category() override { return catUtility; } - void run(ref store) override + void run(ref store, Installables && installables) override { auto drvPaths = Installable::toDerivations(store, installables, true); diff --git a/src/nix/sigs.cc b/src/nix/sigs.cc index 1431652e0..45cd2e1a6 100644 --- a/src/nix/sigs.cc +++ b/src/nix/sigs.cc @@ -219,7 +219,6 @@ struct CmdKey : NixMultiCommand { if (!command) throw UsageError("'nix key' requires a sub-command."); - command->second->prepare(); command->second->run(); } }; diff --git a/src/nix/store-copy-log.cc b/src/nix/store-copy-log.cc index d5fab5f2f..1dda8c0b8 100644 --- a/src/nix/store-copy-log.cc +++ b/src/nix/store-copy-log.cc @@ -26,7 +26,7 @@ struct CmdCopyLog : virtual CopyCommand, virtual InstallablesCommand Category category() override { return catUtility; } - void run(ref srcStore) override + void run(ref srcStore, Installables && installables) override { auto & srcLogStore = require(*srcStore); diff --git a/src/nix/store-delete.cc b/src/nix/store-delete.cc index ca43f1530..6719227df 100644 --- a/src/nix/store-delete.cc +++ b/src/nix/store-delete.cc @@ -32,7 +32,7 @@ struct CmdStoreDelete : StorePathsCommand ; } - void run(ref store, std::vector && storePaths) override + void run(ref store, StorePaths && storePaths) override { auto & gcStore = require(*store); diff --git a/src/nix/store-repair.cc b/src/nix/store-repair.cc index 8fcb3639a..895e39685 100644 --- a/src/nix/store-repair.cc +++ b/src/nix/store-repair.cc @@ -17,7 +17,7 @@ struct CmdStoreRepair : StorePathsCommand ; } - void run(ref store, std::vector && storePaths) override + void run(ref store, StorePaths && storePaths) override { for (auto & path : storePaths) store->repairPath(path); diff --git a/src/nix/store.cc b/src/nix/store.cc index 44e53c7c7..2879e03b3 100644 --- a/src/nix/store.cc +++ b/src/nix/store.cc @@ -18,7 +18,6 @@ struct CmdStore : virtual NixMultiCommand { if (!command) throw UsageError("'nix store' requires a sub-command."); - command->second->prepare(); command->second->run(); } }; -- cgit v1.2.3 From af4cbdafe782ef3064156b33315469c27c36fcff Mon Sep 17 00:00:00 2001 From: Linus Heckemann Date: Thu, 16 Mar 2023 15:21:02 +0100 Subject: LocalDerivationGoal: set NIX_ATTRS_*_FILE correctly for sandboxed builds --- src/libstore/build/local-derivation-goal.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/libstore/build/local-derivation-goal.cc b/src/libstore/build/local-derivation-goal.cc index 6fb9b86e0..13a977bd1 100644 --- a/src/libstore/build/local-derivation-goal.cc +++ b/src/libstore/build/local-derivation-goal.cc @@ -1154,10 +1154,10 @@ void LocalDerivationGoal::writeStructuredAttrs() writeFile(tmpDir + "/.attrs.sh", rewriteStrings(jsonSh, inputRewrites)); chownToBuilder(tmpDir + "/.attrs.sh"); - env["NIX_ATTRS_SH_FILE"] = tmpDir + "/.attrs.sh"; + env["NIX_ATTRS_SH_FILE"] = tmpDirInSandbox + "/.attrs.sh"; writeFile(tmpDir + "/.attrs.json", rewriteStrings(json.dump(), inputRewrites)); chownToBuilder(tmpDir + "/.attrs.json"); - env["NIX_ATTRS_JSON_FILE"] = tmpDir + "/.attrs.json"; + env["NIX_ATTRS_JSON_FILE"] = tmpDirInSandbox + "/.attrs.json"; } } -- cgit v1.2.3 From c998e0172f31fd5707a8361962ec99b3ff9b1b10 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Sun, 5 Feb 2023 12:16:17 -0500 Subject: Move value-only methods to `InstallableValue` These methods would previously fail on the other `Installable`s, so moving them to this class is more correct as to where they actually work. Additionally, a `InstallableValueCommand` is created to make it easier (or rather no worse than before) to write commands that just work on `InstallableValue`s. Besides being a cleanup to avoid failing default methods, this gets us closer to https://github.com/NixOS/rfcs/pull/134. --- src/libcmd/command-installable-value.cc | 11 +++++++++ src/libcmd/command-installable-value.hh | 13 ++++++++++ src/libcmd/installable-value.cc | 44 +++++++++++++++++++++++++++++++++ src/libcmd/installable-value.hh | 30 ++++++++++++++++++++++ src/libcmd/installables.cc | 17 ------------- src/libcmd/installables.hh | 30 ---------------------- src/nix/app.cc | 3 ++- src/nix/bundle.cc | 6 ++--- src/nix/edit.cc | 6 ++--- src/nix/eval.cc | 8 +++--- src/nix/fmt.cc | 6 +++-- src/nix/repl.cc | 12 +++++---- src/nix/run.cc | 6 ++--- src/nix/search.cc | 6 ++--- 14 files changed, 127 insertions(+), 71 deletions(-) create mode 100644 src/libcmd/command-installable-value.cc create mode 100644 src/libcmd/command-installable-value.hh create mode 100644 src/libcmd/installable-value.cc (limited to 'src') diff --git a/src/libcmd/command-installable-value.cc b/src/libcmd/command-installable-value.cc new file mode 100644 index 000000000..d7581534b --- /dev/null +++ b/src/libcmd/command-installable-value.cc @@ -0,0 +1,11 @@ +#include "command-installable-value.hh" + +namespace nix { + +void InstallableValueCommand::run(ref store, ref installable) +{ + auto installableValue = InstallableValue::require(installable); + run(store, installableValue); +} + +} diff --git a/src/libcmd/command-installable-value.hh b/src/libcmd/command-installable-value.hh new file mode 100644 index 000000000..8e31a0b92 --- /dev/null +++ b/src/libcmd/command-installable-value.hh @@ -0,0 +1,13 @@ +#include "installable-value.hh" +#include "command.hh" + +namespace nix { + +struct InstallableValueCommand : InstallableCommand +{ + virtual void run(ref store, ref installable) = 0; + + void run(ref store, ref installable) override; +}; + +} diff --git a/src/libcmd/installable-value.cc b/src/libcmd/installable-value.cc new file mode 100644 index 000000000..30f80edb2 --- /dev/null +++ b/src/libcmd/installable-value.cc @@ -0,0 +1,44 @@ +#include "installable-value.hh" +#include "eval-cache.hh" + +namespace nix { + +std::vector> +InstallableValue::getCursors(EvalState & state) +{ + auto evalCache = + std::make_shared(std::nullopt, state, + [&]() { return toValue(state).first; }); + return {evalCache->getRoot()}; +} + +ref +InstallableValue::getCursor(EvalState & state) +{ + /* 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 UsageError nonValueInstallable(Installable & installable) +{ + return UsageError("installable '%s' does not correspond to a Nix language value", installable.what()); +} + +InstallableValue & InstallableValue::require(Installable & installable) +{ + auto * castedInstallable = dynamic_cast(&installable); + if (!castedInstallable) + throw nonValueInstallable(installable); + return *castedInstallable; +} + +ref InstallableValue::require(ref installable) +{ + auto castedInstallable = installable.dynamic_pointer_cast(); + if (!castedInstallable) + throw nonValueInstallable(*installable); + return ref { castedInstallable }; +} + +} diff --git a/src/libcmd/installable-value.hh b/src/libcmd/installable-value.hh index c6cdc4797..682c8d942 100644 --- a/src/libcmd/installable-value.hh +++ b/src/libcmd/installable-value.hh @@ -4,11 +4,41 @@ namespace nix { +struct App +{ + std::vector context; + Path program; + // FIXME: add args, sandbox settings, metadata, ... +}; + +struct UnresolvedApp +{ + App unresolved; + App resolve(ref evalStore, ref store); +}; + struct InstallableValue : Installable { ref state; InstallableValue(ref state) : state(state) {} + + virtual std::pair toValue(EvalState & state) = 0; + + /* 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> + getCursors(EvalState & state); + + /* Get the first and most preferred cursor this Installable could refer + to, or throw an exception if none exists. */ + virtual ref + getCursor(EvalState & state); + + UnresolvedApp toApp(EvalState & state); + + static InstallableValue & require(Installable & installable); + static ref require(ref installable); }; } diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc index 5cbf26b88..e2164ec72 100644 --- a/src/libcmd/installables.cc +++ b/src/libcmd/installables.cc @@ -364,23 +364,6 @@ DerivedPathWithInfo Installable::toDerivedPath() return std::move(buildables[0]); } -std::vector> -Installable::getCursors(EvalState & state) -{ - auto evalCache = - std::make_shared(std::nullopt, state, - [&]() { return toValue(state).first; }); - return {evalCache->getRoot()}; -} - -ref -Installable::getCursor(EvalState & state) -{ - /* 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( ref store, const Installable & i, diff --git a/src/libcmd/installables.hh b/src/libcmd/installables.hh index 6c2922d89..c5f3cd683 100644 --- a/src/libcmd/installables.hh +++ b/src/libcmd/installables.hh @@ -18,19 +18,6 @@ struct SourceExprCommand; namespace eval_cache { class EvalCache; class AttrCursor; } -struct App -{ - std::vector context; - Path program; - // FIXME: add args, sandbox settings, metadata, ... -}; - -struct UnresolvedApp -{ - App unresolved; - App resolve(ref evalStore, ref store); -}; - enum class Realise { /* Build the derivation. Postcondition: the derivation outputs exist. */ @@ -92,13 +79,6 @@ struct Installable DerivedPathWithInfo toDerivedPath(); - UnresolvedApp toApp(EvalState & state); - - virtual std::pair toValue(EvalState & state) - { - throw Error("argument '%s' cannot be evaluated", what()); - } - /* Return a value only if this installable is a store path or a symlink to it. */ virtual std::optional getStorePath() @@ -106,16 +86,6 @@ 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> - getCursors(EvalState & state); - - /* Get the first and most preferred cursor this Installable could refer - to, or throw an exception if none exists. */ - virtual ref - getCursor(EvalState & state); - virtual FlakeRef nixpkgsFlakeRef() const { return FlakeRef::fromAttrs({{"type","indirect"}, {"id", "nixpkgs"}}); diff --git a/src/nix/app.cc b/src/nix/app.cc index bfd75e278..fd4569bb4 100644 --- a/src/nix/app.cc +++ b/src/nix/app.cc @@ -1,5 +1,6 @@ #include "installables.hh" #include "installable-derived-path.hh" +#include "installable-value.hh" #include "store-api.hh" #include "eval-inline.hh" #include "eval-cache.hh" @@ -40,7 +41,7 @@ std::string resolveString( return rewriteStrings(toResolve, rewrites); } -UnresolvedApp Installable::toApp(EvalState & state) +UnresolvedApp InstallableValue::toApp(EvalState & state) { auto cursor = getCursor(state); auto attrPath = cursor->getAttrPath(); diff --git a/src/nix/bundle.cc b/src/nix/bundle.cc index 973bbd423..7c32a360e 100644 --- a/src/nix/bundle.cc +++ b/src/nix/bundle.cc @@ -1,5 +1,5 @@ -#include "command.hh" #include "installable-flake.hh" +#include "command-installable-value.hh" #include "common-args.hh" #include "shared.hh" #include "store-api.hh" @@ -8,7 +8,7 @@ using namespace nix; -struct CmdBundle : InstallableCommand +struct CmdBundle : InstallableValueCommand { std::string bundler = "github:NixOS/bundlers"; std::optional outLink; @@ -70,7 +70,7 @@ struct CmdBundle : InstallableCommand return res; } - void run(ref store, ref installable) override + void run(ref store, ref installable) override { auto evalState = getEvalState(); diff --git a/src/nix/edit.cc b/src/nix/edit.cc index c46c1c23c..66629fab0 100644 --- a/src/nix/edit.cc +++ b/src/nix/edit.cc @@ -1,4 +1,4 @@ -#include "command.hh" +#include "command-installable-value.hh" #include "shared.hh" #include "eval.hh" #include "attr-path.hh" @@ -9,7 +9,7 @@ using namespace nix; -struct CmdEdit : InstallableCommand +struct CmdEdit : InstallableValueCommand { std::string description() override { @@ -25,7 +25,7 @@ struct CmdEdit : InstallableCommand Category category() override { return catSecondary; } - void run(ref store, ref installable) override + void run(ref store, ref installable) override { auto state = getEvalState(); diff --git a/src/nix/eval.cc b/src/nix/eval.cc index 6c2b60427..43db5150c 100644 --- a/src/nix/eval.cc +++ b/src/nix/eval.cc @@ -1,4 +1,4 @@ -#include "command.hh" +#include "command-installable-value.hh" #include "common-args.hh" #include "shared.hh" #include "store-api.hh" @@ -11,13 +11,13 @@ using namespace nix; -struct CmdEval : MixJSON, InstallableCommand, MixReadOnlyOption +struct CmdEval : MixJSON, InstallableValueCommand, MixReadOnlyOption { bool raw = false; std::optional apply; std::optional writeTo; - CmdEval() : InstallableCommand() + CmdEval() : InstallableValueCommand() { addFlag({ .longName = "raw", @@ -54,7 +54,7 @@ struct CmdEval : MixJSON, InstallableCommand, MixReadOnlyOption Category category() override { return catSecondary; } - void run(ref store, ref installable) override + void run(ref store, ref installable) override { if (raw && json) throw UsageError("--raw and --json are mutually exclusive"); diff --git a/src/nix/fmt.cc b/src/nix/fmt.cc index 6f6a4a632..c85eacded 100644 --- a/src/nix/fmt.cc +++ b/src/nix/fmt.cc @@ -1,4 +1,5 @@ #include "command.hh" +#include "installable-value.hh" #include "run.hh" using namespace nix; @@ -31,8 +32,9 @@ struct CmdFmt : SourceExprCommand { auto evalState = getEvalState(); auto evalStore = getEvalStore(); - auto installable = parseInstallable(store, "."); - auto app = installable->toApp(*evalState).resolve(evalStore, store); + auto installable_ = parseInstallable(store, "."); + auto & installable = InstallableValue::require(*installable_); + auto app = installable.toApp(*evalState).resolve(evalStore, store); Strings programArgs{app.program}; diff --git a/src/nix/repl.cc b/src/nix/repl.cc index 51d3074b4..c2d77ad5f 100644 --- a/src/nix/repl.cc +++ b/src/nix/repl.cc @@ -1,6 +1,7 @@ #include "eval.hh" #include "globals.hh" #include "command.hh" +#include "installable-value.hh" #include "repl.hh" namespace nix { @@ -57,11 +58,12 @@ struct CmdRepl : RawInstallablesCommand auto getValues = [&]()->AbstractNixRepl::AnnotatedValues{ auto installables = parseInstallables(store, rawInstallables); AbstractNixRepl::AnnotatedValues values; - for (auto & installable: installables){ - auto what = installable->what(); + for (auto & installable_: installables){ + auto & installable = InstallableValue::require(*installable_); + auto what = installable.what(); if (file){ - auto [val, pos] = installable->toValue(*state); - auto what = installable->what(); + auto [val, pos] = installable.toValue(*state); + auto what = installable.what(); state->forceValue(*val, pos); auto autoArgs = getAutoArgs(*state); auto valPost = state->allocValue(); @@ -69,7 +71,7 @@ struct CmdRepl : RawInstallablesCommand state->forceValue(*valPost, pos); values.push_back( {valPost, what }); } else { - auto [val, pos] = installable->toValue(*state); + auto [val, pos] = installable.toValue(*state); values.push_back( {val, what} ); } } diff --git a/src/nix/run.cc b/src/nix/run.cc index 56605d9d5..1baf299ab 100644 --- a/src/nix/run.cc +++ b/src/nix/run.cc @@ -1,5 +1,5 @@ #include "run.hh" -#include "command.hh" +#include "command-installable-value.hh" #include "common-args.hh" #include "shared.hh" #include "store-api.hh" @@ -137,7 +137,7 @@ struct CmdShell : InstallablesCommand, MixEnvironment static auto rCmdShell = registerCommand("shell"); -struct CmdRun : InstallableCommand +struct CmdRun : InstallableValueCommand { using InstallableCommand::run; @@ -183,7 +183,7 @@ struct CmdRun : InstallableCommand return res; } - void run(ref store, ref installable) override + void run(ref store, ref installable) override { auto state = getEvalState(); diff --git a/src/nix/search.cc b/src/nix/search.cc index 994ec44c2..c92ed1663 100644 --- a/src/nix/search.cc +++ b/src/nix/search.cc @@ -1,4 +1,4 @@ -#include "command.hh" +#include "command-installable-value.hh" #include "globals.hh" #include "eval.hh" #include "eval-inline.hh" @@ -22,7 +22,7 @@ std::string wrap(std::string prefix, std::string s) return concatStrings(prefix, s, ANSI_NORMAL); } -struct CmdSearch : InstallableCommand, MixJSON +struct CmdSearch : InstallableValueCommand, MixJSON { std::vector res; std::vector excludeRes; @@ -61,7 +61,7 @@ struct CmdSearch : InstallableCommand, MixJSON }; } - void run(ref store, ref installable) override + void run(ref store, ref installable) override { settings.readOnlyMode = true; evalSettings.enableImportFromDerivation.setDefault(false); -- cgit v1.2.3 From e53e5c38d4a6f89dbb7e33448977a95e5b4ab2eb Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 17 Mar 2023 18:32:18 +0100 Subject: Add a setting for configuring the SSL certificates file This provides a platform-independent way to configure the SSL certificates file in the Nix daemon. Previously we provided instructions for overriding the environment variable in launchd, but that obviously doesn't work with systemd. Now we can just tell users to add ssl-cert-file = /etc/ssl/my-certificate-bundle.crt to their nix.conf. --- src/libstore/filetransfer.cc | 2 +- src/libstore/globals.cc | 18 ++++++++++-------- src/libstore/globals.hh | 15 +++++++++++++-- 3 files changed, 24 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/libstore/filetransfer.cc b/src/libstore/filetransfer.cc index b5fe7c03b..1ba399a29 100644 --- a/src/libstore/filetransfer.cc +++ b/src/libstore/filetransfer.cc @@ -318,7 +318,7 @@ struct curlFileTransfer : public FileTransfer if (request.verifyTLS) { if (settings.caFile != "") - curl_easy_setopt(req, CURLOPT_CAINFO, settings.caFile.c_str()); + curl_easy_setopt(req, CURLOPT_CAINFO, settings.caFile.get().c_str()); } else { curl_easy_setopt(req, CURLOPT_SSL_VERIFYPEER, 0); curl_easy_setopt(req, CURLOPT_SSL_VERIFYHOST, 0); diff --git a/src/libstore/globals.cc b/src/libstore/globals.cc index fae79c1a0..7be5c89b7 100644 --- a/src/libstore/globals.cc +++ b/src/libstore/globals.cc @@ -44,14 +44,9 @@ Settings::Settings() lockCPU = getEnv("NIX_AFFINITY_HACK") == "1"; allowSymlinkedStore = getEnv("NIX_IGNORE_SYMLINK_STORE") == "1"; - caFile = getEnv("NIX_SSL_CERT_FILE").value_or(getEnv("SSL_CERT_FILE").value_or("")); - if (caFile == "") { - for (auto & fn : {"/etc/ssl/certs/ca-certificates.crt", "/nix/var/nix/profiles/default/etc/ssl/certs/ca-bundle.crt"}) - if (pathExists(fn)) { - caFile = fn; - break; - } - } + auto sslOverride = getEnv("NIX_SSL_CERT_FILE").value_or(getEnv("SSL_CERT_FILE").value_or("")); + if (sslOverride != "") + caFile = sslOverride; /* Backwards compatibility. */ auto s = getEnv("NIX_REMOTE_SYSTEMS"); @@ -187,6 +182,13 @@ bool Settings::isWSL1() return hasSuffix(utsbuf.release, "-Microsoft"); } +Path Settings::getDefaultSSLCertFile() +{ + for (auto & fn : {"/etc/ssl/certs/ca-certificates.crt", "/nix/var/nix/profiles/default/etc/ssl/certs/ca-bundle.crt"}) + if (pathExists(fn)) return fn; + return ""; +} + const std::string nixVersion = PACKAGE_VERSION; NLOHMANN_JSON_SERIALIZE_ENUM(SandboxMode, { diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh index 93086eaf8..95f6b5e35 100644 --- a/src/libstore/globals.hh +++ b/src/libstore/globals.hh @@ -64,6 +64,8 @@ class Settings : public Config { bool isWSL1(); + Path getDefaultSSLCertFile(); + public: Settings(); @@ -826,8 +828,17 @@ public: > `.netrc`. )"}; - /* Path to the SSL CA file used */ - Path caFile; + Setting caFile{ + this, getDefaultSSLCertFile(), "ssl-cert-file", + R"( + The path of a file containing CA certificates used to + authenticate `https://` downloads. It defaults to the first + of `/etc/ssl/certs/ca-certificates.crt` and + `/nix/var/nix/profiles/default/etc/ssl/certs/ca-bundle.crt` + that exists. It can be overriden using the + `NIX_SSL_CERT_FILE` and `SSL_CERT_FILE` environment variable + (in that order of precedence). + )"}; #if __linux__ Setting filterSyscalls{ -- cgit v1.2.3 From 54b8da0b1050d538a8fb640ba074d0cfe35807c1 Mon Sep 17 00:00:00 2001 From: figsoda Date: Sun, 19 Mar 2023 11:26:48 -0400 Subject: docs: quote URL literals --- src/nix/flake.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/nix/flake.md b/src/nix/flake.md index 9073d0c3b..8eaa41b96 100644 --- a/src/nix/flake.md +++ b/src/nix/flake.md @@ -54,7 +54,7 @@ output attribute). They are also allowed in the `inputs` attribute of a flake, e.g. ```nix -inputs.nixpkgs.url = github:NixOS/nixpkgs; +inputs.nixpkgs.url = "github:NixOS/nixpkgs"; ``` is equivalent to @@ -282,7 +282,7 @@ Nixpkgs flake and provides a single package (i.e. an { description = "A flake for building Hello World"; - inputs.nixpkgs.url = github:NixOS/nixpkgs/nixos-20.03; + inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-20.03"; outputs = { self, nixpkgs }: { @@ -374,7 +374,7 @@ inputs.nixpkgs = { Alternatively, you can use the URL-like syntax: ```nix -inputs.import-cargo.url = github:edolstra/import-cargo; +inputs.import-cargo.url = "github:edolstra/import-cargo"; inputs.nixpkgs.url = "nixpkgs"; ``` -- cgit v1.2.3 From 296831f641b2ce43f179ec710c3ef76726ef96e2 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Fri, 17 Mar 2023 10:33:48 -0400 Subject: Move enabled experimental feature to libutil struct This is needed in subsequent commits to allow the settings and CLI args infrastructure itself to read this setting. --- src/build-remote/build-remote.cc | 4 +-- src/libcmd/common-eval-args.cc | 2 +- src/libcmd/installables.cc | 2 +- src/libexpr/flake/flake.cc | 2 +- src/libexpr/parser.y | 4 +-- src/libexpr/primops.cc | 6 ++--- src/libexpr/primops/fetchTree.cc | 2 +- src/libstore/build/derivation-goal.cc | 12 ++++----- src/libstore/build/local-derivation-goal.cc | 8 +++--- src/libstore/daemon.cc | 4 +-- src/libstore/derivations.cc | 4 +-- src/libstore/derived-path.cc | 2 +- src/libstore/globals.cc | 12 --------- src/libstore/globals.hh | 8 ------ src/libstore/local-store.cc | 10 ++++---- src/libstore/lock.cc | 2 +- src/libstore/misc.cc | 2 +- src/libstore/remote-store.cc | 4 +-- src/libstore/store-api.cc | 8 +++--- src/libutil/config.cc | 26 +++++++++++++++++++ src/libutil/config.hh | 40 ++++++++++++++++++++++++++--- src/nix-build/nix-build.cc | 2 +- src/nix/develop.cc | 2 +- src/nix/flake.cc | 2 +- src/nix/main.cc | 7 +++-- src/nix/realisation.cc | 2 +- src/nix/registry.cc | 2 +- src/nix/repl.cc | 2 +- 28 files changed, 113 insertions(+), 70 deletions(-) (limited to 'src') diff --git a/src/build-remote/build-remote.cc b/src/build-remote/build-remote.cc index 63e3e3fa9..cfc4baaca 100644 --- a/src/build-remote/build-remote.cc +++ b/src/build-remote/build-remote.cc @@ -305,7 +305,7 @@ connected: std::set missingRealisations; StorePathSet missingPaths; - if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations) && !drv.type().hasKnownOutputPaths()) { + if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations) && !drv.type().hasKnownOutputPaths()) { for (auto & outputName : wantedOutputs) { auto thisOutputHash = outputHashes.at(outputName); auto thisOutputId = DrvOutput{ thisOutputHash, outputName }; @@ -337,7 +337,7 @@ connected: for (auto & realisation : missingRealisations) { // Should hold, because if the feature isn't enabled the set // of missing realisations should be empty - settings.requireExperimentalFeature(Xp::CaDerivations); + experimentalFeatureSettings.require(Xp::CaDerivations); store->registerDrvOutput(realisation); } diff --git a/src/libcmd/common-eval-args.cc b/src/libcmd/common-eval-args.cc index 908127b4d..a954a8c6f 100644 --- a/src/libcmd/common-eval-args.cc +++ b/src/libcmd/common-eval-args.cc @@ -166,7 +166,7 @@ Path lookupFileArg(EvalState & state, std::string_view s) } else if (hasPrefix(s, "flake:")) { - settings.requireExperimentalFeature(Xp::Flakes); + experimentalFeatureSettings.require(Xp::Flakes); auto flakeRef = parseFlakeRef(std::string(s.substr(6)), {}, true, false); auto storePath = flakeRef.resolve(state.store).fetchTree(state.store).first.storePath; return state.store->toRealPath(storePath); diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc index 5cbf26b88..cf2096984 100644 --- a/src/libcmd/installables.cc +++ b/src/libcmd/installables.cc @@ -332,7 +332,7 @@ void completeFlakeRefWithFragment( void completeFlakeRef(ref store, std::string_view prefix) { - if (!settings.isExperimentalFeatureEnabled(Xp::Flakes)) + if (!experimentalFeatureSettings.isEnabled(Xp::Flakes)) return; if (prefix == "") diff --git a/src/libexpr/flake/flake.cc b/src/libexpr/flake/flake.cc index 336eb274d..81e94848a 100644 --- a/src/libexpr/flake/flake.cc +++ b/src/libexpr/flake/flake.cc @@ -320,7 +320,7 @@ LockedFlake lockFlake( const FlakeRef & topRef, const LockFlags & lockFlags) { - settings.requireExperimentalFeature(Xp::Flakes); + experimentalFeatureSettings.require(Xp::Flakes); FlakeCache flakeCache; diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index 0f75ed9a0..97e615c37 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -469,7 +469,7 @@ expr_simple new ExprString(std::move(path))}); } | URI { - static bool noURLLiterals = settings.isExperimentalFeatureEnabled(Xp::NoUrlLiterals); + static bool noURLLiterals = experimentalFeatureSettings.isEnabled(Xp::NoUrlLiterals); if (noURLLiterals) throw ParseError({ .msg = hintfmt("URL literals are disabled"), @@ -816,7 +816,7 @@ std::pair EvalState::resolveSearchPathElem(const SearchPathEl } else if (hasPrefix(elem.second, "flake:")) { - settings.requireExperimentalFeature(Xp::Flakes); + experimentalFeatureSettings.require(Xp::Flakes); auto flakeRef = parseFlakeRef(elem.second.substr(6), {}, true, false); debug("fetching flake search path element '%s''", elem.second); auto storePath = flakeRef.resolve(store).fetchTree(store).first.storePath; diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index fb7fc3ddb..3641ee468 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -1141,13 +1141,13 @@ drvName, Bindings * attrs, Value & v) if (i->name == state.sContentAddressed) { contentAddressed = state.forceBool(*i->value, noPos, context_below); if (contentAddressed) - settings.requireExperimentalFeature(Xp::CaDerivations); + experimentalFeatureSettings.require(Xp::CaDerivations); } else if (i->name == state.sImpure) { isImpure = state.forceBool(*i->value, noPos, context_below); if (isImpure) - settings.requireExperimentalFeature(Xp::ImpureDerivations); + experimentalFeatureSettings.require(Xp::ImpureDerivations); } /* The `args' attribute is special: it supplies the @@ -4114,7 +4114,7 @@ void EvalState::createBaseEnv() if (RegisterPrimOp::primOps) for (auto & primOp : *RegisterPrimOp::primOps) if (!primOp.experimentalFeature - || settings.isExperimentalFeatureEnabled(*primOp.experimentalFeature)) + || experimentalFeatureSettings.isEnabled(*primOp.experimentalFeature)) { addPrimOp({ .fun = primOp.fun, diff --git a/src/libexpr/primops/fetchTree.cc b/src/libexpr/primops/fetchTree.cc index fd51dfb90..b7a19c094 100644 --- a/src/libexpr/primops/fetchTree.cc +++ b/src/libexpr/primops/fetchTree.cc @@ -190,7 +190,7 @@ static void fetchTree( static void prim_fetchTree(EvalState & state, const PosIdx pos, Value * * args, Value & v) { - settings.requireExperimentalFeature(Xp::Flakes); + experimentalFeatureSettings.require(Xp::Flakes); fetchTree(state, pos, args, v, std::nullopt, FetchTreeParams { .allowNameArgument = false }); } diff --git a/src/libstore/build/derivation-goal.cc b/src/libstore/build/derivation-goal.cc index 38b73d531..596034c0f 100644 --- a/src/libstore/build/derivation-goal.cc +++ b/src/libstore/build/derivation-goal.cc @@ -199,10 +199,10 @@ void DerivationGoal::haveDerivation() parsedDrv = std::make_unique(drvPath, *drv); if (!drv->type().hasKnownOutputPaths()) - settings.requireExperimentalFeature(Xp::CaDerivations); + experimentalFeatureSettings.require(Xp::CaDerivations); if (!drv->type().isPure()) { - settings.requireExperimentalFeature(Xp::ImpureDerivations); + experimentalFeatureSettings.require(Xp::ImpureDerivations); for (auto & [outputName, output] : drv->outputs) { auto randomPath = StorePath::random(outputPathName(drv->name, outputName)); @@ -336,7 +336,7 @@ void DerivationGoal::gaveUpOnSubstitution() for (auto & i : dynamic_cast(drv.get())->inputDrvs) { /* Ensure that pure, non-fixed-output derivations don't depend on impure derivations. */ - if (settings.isExperimentalFeatureEnabled(Xp::ImpureDerivations) && drv->type().isPure() && !drv->type().isFixed()) { + if (experimentalFeatureSettings.isEnabled(Xp::ImpureDerivations) && drv->type().isPure() && !drv->type().isFixed()) { auto inputDrv = worker.evalStore.readDerivation(i.first); if (!inputDrv.type().isPure()) throw Error("pure derivation '%s' depends on impure derivation '%s'", @@ -477,7 +477,7 @@ void DerivationGoal::inputsRealised() ca.fixed /* Can optionally resolve if fixed, which is good for avoiding unnecessary rebuilds. */ - ? settings.isExperimentalFeatureEnabled(Xp::CaDerivations) + ? experimentalFeatureSettings.isEnabled(Xp::CaDerivations) /* Must resolve if floating and there are any inputs drvs. */ : true); @@ -488,7 +488,7 @@ void DerivationGoal::inputsRealised() }, drvType.raw()); if (resolveDrv && !fullDrv.inputDrvs.empty()) { - settings.requireExperimentalFeature(Xp::CaDerivations); + experimentalFeatureSettings.require(Xp::CaDerivations); /* We are be able to resolve this derivation based on the now-known results of dependencies. If so, we become a @@ -1352,7 +1352,7 @@ std::pair DerivationGoal::checkPathValidity() }; } auto drvOutput = DrvOutput{info.outputHash, i.first}; - if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations)) { + if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations)) { if (auto real = worker.store.queryRealisation(drvOutput)) { info.known = { .path = real->outPath, diff --git a/src/libstore/build/local-derivation-goal.cc b/src/libstore/build/local-derivation-goal.cc index 521117c68..923530d08 100644 --- a/src/libstore/build/local-derivation-goal.cc +++ b/src/libstore/build/local-derivation-goal.cc @@ -413,7 +413,7 @@ void LocalDerivationGoal::startBuilder() ) { #if __linux__ - settings.requireExperimentalFeature(Xp::Cgroups); + experimentalFeatureSettings.require(Xp::Cgroups); auto cgroupFS = getCgroupFS(); if (!cgroupFS) @@ -1393,7 +1393,7 @@ struct RestrictedStore : public virtual RestrictedStoreConfig, public virtual Lo void LocalDerivationGoal::startDaemon() { - settings.requireExperimentalFeature(Xp::RecursiveNix); + experimentalFeatureSettings.require(Xp::RecursiveNix); Store::Params params; params["path-info-cache-size"] = "0"; @@ -2268,7 +2268,7 @@ DrvOutputs LocalDerivationGoal::registerOutputs() bool discardReferences = false; if (auto structuredAttrs = parsedDrv->getStructuredAttrs()) { if (auto udr = get(*structuredAttrs, "unsafeDiscardReferences")) { - settings.requireExperimentalFeature(Xp::DiscardReferences); + experimentalFeatureSettings.require(Xp::DiscardReferences); if (auto output = get(*udr, outputName)) { if (!output->is_boolean()) throw Error("attribute 'unsafeDiscardReferences.\"%s\"' of derivation '%s' must be a Boolean", outputName, drvPath.to_string()); @@ -2688,7 +2688,7 @@ DrvOutputs LocalDerivationGoal::registerOutputs() }, .outPath = newInfo.path }; - if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations) + if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations) && drv->type().isPure()) { signRealisation(thisRealisation); diff --git a/src/libstore/daemon.cc b/src/libstore/daemon.cc index 7f8b0f905..656ad4587 100644 --- a/src/libstore/daemon.cc +++ b/src/libstore/daemon.cc @@ -231,10 +231,10 @@ struct ClientSettings try { if (name == "ssh-auth-sock") // obsolete ; - else if (name == settings.experimentalFeatures.name) { + else if (name == experimentalFeatureSettings.experimentalFeatures.name) { // We don’t want to forward the experimental features to // the daemon, as that could cause some pretty weird stuff - if (parseFeatures(tokenizeString(value)) != settings.experimentalFeatures.get()) + if (parseFeatures(tokenizeString(value)) != experimentalFeatureSettings.experimentalFeatures.get()) debug("Ignoring the client-specified experimental features"); } else if (name == settings.pluginFiles.name) { if (tokenizeString(value) != settings.pluginFiles.get()) diff --git a/src/libstore/derivations.cc b/src/libstore/derivations.cc index 05dc9a3cc..06cc69056 100644 --- a/src/libstore/derivations.cc +++ b/src/libstore/derivations.cc @@ -221,7 +221,7 @@ static DerivationOutput parseDerivationOutput(const Store & store, } const auto hashType = parseHashType(hashAlgo); if (hash == "impure") { - settings.requireExperimentalFeature(Xp::ImpureDerivations); + experimentalFeatureSettings.require(Xp::ImpureDerivations); assert(pathS == ""); return DerivationOutput::Impure { .method = std::move(method), @@ -236,7 +236,7 @@ static DerivationOutput parseDerivationOutput(const Store & store, }, }; } else { - settings.requireExperimentalFeature(Xp::CaDerivations); + experimentalFeatureSettings.require(Xp::CaDerivations); assert(pathS == ""); return DerivationOutput::CAFloating { .method = std::move(method), diff --git a/src/libstore/derived-path.cc b/src/libstore/derived-path.cc index e0d86a42f..e5f0f1b33 100644 --- a/src/libstore/derived-path.cc +++ b/src/libstore/derived-path.cc @@ -105,7 +105,7 @@ RealisedPath::Set BuiltPath::toRealisedPaths(Store & store) const auto drvHashes = staticOutputHashes(store, store.readDerivation(p.drvPath)); for (auto& [outputName, outputPath] : p.outputs) { - if (settings.isExperimentalFeatureEnabled( + if (experimentalFeatureSettings.isEnabled( Xp::CaDerivations)) { auto drvOutput = get(drvHashes, outputName); if (!drvOutput) diff --git a/src/libstore/globals.cc b/src/libstore/globals.cc index fae79c1a0..8781e10ea 100644 --- a/src/libstore/globals.cc +++ b/src/libstore/globals.cc @@ -166,18 +166,6 @@ StringSet Settings::getDefaultExtraPlatforms() return extraPlatforms; } -bool Settings::isExperimentalFeatureEnabled(const ExperimentalFeature & feature) -{ - auto & f = experimentalFeatures.get(); - return std::find(f.begin(), f.end(), feature) != f.end(); -} - -void Settings::requireExperimentalFeature(const ExperimentalFeature & feature) -{ - if (!isExperimentalFeatureEnabled(feature)) - throw MissingExperimentalFeature(feature); -} - bool Settings::isWSL1() { struct utsname utsbuf; diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh index 93086eaf8..db01ab657 100644 --- a/src/libstore/globals.hh +++ b/src/libstore/globals.hh @@ -3,7 +3,6 @@ #include "types.hh" #include "config.hh" #include "util.hh" -#include "experimental-features.hh" #include #include @@ -932,13 +931,6 @@ public: are loaded as plugins (non-recursively). )"}; - Setting> experimentalFeatures{this, {}, "experimental-features", - "Experimental Nix features to enable."}; - - bool isExperimentalFeatureEnabled(const ExperimentalFeature &); - - void requireExperimentalFeature(const ExperimentalFeature &); - Setting narBufferSize{this, 32 * 1024 * 1024, "nar-buffer-size", "Maximum size of NARs before spilling them to disk."}; diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index c9a466ee8..7782f7b50 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -336,7 +336,7 @@ LocalStore::LocalStore(const Params & params) else openDB(*state, false); - if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations)) { + if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations)) { migrateCASchema(state->db, dbDir + "/ca-schema", globalLock); } @@ -366,7 +366,7 @@ LocalStore::LocalStore(const Params & params) state->stmts->QueryPathFromHashPart.create(state->db, "select path from ValidPaths where path >= ? limit 1;"); state->stmts->QueryValidPaths.create(state->db, "select path from ValidPaths"); - if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations)) { + if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations)) { state->stmts->RegisterRealisedOutput.create(state->db, R"( insert into Realisations (drvPath, outputName, outputPath, signatures) @@ -754,7 +754,7 @@ void LocalStore::checkDerivationOutputs(const StorePath & drvPath, const Derivat void LocalStore::registerDrvOutput(const Realisation & info, CheckSigsFlag checkSigs) { - settings.requireExperimentalFeature(Xp::CaDerivations); + experimentalFeatureSettings.require(Xp::CaDerivations); if (checkSigs == NoCheckSigs || !realisationIsUntrusted(info)) registerDrvOutput(info); else @@ -763,7 +763,7 @@ void LocalStore::registerDrvOutput(const Realisation & info, CheckSigsFlag check void LocalStore::registerDrvOutput(const Realisation & info) { - settings.requireExperimentalFeature(Xp::CaDerivations); + experimentalFeatureSettings.require(Xp::CaDerivations); retrySQLite([&]() { auto state(_state.lock()); if (auto oldR = queryRealisation_(*state, info.id)) { @@ -1052,7 +1052,7 @@ LocalStore::queryPartialDerivationOutputMap(const StorePath & path_) return outputs; }); - if (!settings.isExperimentalFeatureEnabled(Xp::CaDerivations)) + if (!experimentalFeatureSettings.isEnabled(Xp::CaDerivations)) return outputs; auto drv = readInvalidDerivation(path); diff --git a/src/libstore/lock.cc b/src/libstore/lock.cc index 4fe1fcf56..7202a64b3 100644 --- a/src/libstore/lock.cc +++ b/src/libstore/lock.cc @@ -129,7 +129,7 @@ struct AutoUserLock : UserLock useUserNamespace = false; #endif - settings.requireExperimentalFeature(Xp::AutoAllocateUids); + experimentalFeatureSettings.require(Xp::AutoAllocateUids); assert(settings.startId > 0); assert(settings.uidCount % maxIdsPerBuild == 0); assert((uint64_t) settings.startId + (uint64_t) settings.uidCount <= std::numeric_limits::max()); diff --git a/src/libstore/misc.cc b/src/libstore/misc.cc index b28768459..89148d415 100644 --- a/src/libstore/misc.cc +++ b/src/libstore/misc.cc @@ -326,7 +326,7 @@ OutputPathMap resolveDerivedPath(Store & store, const DerivedPath::Built & bfd, throw Error( "the derivation '%s' doesn't have an output named '%s'", store.printStorePath(bfd.drvPath), output); - if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations)) { + if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations)) { DrvOutput outputId { *outputHash, output }; auto realisation = store.queryRealisation(outputId); if (!realisation) diff --git a/src/libstore/remote-store.cc b/src/libstore/remote-store.cc index d1296627a..d24d83117 100644 --- a/src/libstore/remote-store.cc +++ b/src/libstore/remote-store.cc @@ -265,7 +265,7 @@ void RemoteStore::setOptions(Connection & conn) overrides.erase(settings.buildCores.name); overrides.erase(settings.useSubstitutes.name); overrides.erase(loggerSettings.showTrace.name); - overrides.erase(settings.experimentalFeatures.name); + overrides.erase(experimentalFeatureSettings.experimentalFeatures.name); overrides.erase(settings.pluginFiles.name); conn.to << overrides.size(); for (auto & i : overrides) @@ -876,7 +876,7 @@ std::vector RemoteStore::buildPathsWithResults( "the derivation '%s' doesn't have an output named '%s'", printStorePath(bfd.drvPath), output); auto outputId = DrvOutput{ *outputHash, output }; - if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations)) { + if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations)) { auto realisation = queryRealisation(outputId); if (!realisation) diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc index 19b0a7f5f..b0ca1321c 100644 --- a/src/libstore/store-api.cc +++ b/src/libstore/store-api.cc @@ -445,10 +445,10 @@ StringSet StoreConfig::getDefaultSystemFeatures() { auto res = settings.systemFeatures.get(); - if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations)) + if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations)) res.insert("ca-derivations"); - if (settings.isExperimentalFeatureEnabled(Xp::RecursiveNix)) + if (experimentalFeatureSettings.isEnabled(Xp::RecursiveNix)) res.insert("recursive-nix"); return res; @@ -1017,7 +1017,7 @@ std::map copyPaths( for (auto & path : paths) { storePaths.insert(path.path()); if (auto realisation = std::get_if(&path.raw)) { - settings.requireExperimentalFeature(Xp::CaDerivations); + experimentalFeatureSettings.require(Xp::CaDerivations); toplevelRealisations.insert(*realisation); } } @@ -1250,7 +1250,7 @@ std::optional Store::getBuildDerivationPath(const StorePath & path) } } - if (!settings.isExperimentalFeatureEnabled(Xp::CaDerivations) || !isValidPath(path)) + if (!experimentalFeatureSettings.isEnabled(Xp::CaDerivations) || !isValidPath(path)) return path; auto drv = readDerivation(path); diff --git a/src/libutil/config.cc b/src/libutil/config.cc index b349f2d80..3b854eded 100644 --- a/src/libutil/config.cc +++ b/src/libutil/config.cc @@ -444,4 +444,30 @@ GlobalConfig::Register::Register(Config * config) configRegistrations->emplace_back(config); } +ExperimentalFeatureSettings experimentalFeatureSettings; + +static GlobalConfig::Register rSettings(&experimentalFeatureSettings); + +bool ExperimentalFeatureSettings::isEnabled(const ExperimentalFeature & feature) const +{ + auto & f = experimentalFeatures.get(); + return std::find(f.begin(), f.end(), feature) != f.end(); +} + +void ExperimentalFeatureSettings::require(const ExperimentalFeature & feature) const +{ + if (!isEnabled(feature)) + throw MissingExperimentalFeature(feature); +} + +bool ExperimentalFeatureSettings::isEnabled(const std::optional & feature) const +{ + return !feature || isEnabled(*feature); +} + +void ExperimentalFeatureSettings::require(const std::optional & feature) const +{ + if (feature) require(*feature); +} + } diff --git a/src/libutil/config.hh b/src/libutil/config.hh index 7ac43c854..3e6796f50 100644 --- a/src/libutil/config.hh +++ b/src/libutil/config.hh @@ -1,12 +1,13 @@ +#pragma once + #include #include #include -#include "types.hh" - #include -#pragma once +#include "types.hh" +#include "experimental-features.hh" namespace nix { @@ -357,4 +358,37 @@ struct GlobalConfig : public AbstractConfig extern GlobalConfig globalConfig; + +struct ExperimentalFeatureSettings : Config { + + Setting> experimentalFeatures{this, {}, "experimental-features", + "Experimental Nix features to enable."}; + + /** + * Check whether the given experimental feature is enabled. + */ + bool isEnabled(const ExperimentalFeature &) const; + + /** + * Require an experimental feature be enabled, throwing an error if it is + * not. + */ + void require(const ExperimentalFeature &) const; + + /** + * `std::nullopt` pointer means no feature, which means there is nothing that could be + * disabled, and so the function returns true in that case. + */ + bool isEnabled(const std::optional &) const; + + /** + * `std::nullopt` pointer means no feature, which means there is nothing that could be + * disabled, and so the function does nothing in that case. + */ + void require(const std::optional &) const; +}; + +// FIXME: don't use a global variable. +extern ExperimentalFeatureSettings experimentalFeatureSettings; + } diff --git a/src/nix-build/nix-build.cc b/src/nix-build/nix-build.cc index a4b3b1f96..bc7e7eb18 100644 --- a/src/nix-build/nix-build.cc +++ b/src/nix-build/nix-build.cc @@ -440,7 +440,7 @@ static void main_nix_build(int argc, char * * argv) shell = store->printStorePath(shellDrvOutputs.at("out").value()) + "/bin/bash"; } - if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations)) { + if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations)) { auto resolvedDrv = drv.tryResolve(*store); assert(resolvedDrv && "Successfully resolved the derivation"); drv = *resolvedDrv; diff --git a/src/nix/develop.cc b/src/nix/develop.cc index f06ade008..17993874b 100644 --- a/src/nix/develop.cc +++ b/src/nix/develop.cc @@ -208,7 +208,7 @@ static StorePath getDerivationEnvironment(ref store, ref evalStore drv.name += "-env"; drv.env.emplace("name", drv.name); drv.inputSrcs.insert(std::move(getEnvShPath)); - if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations)) { + if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations)) { for (auto & output : drv.outputs) { output.second = DerivationOutput::Deferred {}, drv.env[output.first] = hashPlaceholder(output.first); diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 0a6616396..395180267 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -1328,7 +1328,7 @@ struct CmdFlake : NixMultiCommand { if (!command) throw UsageError("'nix flake' requires a sub-command."); - settings.requireExperimentalFeature(Xp::Flakes); + experimentalFeatureSettings.require(Xp::Flakes); command->second->run(); } }; diff --git a/src/nix/main.cc b/src/nix/main.cc index 7b715f281..da920615f 100644 --- a/src/nix/main.cc +++ b/src/nix/main.cc @@ -297,7 +297,10 @@ void mainWrapped(int argc, char * * argv) } if (argc == 2 && std::string(argv[1]) == "__dump-builtins") { - settings.experimentalFeatures = {Xp::Flakes, Xp::FetchClosure}; + experimentalFeatureSettings.experimentalFeatures = { + Xp::Flakes, + Xp::FetchClosure, + }; evalSettings.pureEval = false; EvalState state({}, openStore("dummy://")); auto res = nlohmann::json::object(); @@ -366,7 +369,7 @@ void mainWrapped(int argc, char * * argv) if (args.command->first != "repl" && args.command->first != "doctor" && args.command->first != "upgrade-nix") - settings.requireExperimentalFeature(Xp::NixCommand); + experimentalFeatureSettings.require(Xp::NixCommand); if (args.useNet && !haveInternet()) { warn("you don't have Internet access; disabling some network-dependent features"); diff --git a/src/nix/realisation.cc b/src/nix/realisation.cc index 13db80282..e19e93219 100644 --- a/src/nix/realisation.cc +++ b/src/nix/realisation.cc @@ -45,7 +45,7 @@ struct CmdRealisationInfo : BuiltPathsCommand, MixJSON void run(ref store, BuiltPaths && paths) override { - settings.requireExperimentalFeature(Xp::CaDerivations); + experimentalFeatureSettings.require(Xp::CaDerivations); RealisedPath::Set realisations; for (auto & builtPath : paths) { diff --git a/src/nix/registry.cc b/src/nix/registry.cc index 1f4f820ac..cb94bbd31 100644 --- a/src/nix/registry.cc +++ b/src/nix/registry.cc @@ -224,7 +224,7 @@ struct CmdRegistry : virtual NixMultiCommand void run() override { - settings.requireExperimentalFeature(Xp::Flakes); + experimentalFeatureSettings.require(Xp::Flakes); if (!command) throw UsageError("'nix registry' requires a sub-command."); command->second->run(); diff --git a/src/nix/repl.cc b/src/nix/repl.cc index 51d3074b4..1b329f1a6 100644 --- a/src/nix/repl.cc +++ b/src/nix/repl.cc @@ -37,7 +37,7 @@ struct CmdRepl : RawInstallablesCommand void applyDefaultInstallables(std::vector & rawInstallables) override { - if (!settings.isExperimentalFeatureEnabled(Xp::ReplFlake) && !(file) && rawInstallables.size() >= 1) { + if (!experimentalFeatureSettings.isEnabled(Xp::ReplFlake) && !(file) && rawInstallables.size() >= 1) { warn("future versions of Nix will require using `--file` to load a file"); if (rawInstallables.size() > 1) warn("more than one input file is not currently supported"); -- cgit v1.2.3 From aa663b7e89d3d02248d37ee9f68b52770b247018 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 16 Jan 2023 17:51:04 -0500 Subject: Mark experimental features on settings We hide them in various ways if the experimental feature isn't enabled. To do this, we had to move the experimental features list out of libnixstore, because the setting machinary itself depends on it. To do that, we made a new `ExperimentalFeatureSettings`. --- src/libfetchers/fetch-settings.hh | 12 ++++++++---- src/libutil/config.cc | 25 +++++++++++++++++-------- src/libutil/config.hh | 15 ++++++++++----- 3 files changed, 35 insertions(+), 17 deletions(-) (limited to 'src') diff --git a/src/libfetchers/fetch-settings.hh b/src/libfetchers/fetch-settings.hh index 7049dea30..4bc2d0e1a 100644 --- a/src/libfetchers/fetch-settings.hh +++ b/src/libfetchers/fetch-settings.hh @@ -75,21 +75,25 @@ struct FetchSettings : public Config Path or URI of the global flake registry. When empty, disables the global flake registry. - )"}; + )", + {}, true, Xp::Flakes}; Setting useRegistries{this, true, "use-registries", - "Whether to use flake registries to resolve flake references."}; + "Whether to use flake registries to resolve flake references.", + {}, true, Xp::Flakes}; Setting acceptFlakeConfig{this, false, "accept-flake-config", - "Whether to accept nix configuration from a flake without prompting."}; + "Whether to accept nix configuration from a flake without prompting.", + {}, true, Xp::Flakes}; Setting commitLockFileSummary{ this, "", "commit-lockfile-summary", R"( The commit summary to use when committing changed flake lock files. If empty, the summary is generated based on the action performed. - )"}; + )", + {}, true, Xp::Flakes}; }; // FIXME: don't use a global variable. diff --git a/src/libutil/config.cc b/src/libutil/config.cc index 3b854eded..0dbb4de81 100644 --- a/src/libutil/config.cc +++ b/src/libutil/config.cc @@ -70,10 +70,17 @@ void AbstractConfig::reapplyUnknownSettings() set(s.first, s.second); } +// Whether we should process the option. Excludes aliases, which are handled elsewhere, and disabled features. +static bool applicable(const Config::SettingData & sd) +{ + return !sd.isAlias + && experimentalFeatureSettings.isEnabled(sd.setting->experimentalFeature); +} + void Config::getSettings(std::map & res, bool overriddenOnly) { for (auto & opt : _settings) - if (!opt.second.isAlias && (!overriddenOnly || opt.second.setting->overridden)) + if (applicable(opt.second) && (!overriddenOnly || opt.second.setting->overridden)) res.emplace(opt.first, SettingInfo{opt.second.setting->to_string(), opt.second.setting->description}); } @@ -147,9 +154,8 @@ nlohmann::json Config::toJSON() { auto res = nlohmann::json::object(); for (auto & s : _settings) - if (!s.second.isAlias) { + if (applicable(s.second)) res.emplace(s.first, s.second.setting->toJSON()); - } return res; } @@ -157,24 +163,27 @@ std::string Config::toKeyValue() { auto res = std::string(); for (auto & s : _settings) - if (!s.second.isAlias) { + if (applicable(s.second)) res += fmt("%s = %s\n", s.first, s.second.setting->to_string()); - } return res; } void Config::convertToArgs(Args & args, const std::string & category) { for (auto & s : _settings) - if (!s.second.isAlias) + if (applicable(s.second)) s.second.setting->convertToArg(args, category); } AbstractSetting::AbstractSetting( const std::string & name, const std::string & description, - const std::set & aliases) - : name(name), description(stripIndentation(description)), aliases(aliases) + const std::set & aliases, + std::optional experimentalFeature) + : name(name) + , description(stripIndentation(description)) + , aliases(aliases) + , experimentalFeature(experimentalFeature) { } diff --git a/src/libutil/config.hh b/src/libutil/config.hh index 3e6796f50..748d6043b 100644 --- a/src/libutil/config.hh +++ b/src/libutil/config.hh @@ -195,12 +195,15 @@ public: bool overridden = false; + std::optional experimentalFeature; + protected: AbstractSetting( const std::string & name, const std::string & description, - const std::set & aliases); + const std::set & aliases, + std::optional experimentalFeature = std::nullopt); virtual ~AbstractSetting() { @@ -241,8 +244,9 @@ public: const bool documentDefault, const std::string & name, const std::string & description, - const std::set & aliases = {}) - : AbstractSetting(name, description, aliases) + const std::set & aliases = {}, + std::optional experimentalFeature = std::nullopt) + : AbstractSetting(name, description, aliases, experimentalFeature) , value(def) , defaultValue(def) , documentDefault(documentDefault) @@ -297,8 +301,9 @@ public: const std::string & name, const std::string & description, const std::set & aliases = {}, - const bool documentDefault = true) - : BaseSetting(def, documentDefault, name, description, aliases) + const bool documentDefault = true, + std::optional experimentalFeature = std::nullopt) + : BaseSetting(def, documentDefault, name, description, aliases, experimentalFeature) { options->addSetting(this); } -- cgit v1.2.3 From 4607ac7aed34b1bc2d7a74bff99c63f3bd684511 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 16 Jan 2023 19:13:31 -0500 Subject: Fix handling of experimental features mid-parse If we conditionally "declare" the argument, as we did before, based upon weather the feature is enabled, commands like nix --experimental-features=foo ... --thing-gated-on-foo won't work, because the experimental feature isn't enabled until *after* we start parsing. Instead, allow arguments to also be associated with experimental features (just as we did for builtins and settings), and then the command line parser will filter out the experimental ones. Since the effects of arguments (handler functions) are performed right away, we get the required behavior: earlier arguments can enable later arguments enabled! There is just one catch: we want to keep non-positional flags...non-positional. So if nix --experimental-features=foo ... --thing-gated-on-foo works, then nix --thing-gated-on-foo --experimental-features=foo ... should also work. This is not my favorite long-term solution, but for now this is implemented by delaying the requirement of needed experimental features until *after* all the arguments have been parsed. --- src/libutil/args.cc | 25 ++++++++++++++++++++++--- src/libutil/args.hh | 14 ++++++++++++-- src/libutil/config.cc | 16 ++++++++++++---- src/libutil/util.hh | 1 - src/nix/main.cc | 13 +++++++------ 5 files changed, 53 insertions(+), 16 deletions(-) (limited to 'src') diff --git a/src/libutil/args.cc b/src/libutil/args.cc index 35686a8aa..fc009592c 100644 --- a/src/libutil/args.cc +++ b/src/libutil/args.cc @@ -52,7 +52,7 @@ std::shared_ptr completions; std::string completionMarker = "___COMPLETE___"; -std::optional needsCompletion(std::string_view s) +static std::optional needsCompletion(std::string_view s) { if (!completions) return {}; auto i = s.find(completionMarker); @@ -120,6 +120,12 @@ void Args::parseCmdline(const Strings & _cmdline) if (!argsSeen) initialFlagsProcessed(); + + /* Now that we are done parsing, make sure that any experimental + * feature required by the flags is enabled */ + for (auto & f : flagExperimentalFeatures) + experimentalFeatureSettings.require(f); + } bool Args::processFlag(Strings::iterator & pos, Strings::iterator end) @@ -128,12 +134,18 @@ bool Args::processFlag(Strings::iterator & pos, Strings::iterator end) auto process = [&](const std::string & name, const Flag & flag) -> bool { ++pos; + + if (auto & f = flag.experimentalFeature) + flagExperimentalFeatures.insert(*f); + std::vector args; bool anyCompleted = false; for (size_t n = 0 ; n < flag.handler.arity; ++n) { if (pos == end) { if (flag.handler.arity == ArityAny || anyCompleted) break; - throw UsageError("flag '%s' requires %d argument(s)", name, flag.handler.arity); + throw UsageError( + "flag '%s' requires %d argument(s), but only %d were given", + name, flag.handler.arity, n); } if (auto prefix = needsCompletion(*pos)) { anyCompleted = true; @@ -152,7 +164,11 @@ bool Args::processFlag(Strings::iterator & pos, Strings::iterator end) for (auto & [name, flag] : longFlags) { if (!hiddenCategories.count(flag->category) && hasPrefix(name, std::string(*prefix, 2))) + { + if (auto & f = flag->experimentalFeature) + flagExperimentalFeatures.insert(*f); completions->add("--" + name, flag->description); + } } return false; } @@ -172,7 +188,8 @@ bool Args::processFlag(Strings::iterator & pos, Strings::iterator end) if (prefix == "-") { completions->add("--"); for (auto & [flagName, flag] : shortFlags) - completions->add(std::string("-") + flagName, flag->description); + if (experimentalFeatureSettings.isEnabled(flag->experimentalFeature)) + completions->add(std::string("-") + flagName, flag->description); } } @@ -219,6 +236,8 @@ nlohmann::json Args::toJSON() auto flags = nlohmann::json::object(); for (auto & [name, flag] : longFlags) { + /* Skip experimental flags when listing flags. */ + if (!experimentalFeatureSettings.isEnabled(flag->experimentalFeature)) continue; auto j = nlohmann::json::object(); if (flag->aliases.count(name)) continue; if (flag->shortName) diff --git a/src/libutil/args.hh b/src/libutil/args.hh index 7211ee307..2969806dd 100644 --- a/src/libutil/args.hh +++ b/src/libutil/args.hh @@ -117,6 +117,8 @@ protected: Handler handler; std::function completer; + std::optional experimentalFeature; + static Flag mkHashTypeFlag(std::string && longName, HashType * ht); static Flag mkHashTypeOptFlag(std::string && longName, std::optional * oht); }; @@ -188,6 +190,16 @@ public: friend class MultiCommand; MultiCommand * parent = nullptr; + +private: + + /** + * Experimental features needed when parsing args. These are checked + * after flag parsing is completed in order to support enabling + * experimental features coming after the flag that needs the + * experimental feature. + */ + std::set flagExperimentalFeatures; }; /* A command is an argument parser that can be executed by calling its @@ -253,8 +265,6 @@ enum CompletionType { }; extern CompletionType completionType; -std::optional needsCompletion(std::string_view s); - void completePath(size_t, std::string_view prefix); void completeDir(size_t, std::string_view prefix); diff --git a/src/libutil/config.cc b/src/libutil/config.cc index 0dbb4de81..8d63536d6 100644 --- a/src/libutil/config.cc +++ b/src/libutil/config.cc @@ -170,9 +170,13 @@ std::string Config::toKeyValue() void Config::convertToArgs(Args & args, const std::string & category) { - for (auto & s : _settings) - if (applicable(s.second)) + for (auto & s : _settings) { + /* We do include args for settings gated on disabled + experimental-features. The args themselves however will also be + gated on any experimental feature the underlying setting is. */ + if (!s.second.isAlias) s.second.setting->convertToArg(args, category); + } } AbstractSetting::AbstractSetting( @@ -219,6 +223,7 @@ void BaseSetting::convertToArg(Args & args, const std::string & category) .category = category, .labels = {"value"}, .handler = {[this](std::string s) { overridden = true; set(s); }}, + .experimentalFeature = experimentalFeature, }); if (isAppendable()) @@ -228,6 +233,7 @@ void BaseSetting::convertToArg(Args & args, const std::string & category) .category = category, .labels = {"value"}, .handler = {[this](std::string s) { overridden = true; set(s, true); }}, + .experimentalFeature = experimentalFeature, }); } @@ -279,13 +285,15 @@ template<> void BaseSetting::convertToArg(Args & args, const std::string & .longName = name, .description = fmt("Enable the `%s` setting.", name), .category = category, - .handler = {[this]() { override(true); }} + .handler = {[this]() { override(true); }}, + .experimentalFeature = experimentalFeature, }); args.addFlag({ .longName = "no-" + name, .description = fmt("Disable the `%s` setting.", name), .category = category, - .handler = {[this]() { override(false); }} + .handler = {[this]() { override(false); }}, + .experimentalFeature = experimentalFeature, }); } diff --git a/src/libutil/util.hh b/src/libutil/util.hh index 52ca36fd1..c4ea6c34b 100644 --- a/src/libutil/util.hh +++ b/src/libutil/util.hh @@ -450,7 +450,6 @@ template Strings quoteStrings(const C & c) return res; } - /* Remove trailing whitespace from a string. FIXME: return std::string_view. */ std::string chomp(std::string_view s); diff --git a/src/nix/main.cc b/src/nix/main.cc index da920615f..c79d39459 100644 --- a/src/nix/main.cc +++ b/src/nix/main.cc @@ -54,12 +54,11 @@ static bool haveInternet() std::string programPath; -struct HelpRequested { }; - struct NixArgs : virtual MultiCommand, virtual MixCommonArgs { bool useNet = true; bool refresh = false; + bool helpRequested = false; bool showVersion = false; NixArgs() : MultiCommand(RegisterCommand::getCommandsFor({})), MixCommonArgs("nix") @@ -74,7 +73,7 @@ struct NixArgs : virtual MultiCommand, virtual MixCommonArgs .longName = "help", .description = "Show usage information.", .category = miscCategory, - .handler = {[&]() { throw HelpRequested(); }}, + .handler = {[this]() { this->helpRequested = true; }}, }); addFlag({ @@ -337,7 +336,11 @@ void mainWrapped(int argc, char * * argv) try { args.parseCmdline(argvToStrings(argc, argv)); - } catch (HelpRequested &) { + } catch (UsageError &) { + if (!args.helpRequested && !completions) throw; + } + + if (args.helpRequested) { std::vector subcommand; MultiCommand * command = &args; while (command) { @@ -349,8 +352,6 @@ void mainWrapped(int argc, char * * argv) } showHelp(subcommand, args); return; - } catch (UsageError &) { - if (!completions) throw; } if (completions) { -- cgit v1.2.3 From 16db8dc96f64a0facbb620907e571f2dfc8e802e Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 20 Mar 2023 17:04:57 +0100 Subject: Open slave pseudoterminal before CLONE_NEWUSER Otherwise, when running as root and user namespaces are enabled, opening the slave fails with EPERM. Fixes "opening pseudoterminal slave: Permission denied" followed by a hang (https://hydra.nixos.org/build/213104244), and "error: getting sandbox mount namespace: No such file or directory" (#8072), which happens when the child fails very quickly and consequently reading /proc//ns fails. --- src/libstore/build/local-derivation-goal.cc | 50 ++++++++++++++++++----------- src/libstore/build/local-derivation-goal.hh | 2 +- src/libutil/util.cc | 2 +- 3 files changed, 33 insertions(+), 21 deletions(-) (limited to 'src') diff --git a/src/libstore/build/local-derivation-goal.cc b/src/libstore/build/local-derivation-goal.cc index 521117c68..4f21862ca 100644 --- a/src/libstore/build/local-derivation-goal.cc +++ b/src/libstore/build/local-derivation-goal.cc @@ -827,6 +827,27 @@ void LocalDerivationGoal::startBuilder() if (unlockpt(builderOut.get())) throw SysError("unlocking pseudoterminal"); + /* Open the slave side of the pseudoterminal and use it as stderr. */ + auto openSlave = [&]() + { + AutoCloseFD builderOut = open(slaveName.c_str(), O_RDWR | O_NOCTTY); + if (!builderOut) + throw SysError("opening pseudoterminal slave"); + + // Put the pt into raw mode to prevent \n -> \r\n translation. + struct termios term; + if (tcgetattr(builderOut.get(), &term)) + throw SysError("getting pseudoterminal attributes"); + + cfmakeraw(&term); + + if (tcsetattr(builderOut.get(), TCSANOW, &term)) + throw SysError("putting pseudoterminal into raw mode"); + + if (dup2(builderOut.get(), STDERR_FILENO) == -1) + throw SysError("cannot pipe standard error into log file"); + }; + buildResult.startTime = time(0); /* Fork a child to build the package. */ @@ -880,6 +901,11 @@ void LocalDerivationGoal::startBuilder() Pid helper = startProcess([&]() { sendPid.readSide.close(); + /* We need to open the slave early, before + CLONE_NEWUSER. Otherwise we get EPERM when running as + root. */ + openSlave(); + /* Drop additional groups here because we can't do it after we've created the new user namespace. FIXME: this means that if we're not root in the parent @@ -898,7 +924,7 @@ void LocalDerivationGoal::startBuilder() if (usingUserNamespace) options.cloneFlags |= CLONE_NEWUSER; - pid_t child = startProcess([&]() { runChild(slaveName); }, options); + pid_t child = startProcess([&]() { runChild(); }, options); writeFull(sendPid.writeSide.get(), fmt("%d\n", child)); _exit(0); @@ -974,7 +1000,8 @@ void LocalDerivationGoal::startBuilder() #endif { pid = startProcess([&]() { - runChild(slaveName); + openSlave(); + runChild(); }); } @@ -1620,7 +1647,7 @@ void setupSeccomp() } -void LocalDerivationGoal::runChild(const Path & slaveName) +void LocalDerivationGoal::runChild() { /* Warning: in the child we should absolutely not make any SQLite calls! */ @@ -1629,22 +1656,7 @@ void LocalDerivationGoal::runChild(const Path & slaveName) try { /* child */ - /* Open the slave side of the pseudoterminal. */ - AutoCloseFD builderOut = open(slaveName.c_str(), O_RDWR | O_NOCTTY); - if (!builderOut) - throw SysError("opening pseudoterminal slave"); - - // Put the pt into raw mode to prevent \n -> \r\n translation. - struct termios term; - if (tcgetattr(builderOut.get(), &term)) - throw SysError("getting pseudoterminal attributes"); - - cfmakeraw(&term); - - if (tcsetattr(builderOut.get(), TCSANOW, &term)) - throw SysError("putting pseudoterminal into raw mode"); - - commonChildInit(builderOut.get()); + commonChildInit(-1); try { setupSeccomp(); diff --git a/src/libstore/build/local-derivation-goal.hh b/src/libstore/build/local-derivation-goal.hh index 4d2f1ac28..c9ecc8828 100644 --- a/src/libstore/build/local-derivation-goal.hh +++ b/src/libstore/build/local-derivation-goal.hh @@ -169,7 +169,7 @@ struct LocalDerivationGoal : public DerivationGoal int getChildStatus() override; /* Run the builder's process. */ - void runChild(const std::string & slaveName); + void runChild(); /* Check that the derivation outputs all exist and register them as valid. */ diff --git a/src/libutil/util.cc b/src/libutil/util.cc index c605a33e6..a703c5650 100644 --- a/src/libutil/util.cc +++ b/src/libutil/util.cc @@ -1983,7 +1983,7 @@ void commonChildInit(int stderrFd) throw SysError("creating a new session"); /* Dup the write side of the logger pipe into stderr. */ - if (dup2(stderrFd, STDERR_FILENO) == -1) + if (stderrFd != -1 && dup2(stderrFd, STDERR_FILENO) == -1) throw SysError("cannot pipe standard error into log file"); /* Dup stderr to stdout. */ -- cgit v1.2.3 From 515662ad703cbd7c34df0020947392d233ac82eb Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 20 Mar 2023 18:06:08 +0100 Subject: Cleanup --- src/libstore/build/hook-instance.cc | 5 ++++- src/libstore/build/local-derivation-goal.cc | 2 +- src/libutil/util.cc | 6 +----- src/libutil/util.hh | 2 +- 4 files changed, 7 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/libstore/build/hook-instance.cc b/src/libstore/build/hook-instance.cc index ea2ae210e..075ad554f 100644 --- a/src/libstore/build/hook-instance.cc +++ b/src/libstore/build/hook-instance.cc @@ -35,7 +35,10 @@ HookInstance::HookInstance() /* Fork the hook. */ pid = startProcess([&]() { - commonChildInit(fromHook.writeSide.get()); + if (dup2(fromHook.writeSide.get(), STDERR_FILENO) == -1) + throw SysError("cannot pipe standard error into log file"); + + commonChildInit(); if (chdir("/") == -1) throw SysError("changing into /"); diff --git a/src/libstore/build/local-derivation-goal.cc b/src/libstore/build/local-derivation-goal.cc index 4f21862ca..3484d2044 100644 --- a/src/libstore/build/local-derivation-goal.cc +++ b/src/libstore/build/local-derivation-goal.cc @@ -1656,7 +1656,7 @@ void LocalDerivationGoal::runChild() try { /* child */ - commonChildInit(-1); + commonChildInit(); try { setupSeccomp(); diff --git a/src/libutil/util.cc b/src/libutil/util.cc index a703c5650..843a10eab 100644 --- a/src/libutil/util.cc +++ b/src/libutil/util.cc @@ -1968,7 +1968,7 @@ std::string showBytes(uint64_t bytes) // FIXME: move to libstore/build -void commonChildInit(int stderrFd) +void commonChildInit() { logger = makeSimpleLogger(); @@ -1982,10 +1982,6 @@ void commonChildInit(int stderrFd) if (setsid() == -1) throw SysError("creating a new session"); - /* Dup the write side of the logger pipe into stderr. */ - if (stderrFd != -1 && dup2(stderrFd, STDERR_FILENO) == -1) - throw SysError("cannot pipe standard error into log file"); - /* Dup stderr to stdout. */ if (dup2(STDERR_FILENO, STDOUT_FILENO) == -1) throw SysError("cannot dup stderr into stdout"); diff --git a/src/libutil/util.hh b/src/libutil/util.hh index 52ca36fd1..a8e6f9fbb 100644 --- a/src/libutil/util.hh +++ b/src/libutil/util.hh @@ -704,7 +704,7 @@ typedef std::function PathFilter; extern PathFilter defaultPathFilter; /* Common initialisation performed in child processes. */ -void commonChildInit(int stderrFd); +void commonChildInit(); /* Create a Unix domain socket. */ AutoCloseFD createUnixDomainSocket(); -- cgit v1.2.3 From f87f1bbfe60e14d2687e5210954e2d6ce2249df9 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 20 Mar 2023 14:10:35 -0400 Subject: Fix `nix profile --help` The documentation hadn't be updated to reflect the new default profile location. --- src/nix/profile.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/nix/profile.md b/src/nix/profile.md index 273e02280..bf61ef4b9 100644 --- a/src/nix/profile.md +++ b/src/nix/profile.md @@ -12,7 +12,7 @@ them to be rolled back easily. The default profile used by `nix profile` is `$HOME/.nix-profile`, which, if it does not exist, is created as a symlink to `/nix/var/nix/profiles/default` if Nix is invoked by the -`root` user, or `/nix/var/nix/profiles/per-user/`*username* otherwise. +`root` user, or `${XDG_STATE_HOME-$HOME/.local/state}/nix/profiles/profile` otherwise. You can specify another profile location using `--profile` *path*. @@ -24,11 +24,11 @@ the profile. In turn, *path*`-`*N* is a symlink to a path in the Nix store. For example: ```console -$ ls -l /nix/var/nix/profiles/per-user/alice/profile* -lrwxrwxrwx 1 alice users 14 Nov 25 14:35 /nix/var/nix/profiles/per-user/alice/profile -> profile-7-link -lrwxrwxrwx 1 alice users 51 Oct 28 16:18 /nix/var/nix/profiles/per-user/alice/profile-5-link -> /nix/store/q69xad13ghpf7ir87h0b2gd28lafjj1j-profile -lrwxrwxrwx 1 alice users 51 Oct 29 13:20 /nix/var/nix/profiles/per-user/alice/profile-6-link -> /nix/store/6bvhpysd7vwz7k3b0pndn7ifi5xr32dg-profile -lrwxrwxrwx 1 alice users 51 Nov 25 14:35 /nix/var/nix/profiles/per-user/alice/profile-7-link -> /nix/store/mp0x6xnsg0b8qhswy6riqvimai4gm677-profile +$ ls -l ~alice/.local/state/nix/profiles/profile* +lrwxrwxrwx 1 alice users 14 Nov 25 14:35 /home/alice/.local/state/nix/profiles/profile -> profile-7-link +lrwxrwxrwx 1 alice users 51 Oct 28 16:18 /home/alice/.local/state/nix/profiles/profile-5-link -> /nix/store/q69xad13ghpf7ir87h0b2gd28lafjj1j-profile +lrwxrwxrwx 1 alice users 51 Oct 29 13:20 /home/alice/.local/state/nix/profiles/profile-6-link -> /nix/store/6bvhpysd7vwz7k3b0pndn7ifi5xr32dg-profile +lrwxrwxrwx 1 alice users 51 Nov 25 14:35 /home/alice/.local/state/nix/profiles/profile-7-link -> /nix/store/mp0x6xnsg0b8qhswy6riqvimai4gm677-profile ``` Each of these symlinks is a root for the Nix garbage collector. @@ -38,20 +38,20 @@ profile is a tree of symlinks to the files of the installed packages, e.g. ```console -$ ll -R /nix/var/nix/profiles/per-user/eelco/profile-7-link/ -/nix/var/nix/profiles/per-user/eelco/profile-7-link/: +$ ll -R ~eelco/.local/state/nix/profiles/profile-7-link/ +/home/eelco/.local/state/nix/profiles/profile-7-link/: total 20 dr-xr-xr-x 2 root root 4096 Jan 1 1970 bin -r--r--r-- 2 root root 1402 Jan 1 1970 manifest.json dr-xr-xr-x 4 root root 4096 Jan 1 1970 share -/nix/var/nix/profiles/per-user/eelco/profile-7-link/bin: +/home/eelco/.local/state/nix/profiles/profile-7-link/bin: total 20 lrwxrwxrwx 5 root root 79 Jan 1 1970 chromium -> /nix/store/ijm5k0zqisvkdwjkc77mb9qzb35xfi4m-chromium-86.0.4240.111/bin/chromium lrwxrwxrwx 7 root root 87 Jan 1 1970 spotify -> /nix/store/w9182874m1bl56smps3m5zjj36jhp3rn-spotify-1.1.26.501.gbe11e53b-15/bin/spotify lrwxrwxrwx 3 root root 79 Jan 1 1970 zoom-us -> /nix/store/wbhg2ga8f3h87s9h5k0slxk0m81m4cxl-zoom-us-5.3.469451.0927/bin/zoom-us -/nix/var/nix/profiles/per-user/eelco/profile-7-link/share/applications: +/home/eelco/.local/state/nix/profiles/profile-7-link/share/applications: total 12 lrwxrwxrwx 4 root root 120 Jan 1 1970 chromium-browser.desktop -> /nix/store/4cf803y4vzfm3gyk3vzhzb2327v0kl8a-chromium-unwrapped-86.0.4240.111/share/applications/chromium-browser.desktop lrwxrwxrwx 7 root root 110 Jan 1 1970 spotify.desktop -> /nix/store/w9182874m1bl56smps3m5zjj36jhp3rn-spotify-1.1.26.501.gbe11e53b-15/share/applications/spotify.desktop -- cgit v1.2.3 From 3ac3e31c4d55fb9a3063274a68cd125f73bd3454 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 21 Mar 2023 12:02:19 +0100 Subject: Fix misrendering of 'nix store --help' There are no categories underneath 'nix store', so having 'nix store copy-log' in a category rendered as ':'. --- src/nix/store-copy-log.cc | 2 -- 1 file changed, 2 deletions(-) (limited to 'src') diff --git a/src/nix/store-copy-log.cc b/src/nix/store-copy-log.cc index 1dda8c0b8..a6e8aeff7 100644 --- a/src/nix/store-copy-log.cc +++ b/src/nix/store-copy-log.cc @@ -24,8 +24,6 @@ struct CmdCopyLog : virtual CopyCommand, virtual InstallablesCommand ; } - Category category() override { return catUtility; } - void run(ref srcStore, Installables && installables) override { auto & srcLogStore = require(*srcStore); -- cgit v1.2.3 From 8d6d59cb1ba0a2cfe12f9f444a27833dc531c191 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 21 Mar 2023 12:58:14 +0100 Subject: nix store --help: Include store type documentation --- src/libstore/http-binary-cache-store.cc | 2 +- src/nix/describe-stores.cc | 2 +- src/nix/main.cc | 31 ++++++++++++++++++++++++------- src/nix/store.cc | 7 +++++++ src/nix/store.md | 9 +++++++++ 5 files changed, 42 insertions(+), 9 deletions(-) create mode 100644 src/nix/store.md (limited to 'src') diff --git a/src/libstore/http-binary-cache-store.cc b/src/libstore/http-binary-cache-store.cc index 1479822a9..93f25363a 100644 --- a/src/libstore/http-binary-cache-store.cc +++ b/src/libstore/http-binary-cache-store.cc @@ -12,7 +12,7 @@ struct HttpBinaryCacheStoreConfig : virtual BinaryCacheStoreConfig { using BinaryCacheStoreConfig::BinaryCacheStoreConfig; - const std::string name() override { return "Http Binary Cache Store"; } + const std::string name() override { return "HTTP Binary Cache Store"; } }; class HttpBinaryCacheStore : public virtual HttpBinaryCacheStoreConfig, public virtual BinaryCacheStore diff --git a/src/nix/describe-stores.cc b/src/nix/describe-stores.cc index eafcedd1f..ad6b2f0f8 100644 --- a/src/nix/describe-stores.cc +++ b/src/nix/describe-stores.cc @@ -22,7 +22,7 @@ struct CmdDescribeStores : Command, MixJSON for (auto & implem : *Implementations::registered) { auto storeConfig = implem.getConfig(); auto storeName = storeConfig->name(); - res[storeName] = storeConfig->toJSON(); + res[storeName]["settings"] = storeConfig->toJSON(); } if (json) { logger->cout("%s", res); diff --git a/src/nix/main.cc b/src/nix/main.cc index 7b715f281..0ea6d7784 100644 --- a/src/nix/main.cc +++ b/src/nix/main.cc @@ -164,11 +164,28 @@ struct NixArgs : virtual MultiCommand, virtual MixCommonArgs { commands = RegisterCommand::getCommandsFor({}); } + + std::string dumpCli() + { + auto res = nlohmann::json::object(); + + res["args"] = toJSON(); + + auto stores = nlohmann::json::object(); + for (auto & implem : *Implementations::registered) { + auto storeConfig = implem.getConfig(); + auto storeName = storeConfig->name(); + stores[storeName]["settings"] = storeConfig->toJSON(); + } + res["stores"] = std::move(stores); + + return res.dump(); + } }; /* Render the help for the specified subcommand to stdout using lowdown. */ -static void showHelp(std::vector subcommand, MultiCommand & toplevel) +static void showHelp(std::vector subcommand, NixArgs & toplevel) { auto mdName = subcommand.empty() ? "nix" : fmt("nix3-%s", concatStringsSep("-", subcommand)); @@ -189,11 +206,11 @@ static void showHelp(std::vector subcommand, MultiCommand & topleve , "/"), *vUtils); - auto attrs = state.buildBindings(16); - attrs.alloc("toplevel").mkString(toplevel.toJSON().dump()); + auto vDump = state.allocValue(); + vDump->mkString(toplevel.dumpCli()); auto vRes = state.allocValue(); - state.callFunction(*vGenerateManpage, state.allocValue()->mkAttrs(attrs), *vRes, noPos); + state.callFunction(*vGenerateManpage, *vDump, *vRes, noPos); auto attr = vRes->attrs->get(state.symbols.create(mdName + ".md")); if (!attr) @@ -234,7 +251,7 @@ struct CmdHelp : Command assert(parent); MultiCommand * toplevel = parent; while (toplevel->parent) toplevel = toplevel->parent; - showHelp(subcommand, *toplevel); + showHelp(subcommand, dynamic_cast(*toplevel)); } }; @@ -291,8 +308,8 @@ void mainWrapped(int argc, char * * argv) NixArgs args; - if (argc == 2 && std::string(argv[1]) == "__dump-args") { - logger->cout("%s", args.toJSON()); + if (argc == 2 && std::string(argv[1]) == "__dump-cli") { + logger->cout(args.dumpCli()); return; } diff --git a/src/nix/store.cc b/src/nix/store.cc index 2879e03b3..72b037488 100644 --- a/src/nix/store.cc +++ b/src/nix/store.cc @@ -12,6 +12,13 @@ struct CmdStore : virtual NixMultiCommand return "manipulate a Nix store"; } + std::string doc() override + { + return + #include "store.md" + ; + } + Category category() override { return catUtility; } void run() override diff --git a/src/nix/store.md b/src/nix/store.md new file mode 100644 index 000000000..d80a3b1be --- /dev/null +++ b/src/nix/store.md @@ -0,0 +1,9 @@ +R"( + +# Store types + +Nix supports different types of stores. These are listed below. + +@stores@ + +)" -- cgit v1.2.3 From 7704118d2816c2f7939db6771cd4665d5a68596d Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 21 Mar 2023 12:59:37 +0100 Subject: nix describe-stores: Remove This command was intended for docs generation, but it was never used for that and we don't need it. --- src/nix/describe-stores.cc | 44 -------------------------------------------- 1 file changed, 44 deletions(-) delete mode 100644 src/nix/describe-stores.cc (limited to 'src') diff --git a/src/nix/describe-stores.cc b/src/nix/describe-stores.cc deleted file mode 100644 index ad6b2f0f8..000000000 --- a/src/nix/describe-stores.cc +++ /dev/null @@ -1,44 +0,0 @@ -#include "command.hh" -#include "common-args.hh" -#include "shared.hh" -#include "store-api.hh" - -#include - -using namespace nix; - -struct CmdDescribeStores : Command, MixJSON -{ - std::string description() override - { - return "show registered store types and their available options"; - } - - Category category() override { return catUtility; } - - void run() override - { - auto res = nlohmann::json::object(); - for (auto & implem : *Implementations::registered) { - auto storeConfig = implem.getConfig(); - auto storeName = storeConfig->name(); - res[storeName]["settings"] = storeConfig->toJSON(); - } - if (json) { - logger->cout("%s", res); - } else { - for (auto & [storeName, storeConfig] : res.items()) { - std::cout << "## " << storeName << std::endl << std::endl; - for (auto & [optionName, optionDesc] : storeConfig.items()) { - std::cout << "### " << optionName << std::endl << std::endl; - std::cout << optionDesc["description"].get() << std::endl; - std::cout << "default: " << optionDesc["defaultValue"] << std::endl <("describe-stores"); -- cgit v1.2.3 From 9eb53bbf17037691e1278c71517d74d0962aa43f Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 21 Mar 2023 14:03:05 +0100 Subject: Support per-store Markdown documentation --- src/libstore/dummy-store.cc | 7 ++++++ src/libstore/dummy-store.md | 13 +++++++++++ src/libstore/http-binary-cache-store.cc | 7 ++++++ src/libstore/http-binary-cache-store.md | 8 +++++++ src/libstore/legacy-ssh-store.cc | 9 ++++++- src/libstore/legacy-ssh-store.md | 8 +++++++ src/libstore/local-binary-cache-store.cc | 7 ++++++ src/libstore/local-binary-cache-store.md | 16 +++++++++++++ src/libstore/local-store.cc | 7 ++++++ src/libstore/local-store.hh | 3 ++- src/libstore/local-store.md | 40 ++++++++++++++++++++++++++++++++ src/libstore/s3-binary-cache-store.cc | 7 ++++++ src/libstore/s3-binary-cache-store.md | 8 +++++++ src/libstore/ssh-store.cc | 9 ++++++- src/libstore/ssh-store.md | 8 +++++++ src/libstore/store-api.hh | 5 ++++ src/libstore/uds-remote-store.hh | 7 ++++++ src/libstore/uds-remote-store.md | 9 +++++++ src/nix/main.cc | 1 + 19 files changed, 176 insertions(+), 3 deletions(-) create mode 100644 src/libstore/dummy-store.md create mode 100644 src/libstore/http-binary-cache-store.md create mode 100644 src/libstore/legacy-ssh-store.md create mode 100644 src/libstore/local-binary-cache-store.md create mode 100644 src/libstore/local-store.md create mode 100644 src/libstore/s3-binary-cache-store.md create mode 100644 src/libstore/ssh-store.md create mode 100644 src/libstore/uds-remote-store.md (limited to 'src') diff --git a/src/libstore/dummy-store.cc b/src/libstore/dummy-store.cc index b4fbe0b70..16e5fafd7 100644 --- a/src/libstore/dummy-store.cc +++ b/src/libstore/dummy-store.cc @@ -7,6 +7,13 @@ struct DummyStoreConfig : virtual StoreConfig { using StoreConfig::StoreConfig; const std::string name() override { return "Dummy Store"; } + + std::string doc() override + { + return + #include "dummy-store.md" + ; + } }; struct DummyStore : public virtual DummyStoreConfig, public virtual Store diff --git a/src/libstore/dummy-store.md b/src/libstore/dummy-store.md new file mode 100644 index 000000000..eb7b4ba0d --- /dev/null +++ b/src/libstore/dummy-store.md @@ -0,0 +1,13 @@ +R"( + +**Store URL format**: `dummy://` + +This store type represents a store that contains no store paths and +cannot be written to. It's useful when you want to use the Nix +evaluator when no actual Nix store exists, e.g. + +```console +# nix eval --store dummy:// --expr '1 + 2' +``` + +)" diff --git a/src/libstore/http-binary-cache-store.cc b/src/libstore/http-binary-cache-store.cc index 93f25363a..238fd1d98 100644 --- a/src/libstore/http-binary-cache-store.cc +++ b/src/libstore/http-binary-cache-store.cc @@ -13,6 +13,13 @@ struct HttpBinaryCacheStoreConfig : virtual BinaryCacheStoreConfig using BinaryCacheStoreConfig::BinaryCacheStoreConfig; const std::string name() override { return "HTTP Binary Cache Store"; } + + std::string doc() override + { + return + #include "http-binary-cache-store.md" + ; + } }; class HttpBinaryCacheStore : public virtual HttpBinaryCacheStoreConfig, public virtual BinaryCacheStore diff --git a/src/libstore/http-binary-cache-store.md b/src/libstore/http-binary-cache-store.md new file mode 100644 index 000000000..20c26d0c2 --- /dev/null +++ b/src/libstore/http-binary-cache-store.md @@ -0,0 +1,8 @@ +R"( + +**Store URL format**: `http://...`, `https://...` + +This store allows a binary cache to be accessed via the HTTP +protocol. + +)" diff --git a/src/libstore/legacy-ssh-store.cc b/src/libstore/legacy-ssh-store.cc index 2c9dd2680..7ce3dac6b 100644 --- a/src/libstore/legacy-ssh-store.cc +++ b/src/libstore/legacy-ssh-store.cc @@ -22,7 +22,14 @@ struct LegacySSHStoreConfig : virtual StoreConfig const Setting remoteProgram{(StoreConfig*) this, "nix-store", "remote-program", "path to the nix-store executable on the remote system"}; const Setting remoteStore{(StoreConfig*) this, "", "remote-store", "URI of the store on the remote system"}; - const std::string name() override { return "Legacy SSH Store"; } + const std::string name() override { return "SSH Store"; } + + std::string doc() override + { + return + #include "legacy-ssh-store.md" + ; + } }; struct LegacySSHStore : public virtual LegacySSHStoreConfig, public virtual Store diff --git a/src/libstore/legacy-ssh-store.md b/src/libstore/legacy-ssh-store.md new file mode 100644 index 000000000..043acebd6 --- /dev/null +++ b/src/libstore/legacy-ssh-store.md @@ -0,0 +1,8 @@ +R"( + +**Store URL format**: `ssh://[username@]hostname` + +This store type allows limited access to a remote store on another +machine via SSH. + +)" diff --git a/src/libstore/local-binary-cache-store.cc b/src/libstore/local-binary-cache-store.cc index f20b1fa02..e5ee6fc15 100644 --- a/src/libstore/local-binary-cache-store.cc +++ b/src/libstore/local-binary-cache-store.cc @@ -11,6 +11,13 @@ struct LocalBinaryCacheStoreConfig : virtual BinaryCacheStoreConfig using BinaryCacheStoreConfig::BinaryCacheStoreConfig; const std::string name() override { return "Local Binary Cache Store"; } + + std::string doc() override + { + return + #include "local-binary-cache-store.md" + ; + } }; class LocalBinaryCacheStore : public virtual LocalBinaryCacheStoreConfig, public virtual BinaryCacheStore diff --git a/src/libstore/local-binary-cache-store.md b/src/libstore/local-binary-cache-store.md new file mode 100644 index 000000000..93fddc840 --- /dev/null +++ b/src/libstore/local-binary-cache-store.md @@ -0,0 +1,16 @@ +R"( + +**Store URL format**: `file://`*path* + +This store allows reading and writing a binary cache stored in *path* +in the local filesystem. If *path* does not exist, it will be created. + +For example, the following builds or downloads `nixpkgs#hello` into +the local store and then copies it to the binary cache in +`/tmp/binary-cache`: + +``` +# nix copy --to file:///tmp/binary-cache nixpkgs#hello +``` + +)" diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index c9a466ee8..1eada7cc3 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -44,6 +44,13 @@ namespace nix { +std::string LocalStoreConfig::doc() +{ + return + #include "local-store.md" + ; +} + struct LocalStore::State::Stmts { /* Some precompiled SQLite statements. */ SQLiteStmt RegisterValidPath; diff --git a/src/libstore/local-store.hh b/src/libstore/local-store.hh index a84eb7c26..5e5ce86c8 100644 --- a/src/libstore/local-store.hh +++ b/src/libstore/local-store.hh @@ -41,8 +41,9 @@ struct LocalStoreConfig : virtual LocalFSStoreConfig "require-sigs", "whether store paths should have a trusted signature on import"}; const std::string name() override { return "Local Store"; } -}; + std::string doc() override; +}; class LocalStore : public virtual LocalStoreConfig, public virtual LocalFSStore, public virtual GcStore { diff --git a/src/libstore/local-store.md b/src/libstore/local-store.md new file mode 100644 index 000000000..893457d32 --- /dev/null +++ b/src/libstore/local-store.md @@ -0,0 +1,40 @@ +R"( + +**Store URL format**: `local`, *root* + +This store type accesses a Nix store in the local filesystem directly +(i.e. not via the Nix daemon). *root* is an absolute path that denotes +the "root" of the filesystem; other directories such as the Nix store +directory (as denoted by the `store` setting) are interpreted relative +to *root*. The store pseudo-URL `local` denotes a store that uses `/` +as its root directory. + +A store that uses a *root* other than `/` is called a *chroot +store*. With such stores, the store directory is "logically" still +`/nix/store`, so programs stored in them can only be built and +executed by `chroot`-ing into *root*. Chroot stores only support +building and running on Linux when mount and user namespaces are +enabled. + +For example, the following uses `/tmp/root` as the chroot environment +to build or download `nixpkgs#hello` and then execute it: + +```console +# nix run --store /tmp/root nixpkgs#hello +Hello, world! +``` + +Here, the "physical" store location is `/tmp/root/nix/store`, and +Nix's store metadata is in `/tmp/root/nix/var/nix/db`. + +It is also possible, but not recommended, to change the "logical" +location of the Nix store from its default of `/nix/store`. This makes +it impossible to use default substituters such as +`https://cache.nixos.org/`, and thus you may have to build everything +locally. Here is an example: + +```console +# nix build --store 'local?store=/tmp/my-nix/store&state=/tmp/my-nix/state&log=/tmp/my-nix/log' nixpkgs#hello +``` + +)" diff --git a/src/libstore/s3-binary-cache-store.cc b/src/libstore/s3-binary-cache-store.cc index 8006bd733..05842a2ea 100644 --- a/src/libstore/s3-binary-cache-store.cc +++ b/src/libstore/s3-binary-cache-store.cc @@ -205,6 +205,13 @@ struct S3BinaryCacheStoreConfig : virtual BinaryCacheStoreConfig (StoreConfig*) this, 5 * 1024 * 1024, "buffer-size", "size (in bytes) of each part in multi-part uploads"}; const std::string name() override { return "S3 Binary Cache Store"; } + + std::string doc() override + { + return + #include "s3-binary-cache-store.md" + ; + } }; struct S3BinaryCacheStoreImpl : virtual S3BinaryCacheStoreConfig, public virtual S3BinaryCacheStore diff --git a/src/libstore/s3-binary-cache-store.md b/src/libstore/s3-binary-cache-store.md new file mode 100644 index 000000000..70fe0eb09 --- /dev/null +++ b/src/libstore/s3-binary-cache-store.md @@ -0,0 +1,8 @@ +R"( + +**Store URL format**: `s3://`*bucket-name* + +This store allows reading and writing a binary cache stored in an AWS +S3 bucket. + +)" diff --git a/src/libstore/ssh-store.cc b/src/libstore/ssh-store.cc index cfa063803..6f3ee6262 100644 --- a/src/libstore/ssh-store.cc +++ b/src/libstore/ssh-store.cc @@ -18,7 +18,14 @@ struct SSHStoreConfig : virtual RemoteStoreConfig const Setting remoteProgram{(StoreConfig*) this, "nix-daemon", "remote-program", "path to the nix-daemon executable on the remote system"}; const Setting remoteStore{(StoreConfig*) this, "", "remote-store", "URI of the store on the remote system"}; - const std::string name() override { return "SSH Store"; } + const std::string name() override { return "Experimental SSH Store"; } + + std::string doc() override + { + return + #include "ssh-store.md" + ; + } }; class SSHStore : public virtual SSHStoreConfig, public virtual RemoteStore diff --git a/src/libstore/ssh-store.md b/src/libstore/ssh-store.md new file mode 100644 index 000000000..881537e71 --- /dev/null +++ b/src/libstore/ssh-store.md @@ -0,0 +1,8 @@ +R"( + +**Store URL format**: `ssh-ng://[username@]hostname` + +Experimental store type that allows full access to a Nix store on a +remote machine. + +)" diff --git a/src/libstore/store-api.hh b/src/libstore/store-api.hh index 4d8db3596..9afb33b9e 100644 --- a/src/libstore/store-api.hh +++ b/src/libstore/store-api.hh @@ -101,6 +101,11 @@ struct StoreConfig : public Config virtual const std::string name() = 0; + virtual std::string doc() + { + return ""; + } + const PathSetting storeDir_{this, false, settings.nixStore, "store", "path to the Nix store"}; const Path storeDir = storeDir_; diff --git a/src/libstore/uds-remote-store.hh b/src/libstore/uds-remote-store.hh index d31a4d592..caa452919 100644 --- a/src/libstore/uds-remote-store.hh +++ b/src/libstore/uds-remote-store.hh @@ -15,6 +15,13 @@ struct UDSRemoteStoreConfig : virtual LocalFSStoreConfig, virtual RemoteStoreCon } const std::string name() override { return "Local Daemon Store"; } + + std::string doc() override + { + return + #include "uds-remote-store.md" + ; + } }; class UDSRemoteStore : public virtual UDSRemoteStoreConfig, public virtual LocalFSStore, public virtual RemoteStore diff --git a/src/libstore/uds-remote-store.md b/src/libstore/uds-remote-store.md new file mode 100644 index 000000000..8df0bd6ff --- /dev/null +++ b/src/libstore/uds-remote-store.md @@ -0,0 +1,9 @@ +R"( + +**Store URL format**: `daemon`, `unix://`*path* + +This store type accesses a Nix store by talking to a Nix daemon +listening on the Unix domain socket *path*. The store pseudo-URL +`daemon` is equivalent to `unix:///nix/var/nix/daemon-socket/socket`. + +)" diff --git a/src/nix/main.cc b/src/nix/main.cc index 0ea6d7784..6caca9c22 100644 --- a/src/nix/main.cc +++ b/src/nix/main.cc @@ -175,6 +175,7 @@ struct NixArgs : virtual MultiCommand, virtual MixCommonArgs for (auto & implem : *Implementations::registered) { auto storeConfig = implem.getConfig(); auto storeName = storeConfig->name(); + stores[storeName]["doc"] = storeConfig->doc(); stores[storeName]["settings"] = storeConfig->toJSON(); } res["stores"] = std::move(stores); -- cgit v1.2.3 From 233b063b08b6a82921d26fb5a86e15c2b94a72ee Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 21 Mar 2023 14:37:09 +0100 Subject: Move store docs to 'nix help-stores' Why not 'nix help stores'? Well, 'nix help ' already means 'show help on the "arg" subcommand'. --- src/libstore/globals.hh | 5 ++++- src/nix/help-stores.md | 7 +++++++ src/nix/main.cc | 32 +++++++++++++++++++++++++++++++- src/nix/nix.md | 5 ++--- src/nix/store.cc | 7 ------- src/nix/store.md | 9 --------- 6 files changed, 44 insertions(+), 21 deletions(-) create mode 100644 src/nix/help-stores.md delete mode 100644 src/nix/store.md (limited to 'src') diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh index 93086eaf8..4382d1b85 100644 --- a/src/libstore/globals.hh +++ b/src/libstore/globals.hh @@ -97,7 +97,10 @@ public: Path nixDaemonSocketFile; Setting storeUri{this, getEnv("NIX_REMOTE").value_or("auto"), "store", - "The default Nix store to use."}; + R"( + The URL of the Nix store to use. See [`nix help-stores`](@docroot@/command-ref/new-cli/nix3-help-stores.md) + for supported store types and settings. + )"}; Setting keepFailed{this, false, "keep-failed", "Whether to keep temporary directories of failed builds."}; diff --git a/src/nix/help-stores.md b/src/nix/help-stores.md new file mode 100644 index 000000000..a33a7ea70 --- /dev/null +++ b/src/nix/help-stores.md @@ -0,0 +1,7 @@ +R"( + +Nix supports different types of stores. These are described below. + +@stores@ + +)" diff --git a/src/nix/main.cc b/src/nix/main.cc index 6caca9c22..5981028f7 100644 --- a/src/nix/main.cc +++ b/src/nix/main.cc @@ -223,6 +223,14 @@ static void showHelp(std::vector subcommand, NixArgs & toplevel) std::cout << renderMarkdownToTerminal(markdown) << "\n"; } +static NixArgs & getNixArgs(Command & cmd) +{ + assert(cmd.parent); + MultiCommand * toplevel = cmd.parent; + while (toplevel->parent) toplevel = toplevel->parent; + return dynamic_cast(*toplevel); +} + struct CmdHelp : Command { std::vector subcommand; @@ -252,12 +260,34 @@ struct CmdHelp : Command assert(parent); MultiCommand * toplevel = parent; while (toplevel->parent) toplevel = toplevel->parent; - showHelp(subcommand, dynamic_cast(*toplevel)); + showHelp(subcommand, getNixArgs(*this)); } }; static auto rCmdHelp = registerCommand("help"); +struct CmdHelpStores : Command +{ + std::string description() override + { + return "show help about store types and their settings"; + } + + std::string doc() override + { + return + #include "help-stores.md" + ; + } + + void run() override + { + showHelp({"help-stores"}, getNixArgs(*this)); + } +}; + +static auto rCmdHelpStores = registerCommand("help-stores"); + void mainWrapped(int argc, char * * argv) { savedArgv = argv; diff --git a/src/nix/nix.md b/src/nix/nix.md index 0a90fa6c9..e1865b31c 100644 --- a/src/nix/nix.md +++ b/src/nix/nix.md @@ -220,8 +220,7 @@ operate are determined as follows: # Nix stores -Most `nix` subcommands operate on a *Nix store*. - -TODO: list store types, options +Most `nix` subcommands operate on a *Nix store*. These are documented +in [`nix help-stores`](./nix3-help-stores.md). )"" diff --git a/src/nix/store.cc b/src/nix/store.cc index 72b037488..2879e03b3 100644 --- a/src/nix/store.cc +++ b/src/nix/store.cc @@ -12,13 +12,6 @@ struct CmdStore : virtual NixMultiCommand return "manipulate a Nix store"; } - std::string doc() override - { - return - #include "store.md" - ; - } - Category category() override { return catUtility; } void run() override diff --git a/src/nix/store.md b/src/nix/store.md deleted file mode 100644 index d80a3b1be..000000000 --- a/src/nix/store.md +++ /dev/null @@ -1,9 +0,0 @@ -R"( - -# Store types - -Nix supports different types of stores. These are listed below. - -@stores@ - -)" -- cgit v1.2.3 From c967c29290b1d0b7ea5a169c39324ddca46403ba Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 21 Mar 2023 14:43:58 +0100 Subject: Add a "help" category This makes the help commands show up prominently at the top of the 'nix' manpage. --- src/libcmd/command.hh | 1 + src/nix/main.cc | 5 +++++ 2 files changed, 6 insertions(+) (limited to 'src') diff --git a/src/libcmd/command.hh b/src/libcmd/command.hh index 874ca3249..b7a87dad0 100644 --- a/src/libcmd/command.hh +++ b/src/libcmd/command.hh @@ -18,6 +18,7 @@ class EvalState; struct Pos; class Store; +static constexpr Command::Category catHelp = -1; static constexpr Command::Category catSecondary = 100; static constexpr Command::Category catUtility = 101; static constexpr Command::Category catNixInstallation = 102; diff --git a/src/nix/main.cc b/src/nix/main.cc index 5981028f7..ffe558ee8 100644 --- a/src/nix/main.cc +++ b/src/nix/main.cc @@ -65,6 +65,7 @@ struct NixArgs : virtual MultiCommand, virtual MixCommonArgs NixArgs() : MultiCommand(RegisterCommand::getCommandsFor({})), MixCommonArgs("nix") { categories.clear(); + categories[catHelp] = "Help commands"; categories[Command::catDefault] = "Main commands"; categories[catSecondary] = "Infrequently used commands"; categories[catUtility] = "Utility/scripting commands"; @@ -255,6 +256,8 @@ struct CmdHelp : Command ; } + Category category() override { return catHelp; } + void run() override { assert(parent); @@ -280,6 +283,8 @@ struct CmdHelpStores : Command ; } + Category category() override { return catHelp; } + void run() override { showHelp({"help-stores"}, getNixArgs(*this)); -- cgit v1.2.3 From 16cb9b9f045d7fc70faf911bfd564e2c79c66685 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 21 Mar 2023 14:48:15 +0100 Subject: Fix rendering of fetchGit documentation stripIndentation() doesn't support tabs, so the entire markdown ended up indented and thus rendered as a code block. --- src/libexpr/primops/fetchTree.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/libexpr/primops/fetchTree.cc b/src/libexpr/primops/fetchTree.cc index fd51dfb90..0f54fedde 100644 --- a/src/libexpr/primops/fetchTree.cc +++ b/src/libexpr/primops/fetchTree.cc @@ -481,10 +481,10 @@ static RegisterPrimOp primop_fetchGit({ builtins.fetchGit ./work-dir ``` - If the URL points to a local directory, and no `ref` or `rev` is - given, `fetchGit` will use the current content of the checked-out - files, even if they are not committed or added to Git's index. It will - only consider files added to the Git repository, as listed by `git ls-files`. + If the URL points to a local directory, and no `ref` or `rev` is + given, `fetchGit` will use the current content of the checked-out + files, even if they are not committed or added to Git's index. It will + only consider files added to the Git repository, as listed by `git ls-files`. )", .fun = prim_fetchGit, }); -- cgit v1.2.3 From 5691bac2025db9dbf637f23d1d2dd502010df37b Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 22 Mar 2023 14:23:36 +0100 Subject: Improve store setting descriptions / Markdown formatting --- src/libstore/binary-cache-store.hh | 34 ++++++++++++++----- src/libstore/legacy-ssh-store.cc | 14 ++++---- src/libstore/local-fs-store.hh | 16 ++++++--- src/libstore/local-store.hh | 3 +- src/libstore/remote-store.hh | 10 +++--- src/libstore/s3-binary-cache-store.cc | 64 ++++++++++++++++++++++++++++++----- src/libstore/ssh-store-config.hh | 29 ++++++++++++++++ src/libstore/ssh-store.cc | 9 ++--- src/libstore/store-api.hh | 31 +++++++++++++---- src/nix/help-stores.md | 4 +++ 10 files changed, 165 insertions(+), 49 deletions(-) create mode 100644 src/libstore/ssh-store-config.hh (limited to 'src') diff --git a/src/libstore/binary-cache-store.hh b/src/libstore/binary-cache-store.hh index abd92a83c..c1d08926d 100644 --- a/src/libstore/binary-cache-store.hh +++ b/src/libstore/binary-cache-store.hh @@ -16,17 +16,33 @@ struct BinaryCacheStoreConfig : virtual StoreConfig { using StoreConfig::StoreConfig; - const Setting compression{(StoreConfig*) this, "xz", "compression", "NAR compression method ('xz', 'bzip2', 'gzip', 'zstd', or 'none')"}; - const Setting writeNARListing{(StoreConfig*) this, false, "write-nar-listing", "whether to write a JSON file listing the files in each NAR"}; - const Setting writeDebugInfo{(StoreConfig*) this, false, "index-debug-info", "whether to index DWARF debug info files by build ID"}; - const Setting secretKeyFile{(StoreConfig*) this, "", "secret-key", "path to secret key used to sign the binary cache"}; - const Setting localNarCache{(StoreConfig*) this, "", "local-nar-cache", "path to a local cache of NARs"}; + const Setting compression{(StoreConfig*) this, "xz", "compression", + "NAR compression method (`xz`, `bzip2`, `gzip`, `zstd`, or `none`)."}; + + const Setting writeNARListing{(StoreConfig*) this, false, "write-nar-listing", + "Whether to write a JSON file that lists the files in each NAR."}; + + const Setting writeDebugInfo{(StoreConfig*) this, false, "index-debug-info", + R"( + Whether to index DWARF debug info files by build ID. This allows [`dwarffs`](https://github.com/edolstra/dwarffs) to + fetch debug info on demand + )"}; + + const Setting secretKeyFile{(StoreConfig*) this, "", "secret-key", + "Path to the secret key used to sign the binary cache."}; + + const Setting localNarCache{(StoreConfig*) this, "", "local-nar-cache", + "Path to a local cache of NARs fetched from this binary cache, used by commands such as `nix store cat`."}; + const Setting parallelCompression{(StoreConfig*) this, false, "parallel-compression", - "enable multi-threading compression for NARs, available for xz and zstd only currently"}; + "Enable multi-threaded compression of NARs. This is currently only available for `xz` and `zstd`."}; + const Setting compressionLevel{(StoreConfig*) this, -1, "compression-level", - "specify 'preset level' of compression to be used with NARs: " - "meaning and accepted range of values depends on compression method selected, " - "other than -1 which we reserve to indicate Nix defaults should be used"}; + R"( + The *preset level* to be used when compressing NARs. + The meaning and accepted values depend on the compression method selected. + `-1` specifies that the default compression level should be used. + )"}; }; class BinaryCacheStore : public virtual BinaryCacheStoreConfig, diff --git a/src/libstore/legacy-ssh-store.cc b/src/libstore/legacy-ssh-store.cc index 7ce3dac6b..a8ce0b8b0 100644 --- a/src/libstore/legacy-ssh-store.cc +++ b/src/libstore/legacy-ssh-store.cc @@ -1,3 +1,4 @@ +#include "ssh-store-config.hh" #include "archive.hh" #include "pool.hh" #include "remote-store.hh" @@ -12,15 +13,12 @@ namespace nix { -struct LegacySSHStoreConfig : virtual StoreConfig +struct LegacySSHStoreConfig : virtual CommonSSHStoreConfig { - using StoreConfig::StoreConfig; - const Setting maxConnections{(StoreConfig*) this, 1, "max-connections", "maximum number of concurrent SSH connections"}; - const Setting sshKey{(StoreConfig*) this, "", "ssh-key", "path to an SSH private key"}; - const Setting sshPublicHostKey{(StoreConfig*) this, "", "base64-ssh-public-host-key", "The public half of the host's SSH key"}; - const Setting compress{(StoreConfig*) this, false, "compress", "whether to compress the connection"}; - const Setting remoteProgram{(StoreConfig*) this, "nix-store", "remote-program", "path to the nix-store executable on the remote system"}; - const Setting remoteStore{(StoreConfig*) this, "", "remote-store", "URI of the store on the remote system"}; + using CommonSSHStoreConfig::CommonSSHStoreConfig; + + const Setting maxConnections{(StoreConfig*) this, 1, "max-connections", + "Maximum number of concurrent SSH connections."}; const std::string name() override { return "SSH Store"; } diff --git a/src/libstore/local-fs-store.hh b/src/libstore/local-fs-store.hh index 947707341..796e72045 100644 --- a/src/libstore/local-fs-store.hh +++ b/src/libstore/local-fs-store.hh @@ -9,20 +9,28 @@ namespace nix { struct LocalFSStoreConfig : virtual StoreConfig { using StoreConfig::StoreConfig; + // FIXME: the (StoreConfig*) cast works around a bug in gcc that causes // it to omit the call to the Setting constructor. Clang works fine // either way. + const PathSetting rootDir{(StoreConfig*) this, true, "", - "root", "directory prefixed to all other paths"}; + "root", + "Directory prefixed to all other paths."}; + const PathSetting stateDir{(StoreConfig*) this, false, rootDir != "" ? rootDir + "/nix/var/nix" : settings.nixStateDir, - "state", "directory where Nix will store state"}; + "state", + "Directory where Nix will store state."}; + const PathSetting logDir{(StoreConfig*) this, false, rootDir != "" ? rootDir + "/nix/var/log/nix" : settings.nixLogDir, - "log", "directory where Nix will store state"}; + "log", + "directory where Nix will store log files."}; + const PathSetting realStoreDir{(StoreConfig*) this, false, rootDir != "" ? rootDir + "/nix/store" : storeDir, "real", - "physical path to the Nix store"}; + "Physical path of the Nix store."}; }; class LocalFSStore : public virtual LocalFSStoreConfig, diff --git a/src/libstore/local-store.hh b/src/libstore/local-store.hh index 5e5ce86c8..3743f6b10 100644 --- a/src/libstore/local-store.hh +++ b/src/libstore/local-store.hh @@ -38,7 +38,8 @@ struct LocalStoreConfig : virtual LocalFSStoreConfig Setting requireSigs{(StoreConfig*) this, settings.requireSigs, - "require-sigs", "whether store paths should have a trusted signature on import"}; + "require-sigs", + "Whether store paths copied into this store should have a trusted signature."}; const std::string name() override { return "Local Store"; } diff --git a/src/libstore/remote-store.hh b/src/libstore/remote-store.hh index 8cd7cc822..999151239 100644 --- a/src/libstore/remote-store.hh +++ b/src/libstore/remote-store.hh @@ -22,11 +22,13 @@ struct RemoteStoreConfig : virtual StoreConfig { using StoreConfig::StoreConfig; - const Setting maxConnections{(StoreConfig*) this, 1, - "max-connections", "maximum number of concurrent connections to the Nix daemon"}; + const Setting maxConnections{(StoreConfig*) this, 1, "max-connections", + "Maximum number of concurrent connections to the Nix daemon."}; - const Setting maxConnectionAge{(StoreConfig*) this, std::numeric_limits::max(), - "max-connection-age", "number of seconds to reuse a connection"}; + const Setting maxConnectionAge{(StoreConfig*) this, + std::numeric_limits::max(), + "max-connection-age", + "Maximum age of a connection before it is closed."}; }; /* FIXME: RemoteStore is a misnomer - should be something like diff --git a/src/libstore/s3-binary-cache-store.cc b/src/libstore/s3-binary-cache-store.cc index 05842a2ea..ac82147ee 100644 --- a/src/libstore/s3-binary-cache-store.cc +++ b/src/libstore/s3-binary-cache-store.cc @@ -192,17 +192,63 @@ S3BinaryCacheStore::S3BinaryCacheStore(const Params & params) struct S3BinaryCacheStoreConfig : virtual BinaryCacheStoreConfig { using BinaryCacheStoreConfig::BinaryCacheStoreConfig; - const Setting profile{(StoreConfig*) this, "", "profile", "The name of the AWS configuration profile to use."}; - const Setting region{(StoreConfig*) this, Aws::Region::US_EAST_1, "region", {"aws-region"}}; - const Setting scheme{(StoreConfig*) this, "", "scheme", "The scheme to use for S3 requests, https by default."}; - const Setting endpoint{(StoreConfig*) this, "", "endpoint", "An optional override of the endpoint to use when talking to S3."}; - const Setting narinfoCompression{(StoreConfig*) this, "", "narinfo-compression", "compression method for .narinfo files"}; - const Setting lsCompression{(StoreConfig*) this, "", "ls-compression", "compression method for .ls files"}; - const Setting logCompression{(StoreConfig*) this, "", "log-compression", "compression method for log/* files"}; + + const Setting profile{(StoreConfig*) this, "", "profile", + R"( + The name of the AWS configuration profile to use. By default + Nix will use the `default` profile. + )"}; + + const Setting region{(StoreConfig*) this, Aws::Region::US_EAST_1, "region", + R"( + The region of the S3 bucket. If your bucket is not in + `us–east-1`, you should always explicitly specify the region + parameter. + )"}; + + const Setting scheme{(StoreConfig*) this, "", "scheme", + R"( + The scheme used for S3 requests, `https` (default) or `http`. This + option allows you to disable HTTPS for binary caches which don't + support it. + + > **Note** + > + > HTTPS should be used if the cache might contain sensitive + > information. + )"}; + + const Setting endpoint{(StoreConfig*) this, "", "endpoint", + R"( + The URL of the endpoint of an S3-compatible service such as MinIO. + Do not specify this setting if you're using Amazon S3. + + > **Note** + > + > This endpoint must support HTTPS and will use path-based + > addressing instead of virtual host based addressing. + )"}; + + const Setting narinfoCompression{(StoreConfig*) this, "", "narinfo-compression", + "Compression method for `.narinfo` files."}; + + const Setting lsCompression{(StoreConfig*) this, "", "ls-compression", + "Compression method for `.ls` files."}; + + const Setting logCompression{(StoreConfig*) this, "", "log-compression", + R"( + Compression method for `log/*` files. It is recommended to + use a compression method supported by most web browsers + (e.g. `brotli`). + )"}; + const Setting multipartUpload{ - (StoreConfig*) this, false, "multipart-upload", "whether to use multi-part uploads"}; + (StoreConfig*) this, false, "multipart-upload", + "Whether to use multi-part uploads."}; + const Setting bufferSize{ - (StoreConfig*) this, 5 * 1024 * 1024, "buffer-size", "size (in bytes) of each part in multi-part uploads"}; + (StoreConfig*) this, 5 * 1024 * 1024, "buffer-size", + "Size (in bytes) of each part in multi-part uploads."}; const std::string name() override { return "S3 Binary Cache Store"; } diff --git a/src/libstore/ssh-store-config.hh b/src/libstore/ssh-store-config.hh new file mode 100644 index 000000000..767fc5e05 --- /dev/null +++ b/src/libstore/ssh-store-config.hh @@ -0,0 +1,29 @@ +#include "store-api.hh" + +namespace nix { + +struct CommonSSHStoreConfig : virtual StoreConfig +{ + using StoreConfig::StoreConfig; + + const Setting sshKey{(StoreConfig*) this, "", "ssh-key", + "Path to the SSH private key used to authenticate to the remote machine."}; + + const Setting sshPublicHostKey{(StoreConfig*) this, "", "base64-ssh-public-host-key", + "The public host key of the remote machine."}; + + const Setting compress{(StoreConfig*) this, false, "compress", + "Whether to enable SSH compression."}; + + const Setting remoteProgram{(StoreConfig*) this, "nix-store", "remote-program", + "Path to the `nix-store` executable on the remote machine."}; + + const Setting remoteStore{(StoreConfig*) this, "", "remote-store", + R"( + [Store URL](@docroot@/command-ref/new-cli/nix3-help-stores.md#store-url-format) + to be used on the remote machine. The default is `auto` + (i.e. use the Nix daemon or `/nix/store` directly). + )"}; +}; + +} diff --git a/src/libstore/ssh-store.cc b/src/libstore/ssh-store.cc index 6f3ee6262..3206bf169 100644 --- a/src/libstore/ssh-store.cc +++ b/src/libstore/ssh-store.cc @@ -1,3 +1,4 @@ +#include "ssh-store-config.hh" #include "store-api.hh" #include "remote-store.hh" #include "remote-fs-accessor.hh" @@ -8,16 +9,10 @@ namespace nix { -struct SSHStoreConfig : virtual RemoteStoreConfig +struct SSHStoreConfig : virtual RemoteStoreConfig, virtual CommonSSHStoreConfig { using RemoteStoreConfig::RemoteStoreConfig; - const Setting sshKey{(StoreConfig*) this, "", "ssh-key", "path to an SSH private key"}; - const Setting sshPublicHostKey{(StoreConfig*) this, "", "base64-ssh-public-host-key", "The public half of the host's SSH key"}; - const Setting compress{(StoreConfig*) this, false, "compress", "whether to compress the connection"}; - const Setting remoteProgram{(StoreConfig*) this, "nix-daemon", "remote-program", "path to the nix-daemon executable on the remote system"}; - const Setting remoteStore{(StoreConfig*) this, "", "remote-store", "URI of the store on the remote system"}; - const std::string name() override { return "Experimental SSH Store"; } std::string doc() override diff --git a/src/libstore/store-api.hh b/src/libstore/store-api.hh index 9afb33b9e..2f4391c43 100644 --- a/src/libstore/store-api.hh +++ b/src/libstore/store-api.hh @@ -107,16 +107,35 @@ struct StoreConfig : public Config } const PathSetting storeDir_{this, false, settings.nixStore, - "store", "path to the Nix store"}; + "store", + R"( + Logical location of the Nix store, usually + `/nix/store`. Note that you can only copy store paths + between stores if they have the same `store` setting. + )"}; const Path storeDir = storeDir_; - const Setting pathInfoCacheSize{this, 65536, "path-info-cache-size", "size of the in-memory store path information cache"}; + const Setting pathInfoCacheSize{this, 65536, "path-info-cache-size", + "Size of the in-memory store path metadata cache."}; - const Setting isTrusted{this, false, "trusted", "whether paths from this store can be used as substitutes even when they lack trusted signatures"}; + const Setting isTrusted{this, false, "trusted", + R"( + Whether paths from this store can be used as substitutes + even if they are not signed by a key listed in the + [`trusted-public-keys`](@docroot@/command-ref/conf-file.md#conf-trusted-public-keys) + setting. + )"}; - Setting priority{this, 0, "priority", "priority of this substituter (lower value means higher priority)"}; + Setting priority{this, 0, "priority", + R"( + Priority of this store when used as a substituter. A lower value means a higher priority. + )"}; - Setting wantMassQuery{this, false, "want-mass-query", "whether this substituter can be queried efficiently for path validity"}; + Setting wantMassQuery{this, false, "want-mass-query", + R"( + Whether this store (when used as a substituter) can be + queried efficiently for path validity. + )"}; Setting systemFeatures{this, getDefaultSystemFeatures(), "system-features", @@ -130,8 +149,6 @@ public: typedef std::map Params; - - protected: struct PathInfoCacheValue { diff --git a/src/nix/help-stores.md b/src/nix/help-stores.md index a33a7ea70..8f872cb79 100644 --- a/src/nix/help-stores.md +++ b/src/nix/help-stores.md @@ -2,6 +2,10 @@ R"( Nix supports different types of stores. These are described below. +## Store URL format + +TODO + @stores@ )" -- cgit v1.2.3 From b134546f08000ce80722fdc4de58ac5fd20865ed Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 23 Mar 2023 09:11:15 +0100 Subject: Fix clang build --- src/libstore/legacy-ssh-store.cc | 1 + src/libstore/ssh-store.cc | 2 ++ 2 files changed, 3 insertions(+) (limited to 'src') diff --git a/src/libstore/legacy-ssh-store.cc b/src/libstore/legacy-ssh-store.cc index a8ce0b8b0..72b8e89fc 100644 --- a/src/libstore/legacy-ssh-store.cc +++ b/src/libstore/legacy-ssh-store.cc @@ -56,6 +56,7 @@ struct LegacySSHStore : public virtual LegacySSHStoreConfig, public virtual Stor LegacySSHStore(const std::string & scheme, const std::string & host, const Params & params) : StoreConfig(params) + , CommonSSHStoreConfig(params) , LegacySSHStoreConfig(params) , Store(params) , host(host) diff --git a/src/libstore/ssh-store.cc b/src/libstore/ssh-store.cc index 3206bf169..be8a67c13 100644 --- a/src/libstore/ssh-store.cc +++ b/src/libstore/ssh-store.cc @@ -12,6 +12,7 @@ namespace nix { struct SSHStoreConfig : virtual RemoteStoreConfig, virtual CommonSSHStoreConfig { using RemoteStoreConfig::RemoteStoreConfig; + using CommonSSHStoreConfig::CommonSSHStoreConfig; const std::string name() override { return "Experimental SSH Store"; } @@ -30,6 +31,7 @@ public: SSHStore(const std::string & scheme, const std::string & host, const Params & params) : StoreConfig(params) , RemoteStoreConfig(params) + , CommonSSHStoreConfig(params) , SSHStoreConfig(params) , Store(params) , RemoteStore(params) -- cgit v1.2.3 From 80f0b8d3070f346bb89bd0ab1a541940a7ceeec1 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 23 Mar 2023 09:35:35 +0100 Subject: Fix SSHStore --- src/libstore/legacy-ssh-store.cc | 3 +++ src/libstore/ssh-store-config.hh | 3 --- src/libstore/ssh-store.cc | 3 +++ 3 files changed, 6 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/libstore/legacy-ssh-store.cc b/src/libstore/legacy-ssh-store.cc index 72b8e89fc..98322b045 100644 --- a/src/libstore/legacy-ssh-store.cc +++ b/src/libstore/legacy-ssh-store.cc @@ -17,6 +17,9 @@ struct LegacySSHStoreConfig : virtual CommonSSHStoreConfig { using CommonSSHStoreConfig::CommonSSHStoreConfig; + const Setting remoteProgram{(StoreConfig*) this, "nix-store", "remote-program", + "Path to the `nix-store` executable on the remote machine."}; + const Setting maxConnections{(StoreConfig*) this, 1, "max-connections", "Maximum number of concurrent SSH connections."}; diff --git a/src/libstore/ssh-store-config.hh b/src/libstore/ssh-store-config.hh index 767fc5e05..c4232df34 100644 --- a/src/libstore/ssh-store-config.hh +++ b/src/libstore/ssh-store-config.hh @@ -15,9 +15,6 @@ struct CommonSSHStoreConfig : virtual StoreConfig const Setting compress{(StoreConfig*) this, false, "compress", "Whether to enable SSH compression."}; - const Setting remoteProgram{(StoreConfig*) this, "nix-store", "remote-program", - "Path to the `nix-store` executable on the remote machine."}; - const Setting remoteStore{(StoreConfig*) this, "", "remote-store", R"( [Store URL](@docroot@/command-ref/new-cli/nix3-help-stores.md#store-url-format) diff --git a/src/libstore/ssh-store.cc b/src/libstore/ssh-store.cc index be8a67c13..962221ad2 100644 --- a/src/libstore/ssh-store.cc +++ b/src/libstore/ssh-store.cc @@ -14,6 +14,9 @@ struct SSHStoreConfig : virtual RemoteStoreConfig, virtual CommonSSHStoreConfig using RemoteStoreConfig::RemoteStoreConfig; using CommonSSHStoreConfig::CommonSSHStoreConfig; + const Setting remoteProgram{(StoreConfig*) this, "nix-daemon", "remote-program", + "Path to the `nix-daemon` executable on the remote machine."}; + const std::string name() override { return "Experimental SSH Store"; } std::string doc() override -- cgit v1.2.3 From abc449bc30bd0c8e2aaaa7840e83237729e9af19 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 23 Mar 2023 10:12:57 +0100 Subject: Update src/libstore/local-store.md Co-authored-by: Valentin Gagarin --- src/libstore/local-store.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/libstore/local-store.md b/src/libstore/local-store.md index 893457d32..580cf5358 100644 --- a/src/libstore/local-store.md +++ b/src/libstore/local-store.md @@ -13,7 +13,7 @@ A store that uses a *root* other than `/` is called a *chroot store*. With such stores, the store directory is "logically" still `/nix/store`, so programs stored in them can only be built and executed by `chroot`-ing into *root*. Chroot stores only support -building and running on Linux when mount and user namespaces are +building and running on Linux when [`mount namespaces`](https://man7.org/linux/man-pages/man7/mount_namespaces.7.html) and [`user namespaces`](https://man7.org/linux/man-pages/man7/user_namespaces.7.html) are enabled. For example, the following uses `/tmp/root` as the chroot environment -- cgit v1.2.3 From 82bd9535dd663209a38c1e6ebd8867875fa363ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophane=20Hufschmitt?= Date: Mon, 20 Mar 2023 10:24:48 +0100 Subject: nix-channel: Restore the old root channels directory --- src/nix-channel/nix-channel.cc | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nix-channel/nix-channel.cc b/src/nix-channel/nix-channel.cc index 338a7d18e..a79354404 100755 --- a/src/nix-channel/nix-channel.cc +++ b/src/nix-channel/nix-channel.cc @@ -168,7 +168,13 @@ static int main_nix_channel(int argc, char ** argv) nixDefExpr = settings.useXDGBaseDirectories ? createNixStateDir() + "/defexpr" : home + "/.nix-defexpr"; // Figure out the name of the channels profile. - profile = profilesDir() + "/channels"; + // For backwards-compatibility, install the root channels under + // a custom location, as these are also used as "global" channeles, and + // their location is hardcoded in a number of places. + profile = getuid() == 0 + ? settings.nixStateDir + "/profiles/per-user/root/channels" + : profilesDir() + "/channels"; + createDirs(dirOf(profile)); enum { cNone, -- cgit v1.2.3 From fb67c1a1fb5eba91abe3ab411a93cb49960454cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophane=20Hufschmitt?= Date: Tue, 21 Mar 2023 13:37:19 +0100 Subject: Factor out the generation of the profile/channel directory Make sure that all the code paths use the same one, and that the backwards-compatibility measures are probably in place when needed --- src/libexpr/eval.cc | 5 +++-- src/libstore/profiles.cc | 30 +++++++++++++++++++++++++----- src/libstore/profiles.hh | 9 +++++++++ src/nix-channel/nix-channel.cc | 7 +------ src/nix-env/nix-env.cc | 4 ++-- 5 files changed, 40 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 2721b6733..584bbc879 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -8,6 +8,7 @@ #include "eval-inline.hh" #include "filetransfer.hh" #include "function-trace.hh" +#include "profiles.hh" #include #include @@ -2491,8 +2492,8 @@ Strings EvalSettings::getDefaultNixPath() if (!evalSettings.restrictEval && !evalSettings.pureEval) { add(settings.useXDGBaseDirectories ? getStateDir() + "/nix/defexpr/channels" : getHome() + "/.nix-defexpr/channels"); - add(settings.nixStateDir + "/profiles/per-user/root/channels/nixpkgs", "nixpkgs"); - add(settings.nixStateDir + "/profiles/per-user/root/channels"); + add(rootChannelsDir() + "/nixpkgs", "nixpkgs"); + add(rootChannelsDir()); } return res; diff --git a/src/libstore/profiles.cc b/src/libstore/profiles.cc index 179161ff7..ba5c8583f 100644 --- a/src/libstore/profiles.cc +++ b/src/libstore/profiles.cc @@ -282,28 +282,48 @@ std::string optimisticLockProfile(const Path & profile) Path profilesDir() { - auto profileRoot = createNixStateDir() + "/profiles"; + auto profileRoot = + (getuid() == 0) + ? rootProfilesDir() + : createNixStateDir() + "/profiles"; createDirs(profileRoot); return profileRoot; } +Path rootProfilesDir() +{ + return settings.nixStateDir + "/profiles/per-user/root"; +} + Path getDefaultProfile() { Path profileLink = settings.useXDGBaseDirectories ? createNixStateDir() + "/profile" : getHome() + "/.nix-profile"; try { - auto profile = - getuid() == 0 - ? settings.nixStateDir + "/profiles/default" - : profilesDir() + "/profile"; + auto profile = profilesDir() + "/profile"; if (!pathExists(profileLink)) { replaceSymlink(profile, profileLink); } + // Backwards compatibiliy measure: Make root's profile available as + // `.../default` as it's what NixOS and most of the init scripts expect + Path globalProfileLink = settings.nixStateDir + "/profiles/default"; + if (getuid() == 0 && !pathExists(globalProfileLink)) { + replaceSymlink(profile, globalProfileLink); + } return absPath(readLink(profileLink), dirOf(profileLink)); } catch (Error &) { return profileLink; } } +Path defaultChannelsDir() +{ + return profilesDir() + "/channels"; +} + +Path rootChannelsDir() +{ + return rootProfilesDir() + "/channels"; +} } diff --git a/src/libstore/profiles.hh b/src/libstore/profiles.hh index fbf95b850..9d5d4779c 100644 --- a/src/libstore/profiles.hh +++ b/src/libstore/profiles.hh @@ -72,6 +72,15 @@ std::string optimisticLockProfile(const Path & profile); profiles. */ Path profilesDir(); +/* Returns the path to the profile directory for root (but doesn't try creating it) */ +Path rootProfilesDir(); + +/* Creates and returns the path to the file used for storing the users's channels */ +Path defaultChannelsDir(); + +/* Returns the path to the channel directory for root (but doesn't try creating it) */ +Path rootChannelsDir(); + /* Resolve the default profile (~/.nix-profile by default, $XDG_STATE_HOME/ nix/profile if XDG Base Directory Support is enabled), and create if doesn't exist */ diff --git a/src/nix-channel/nix-channel.cc b/src/nix-channel/nix-channel.cc index a79354404..740737ffe 100755 --- a/src/nix-channel/nix-channel.cc +++ b/src/nix-channel/nix-channel.cc @@ -168,12 +168,7 @@ static int main_nix_channel(int argc, char ** argv) nixDefExpr = settings.useXDGBaseDirectories ? createNixStateDir() + "/defexpr" : home + "/.nix-defexpr"; // Figure out the name of the channels profile. - // For backwards-compatibility, install the root channels under - // a custom location, as these are also used as "global" channeles, and - // their location is hardcoded in a number of places. - profile = getuid() == 0 - ? settings.nixStateDir + "/profiles/per-user/root/channels" - : profilesDir() + "/channels"; + profile = profilesDir() + "/channels"; createDirs(dirOf(profile)); enum { diff --git a/src/nix-env/nix-env.cc b/src/nix-env/nix-env.cc index 3a012638b..aa7ada37d 100644 --- a/src/nix-env/nix-env.cc +++ b/src/nix-env/nix-env.cc @@ -1403,11 +1403,11 @@ static int main_nix_env(int argc, char * * argv) try { createDirs(globals.instSource.nixExprPath); replaceSymlink( - fmt("%s/profiles/per-user/%s/channels", settings.nixStateDir, getUserName()), + defaultChannelsDir(), globals.instSource.nixExprPath + "/channels"); if (getuid() != 0) replaceSymlink( - fmt("%s/profiles/per-user/root/channels", settings.nixStateDir), + rootChannelsDir(), globals.instSource.nixExprPath + "/channels_root"); } catch (Error &) { } } -- cgit v1.2.3 From 161f4b0dea82bdaed7cf8928e523702f61cf899b Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 23 Mar 2023 10:38:48 +0100 Subject: Document store URLs --- src/libcmd/common-eval-args.cc | 6 +++++- src/libstore/globals.hh | 12 ++++++++---- src/nix/help-stores.md | 37 ++++++++++++++++++++++++++++++++++++- 3 files changed, 49 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/libcmd/common-eval-args.cc b/src/libcmd/common-eval-args.cc index 908127b4d..166b8aaa9 100644 --- a/src/libcmd/common-eval-args.cc +++ b/src/libcmd/common-eval-args.cc @@ -136,7 +136,11 @@ MixEvalArgs::MixEvalArgs() addFlag({ .longName = "eval-store", - .description = "The Nix store to use for evaluations.", + .description = + R"( + The [URL of the Nix store](@docroot@/command-ref/new-cli/nix3-help-stores.md#store-url-format) + to use for evaluation, i.e. to store derivations (`.drv` files) and inputs referenced by them. + )", .category = category, .labels = {"store-url"}, .handler = {&evalStoreUrl}, diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh index 4382d1b85..a74d5a46d 100644 --- a/src/libstore/globals.hh +++ b/src/libstore/globals.hh @@ -98,7 +98,9 @@ public: Setting storeUri{this, getEnv("NIX_REMOTE").value_or("auto"), "store", R"( - The URL of the Nix store to use. See [`nix help-stores`](@docroot@/command-ref/new-cli/nix3-help-stores.md) + The [URL of the Nix store](@docroot@/command-ref/new-cli/nix3-help-stores.md#store-url-format) + to use for most operations. + See [`nix help-stores`](@docroot@/command-ref/new-cli/nix3-help-stores.md) for supported store types and settings. )"}; @@ -681,8 +683,9 @@ public: Strings{"https://cache.nixos.org/"}, "substituters", R"( - A list of URLs of substituters, separated by whitespace. Substituters - are tried based on their Priority value, which each substituter can set + A list of [URLs of Nix stores](@docroot@/command-ref/new-cli/nix3-help-stores.md#store-url-format) + to be used as substituters, separated by whitespace. + Substituters are tried based on their Priority value, which each substituter can set independently. Lower value means higher priority. The default is `https://cache.nixos.org`, with a Priority of 40. @@ -700,7 +703,8 @@ public: Setting trustedSubstituters{ this, {}, "trusted-substituters", R"( - A list of URLs of substituters, separated by whitespace. These are + A list of [URLs of Nix stores](@docroot@/command-ref/new-cli/nix3-help-stores.md#store-url-format), + separated by whitespace. These are not used by default, but can be enabled by users of the Nix daemon by specifying `--option substituters urls` on the command line. Unprivileged users are only allowed to pass a subset of the diff --git a/src/nix/help-stores.md b/src/nix/help-stores.md index 8f872cb79..aa381393b 100644 --- a/src/nix/help-stores.md +++ b/src/nix/help-stores.md @@ -4,7 +4,42 @@ Nix supports different types of stores. These are described below. ## Store URL format -TODO +Stores are specified using a URL-like syntax. For example, the command + +```console +# nix path-info --store https://cache.nixos.org/ --json \ + /nix/store/a7gvj343m05j2s32xcnwr35v31ynlypr-coreutils-9.1 +``` + +fetches information about a store path in the HTTP binary cache +located at https://cache.nixos.org/, which is a type of store. + +Store URLs can specify **store settings** using URL query strings, +i.e. by appending `?name1=value1&name2=value2&...` to the URL. For +instance, + +``` +--store ssh://machine.example.org?ssh-key=/path/to/my/key +``` + +tells Nix to access the store on a remote machine via the SSH +protocol, using `/path/to/my/key` as the SSH private key. The +supported settings for each store type are documented below. + +The special store URL `auto` causes Nix to automatically select a +store as follows: + +* Use the local store `/nix/store` if `/nix/var/nix` is writable by + the current user. + +* Otherwise, if `/nix/var/nix/daemon-socket/socket` exists, [connect + to the Nix daemon listening on that socket](#local-daemon-store). + +* Otherwise, on Linux only, use the local chroot store + `~/.local/share/nix/root`, which will be created automatically if it + does not exist. + +* Otherwise, use the local store `/nix/store`. @stores@ -- cgit v1.2.3 From b79df9dedce957e083870d72e649262b5a8893a7 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 23 Mar 2023 15:23:13 +0100 Subject: Register LocalStore to ensure it's included in the manual --- src/libstore/local-store.cc | 8 ++++++++ src/libstore/local-store.hh | 4 ++++ src/libstore/uds-remote-store.cc | 6 +++--- 3 files changed, 15 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index 1eada7cc3..22a5dd8f5 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -420,6 +420,13 @@ LocalStore::LocalStore(const Params & params) } +LocalStore::LocalStore(std::string scheme, std::string path, const Params & params) + : LocalStore(params) +{ + throw UnimplementedError("LocalStore"); +} + + AutoCloseFD LocalStore::openGCLock() { Path fnGCLock = stateDir + "/gc.lock"; @@ -1957,5 +1964,6 @@ std::optional LocalStore::getVersion() return nixVersion; } +static RegisterStoreImplementation regLocalStore; } // namespace nix diff --git a/src/libstore/local-store.hh b/src/libstore/local-store.hh index 3743f6b10..639772b36 100644 --- a/src/libstore/local-store.hh +++ b/src/libstore/local-store.hh @@ -102,9 +102,13 @@ public: /* Initialise the local store, upgrading the schema if necessary. */ LocalStore(const Params & params); + LocalStore(std::string scheme, std::string path, const Params & params); ~LocalStore(); + static std::set uriSchemes() + { return {}; } + /* Implementations of abstract store API methods. */ std::string getUri() override; diff --git a/src/libstore/uds-remote-store.cc b/src/libstore/uds-remote-store.cc index 5c38323cd..0fb7c38e9 100644 --- a/src/libstore/uds-remote-store.cc +++ b/src/libstore/uds-remote-store.cc @@ -26,9 +26,9 @@ UDSRemoteStore::UDSRemoteStore(const Params & params) UDSRemoteStore::UDSRemoteStore( - const std::string scheme, - std::string socket_path, - const Params & params) + const std::string scheme, + std::string socket_path, + const Params & params) : UDSRemoteStore(params) { path.emplace(socket_path); -- cgit v1.2.3 From 168b6021c558f317fc01b142c1610742e9b9096b Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 23 Mar 2023 15:32:59 +0100 Subject: Tweaks --- src/libstore/local-store.md | 9 ++++----- src/nix/help-stores.md | 8 ++++---- 2 files changed, 8 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/libstore/local-store.md b/src/libstore/local-store.md index 580cf5358..8174df839 100644 --- a/src/libstore/local-store.md +++ b/src/libstore/local-store.md @@ -3,11 +3,10 @@ R"( **Store URL format**: `local`, *root* This store type accesses a Nix store in the local filesystem directly -(i.e. not via the Nix daemon). *root* is an absolute path that denotes -the "root" of the filesystem; other directories such as the Nix store -directory (as denoted by the `store` setting) are interpreted relative -to *root*. The store pseudo-URL `local` denotes a store that uses `/` -as its root directory. +(i.e. not via the Nix daemon). *root* is an absolute path that is +prefixed to other directories such as the Nix store directory. The +store pseudo-URL `local` denotes a store that uses `/` as its root +directory. A store that uses a *root* other than `/` is called a *chroot store*. With such stores, the store directory is "logically" still diff --git a/src/nix/help-stores.md b/src/nix/help-stores.md index aa381393b..47ba9b94d 100644 --- a/src/nix/help-stores.md +++ b/src/nix/help-stores.md @@ -29,17 +29,17 @@ supported settings for each store type are documented below. The special store URL `auto` causes Nix to automatically select a store as follows: -* Use the local store `/nix/store` if `/nix/var/nix` is writable by - the current user. +* Use the [local store](#local-store) `/nix/store` if `/nix/var/nix` + is writable by the current user. * Otherwise, if `/nix/var/nix/daemon-socket/socket` exists, [connect to the Nix daemon listening on that socket](#local-daemon-store). -* Otherwise, on Linux only, use the local chroot store +* Otherwise, on Linux only, use the [local chroot store](#local-store) `~/.local/share/nix/root`, which will be created automatically if it does not exist. -* Otherwise, use the local store `/nix/store`. +* Otherwise, use the [local store](#local-store) `/nix/store`. @stores@ -- cgit v1.2.3 From 233c4cf30ffa3615259348111f16e16132a50dad Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 21 Mar 2023 22:16:44 +0100 Subject: error.cc: Only suggest show-trace when truncated trace items would be printed Otherwise, a trace consisting of frame frame frame non-frame ... would reach the non-frame and print the suggestion, even though it would have ignored the non-frame anyway. This resulted in a peculariar situation where --show-trace would have no apparent effect, as the trace was actually already complete. --- src/libutil/error.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/libutil/error.cc b/src/libutil/error.cc index e4f0d4677..c9d61942a 100644 --- a/src/libutil/error.cc +++ b/src/libutil/error.cc @@ -302,14 +302,14 @@ std::ostream & showErrorInfo(std::ostream & out, const ErrorInfo & einfo, bool s if (!einfo.traces.empty()) { size_t count = 0; for (const auto & trace : einfo.traces) { + if (trace.hint.str().empty()) continue; + if (frameOnly && !trace.frame) continue; + if (!showTrace && count > 3) { oss << "\n" << ANSI_WARNING "(stack trace truncated; use '--show-trace' to show the full trace)" ANSI_NORMAL << "\n"; break; } - if (trace.hint.str().empty()) continue; - if (frameOnly && !trace.frame) continue; - count++; frameOnly = trace.frame; -- cgit v1.2.3 From 256f3e306369131cb13756ed94606d47c343103e Mon Sep 17 00:00:00 2001 From: John Ericson Date: Sun, 5 Feb 2023 23:28:18 -0500 Subject: Stratify `ExtraPathInfo` along `Installable` hierarchy Instead of having a bunch of optional fields, have a few subclasses which can have mandatory fields. Additionally, the new `getExtraPathInfo`, and `nixpkgsFlakeRef`, are moved to `InstallableValue`. I did these things because https://github.com/NixOS/rfcs/pull/134 ; with these things moved to `InstallableValue`, the base `Installable` no longer depends on libexpr! This is a major step towards that. Also, add a bunch of doc comments for sake of the internal API docs. --- src/libcmd/command.hh | 2 +- src/libcmd/installable-attr-path.cc | 4 ++ src/libcmd/installable-derived-path.cc | 5 +- src/libcmd/installable-flake.cc | 26 ++++--- src/libcmd/installable-flake.hh | 30 +++++++- src/libcmd/installable-value.hh | 71 +++++++++++++++++-- src/libcmd/installables.cc | 2 +- src/libcmd/installables.hh | 123 ++++++++++++++++++++++++--------- src/nix/build.cc | 1 - src/nix/bundle.cc | 1 + src/nix/develop.cc | 12 ++-- src/nix/profile.cc | 53 +++++++++----- 12 files changed, 254 insertions(+), 76 deletions(-) (limited to 'src') diff --git a/src/libcmd/command.hh b/src/libcmd/command.hh index 874ca3249..c39901618 100644 --- a/src/libcmd/command.hh +++ b/src/libcmd/command.hh @@ -1,6 +1,6 @@ #pragma once -#include "installables.hh" +#include "installable-value.hh" #include "args.hh" #include "common-eval-args.hh" #include "path.hh" diff --git a/src/libcmd/installable-attr-path.cc b/src/libcmd/installable-attr-path.cc index d9377f0d6..cf513126d 100644 --- a/src/libcmd/installable-attr-path.cc +++ b/src/libcmd/installable-attr-path.cc @@ -87,6 +87,10 @@ DerivedPathsWithInfo InstallableAttrPath::toDerivedPaths() .drvPath = drvPath, .outputs = outputs, }, + .info = make_ref(ExtraPathInfoValue::Value { + /* FIXME: reconsider backwards compatibility above + so we can fill in this info. */ + }), }); return res; diff --git a/src/libcmd/installable-derived-path.cc b/src/libcmd/installable-derived-path.cc index 729dc7d31..6ecf54b7c 100644 --- a/src/libcmd/installable-derived-path.cc +++ b/src/libcmd/installable-derived-path.cc @@ -10,7 +10,10 @@ std::string InstallableDerivedPath::what() const DerivedPathsWithInfo InstallableDerivedPath::toDerivedPaths() { - return {{.path = derivedPath, .info = {} }}; + return {{ + .path = derivedPath, + .info = make_ref(), + }}; } std::optional InstallableDerivedPath::getStorePath() diff --git a/src/libcmd/installable-flake.cc b/src/libcmd/installable-flake.cc index 7b0cc376d..a3352af76 100644 --- a/src/libcmd/installable-flake.cc +++ b/src/libcmd/installable-flake.cc @@ -101,7 +101,8 @@ DerivedPathsWithInfo InstallableFlake::toDerivedPaths() return {{ .path = DerivedPath::Opaque { .path = std::move(storePath), - } + }, + .info = make_ref(), }}; } @@ -113,7 +114,8 @@ DerivedPathsWithInfo InstallableFlake::toDerivedPaths() return {{ .path = DerivedPath::Opaque { .path = std::move(*storePath), - } + }, + .info = make_ref(), }}; } else throw Error("flake output attribute '%s' evaluates to the string '%s' which is not a store path", attrPath, s); @@ -160,13 +162,16 @@ DerivedPathsWithInfo InstallableFlake::toDerivedPaths() }, }, extendedOutputsSpec.raw()), }, - .info = { - .priority = priority, - .originalRef = flakeRef, - .resolvedRef = getLockedFlake()->flake.lockedRef, - .attrPath = attrPath, - .extendedOutputsSpec = extendedOutputsSpec, - } + .info = make_ref( + ExtraPathInfoValue::Value { + .priority = priority, + .attrPath = attrPath, + .extendedOutputsSpec = extendedOutputsSpec, + }, + ExtraPathInfoFlake::Flake { + .originalRef = flakeRef, + .resolvedRef = getLockedFlake()->flake.lockedRef, + }), }}; } @@ -212,6 +217,7 @@ std::shared_ptr InstallableFlake::getLockedFlake() const { if (!_lockedFlake) { flake::LockFlags lockFlagsApplyConfig = lockFlags; + // FIXME why this side effect? lockFlagsApplyConfig.applyNixConfig = true; _lockedFlake = std::make_shared(lockFlake(*state, flakeRef, lockFlagsApplyConfig)); } @@ -229,7 +235,7 @@ FlakeRef InstallableFlake::nixpkgsFlakeRef() const } } - return Installable::nixpkgsFlakeRef(); + return InstallableValue::nixpkgsFlakeRef(); } } diff --git a/src/libcmd/installable-flake.hh b/src/libcmd/installable-flake.hh index c75765086..313d2d7a3 100644 --- a/src/libcmd/installable-flake.hh +++ b/src/libcmd/installable-flake.hh @@ -4,6 +4,30 @@ namespace nix { +/** + * Extra info about a \ref DerivedPath "derived path" that ultimately + * come from a Flake. + * + * Invariant: every ExtraPathInfo gotten from an InstallableFlake should + * be possible to downcast to an ExtraPathInfoFlake. + */ +struct ExtraPathInfoFlake : ExtraPathInfoValue +{ + /** + * Extra struct to get around C++ designated initializer limitations + */ + struct Flake { + FlakeRef originalRef; + FlakeRef resolvedRef; + }; + + Flake flake; + + ExtraPathInfoFlake(Value && v, Flake && f) + : ExtraPathInfoValue(std::move(v)), flake(f) + { } +}; + struct InstallableFlake : InstallableValue { FlakeRef flakeRef; @@ -33,8 +57,10 @@ struct InstallableFlake : InstallableValue std::pair toValue(EvalState & state) override; - /* Get a cursor to every attrpath in getActualAttrPaths() - that exists. However if none exists, throw an exception. */ + /** + * Get a cursor to every attrpath in getActualAttrPaths() that + * exists. However if none exists, throw an exception. + */ std::vector> getCursors(EvalState & state) override; diff --git a/src/libcmd/installable-value.hh b/src/libcmd/installable-value.hh index 682c8d942..9e076cb10 100644 --- a/src/libcmd/installable-value.hh +++ b/src/libcmd/installable-value.hh @@ -1,9 +1,15 @@ #pragma once #include "installables.hh" +#include "flake/flake.hh" namespace nix { +struct DrvInfo; +struct SourceExprCommand; + +namespace eval_cache { class EvalCache; class AttrCursor; } + struct App { std::vector context; @@ -17,26 +23,83 @@ struct UnresolvedApp App resolve(ref evalStore, ref store); }; +/** + * Extra info about a \ref DerivedPath "derived path" that ultimately + * come from a Nix language value. + * + * Invariant: every ExtraPathInfo gotten from an InstallableValue should + * be possible to downcast to an ExtraPathInfoValue. + */ +struct ExtraPathInfoValue : ExtraPathInfo +{ + /** + * Extra struct to get around C++ designated initializer limitations + */ + struct Value { + /** + * An optional priority for use with "build envs". See Package + */ + std::optional priority; + + /** + * The attribute path associated with this value. The idea is + * that an installable referring to a value typically refers to + * a larger value, from which we project a smaller value out + * with this. + */ + std::string attrPath; + + /** + * \todo merge with DerivedPath's 'outputs' field? + */ + ExtendedOutputsSpec extendedOutputsSpec; + }; + + Value value; + + ExtraPathInfoValue(Value && v) + : value(v) + { } + + virtual ~ExtraPathInfoValue() = default; +}; + +/** + * An Installable which corresponds a Nix langauge value, in addition to + * a collection of \ref DerivedPath "derived paths". + */ struct InstallableValue : Installable { ref state; InstallableValue(ref state) : state(state) {} + virtual ~InstallableValue() { } + virtual std::pair toValue(EvalState & state) = 0; - /* Get a cursor to each value this Installable could refer to. However - if none exists, throw exception instead of returning empty vector. */ + /** + * 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> getCursors(EvalState & state); - /* Get the first and most preferred cursor this Installable could refer - to, or throw an exception if none exists. */ + /** + * Get the first and most preferred cursor this Installable could + * refer to, or throw an exception if none exists. + */ virtual ref getCursor(EvalState & state); UnresolvedApp toApp(EvalState & state); + virtual FlakeRef nixpkgsFlakeRef() const + { + return FlakeRef::fromAttrs({{"type","indirect"}, {"id", "nixpkgs"}}); + } + static InstallableValue & require(Installable & installable); static ref require(ref installable); }; diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc index e2164ec72..d7120df19 100644 --- a/src/libcmd/installables.cc +++ b/src/libcmd/installables.cc @@ -517,7 +517,7 @@ std::vector, BuiltPathWithResult>> Installable::build struct Aux { - ExtraPathInfo info; + ref info; ref installable; }; diff --git a/src/libcmd/installables.hh b/src/libcmd/installables.hh index c5f3cd683..b6efc0f17 100644 --- a/src/libcmd/installables.hh +++ b/src/libcmd/installables.hh @@ -4,9 +4,7 @@ #include "path.hh" #include "outputs-spec.hh" #include "derived-path.hh" -#include "eval.hh" #include "store-api.hh" -#include "flake/flake.hh" #include "build-result.hh" #include @@ -14,83 +12,144 @@ namespace nix { struct DrvInfo; -struct SourceExprCommand; - -namespace eval_cache { class EvalCache; class AttrCursor; } enum class Realise { - /* Build the derivation. Postcondition: the - derivation outputs exist. */ + /** + * Build the derivation. + * + * Postcondition: the derivation outputs exist. + */ Outputs, - /* Don't build the derivation. Postcondition: the store derivation - exists. */ + /** + * Don't build the derivation. + * + * Postcondition: the store derivation exists. + */ Derivation, - /* Evaluate in dry-run mode. Postcondition: nothing. */ - // FIXME: currently unused, but could be revived if we can - // evaluate derivations in-memory. + /** + * Evaluate in dry-run mode. + * + * Postcondition: nothing. + * + * \todo currently unused, but could be revived if we can evaluate + * derivations in-memory. + */ Nothing }; -/* How to handle derivations in commands that operate on store paths. */ +/** + * How to handle derivations in commands that operate on store paths. + */ enum class OperateOn { - /* Operate on the output path. */ + /** + * Operate on the output path. + */ Output, - /* Operate on the .drv path. */ + /** + * Operate on the .drv path. + */ Derivation }; +/** + * Extra info about a DerivedPath + * + * Yes, this is empty, but that is intended. It will be sub-classed by + * the subclasses of Installable to allow those to provide more info. + * Certain commands will make use of this info. + */ struct ExtraPathInfo { - std::optional priority; - std::optional originalRef; - std::optional resolvedRef; - std::optional attrPath; - // FIXME: merge with DerivedPath's 'outputs' field? - std::optional extendedOutputsSpec; + virtual ~ExtraPathInfo() = default; }; -/* A derived path with any additional info that commands might - need from the derivation. */ +/** + * A DerivedPath with \ref ExtraPathInfo "any additional info" that + * commands might need from the derivation. + */ struct DerivedPathWithInfo { DerivedPath path; - ExtraPathInfo info; + ref info; }; +/** + * Like DerivedPathWithInfo but extending BuiltPath with \ref + * ExtraPathInfo "extra info" and also possibly the \ref BuildResult + * "result of building". + */ struct BuiltPathWithResult { BuiltPath path; - ExtraPathInfo info; + ref info; std::optional result; }; +/** + * Shorthand, for less typing and helping us keep the choice of + * collection in sync. + */ typedef std::vector DerivedPathsWithInfo; struct Installable; + +/** + * Shorthand, for less typing and helping us keep the choice of + * collection in sync. + */ typedef std::vector> Installables; +/** + * Installables are the main positional arguments for the Nix + * Command-line. + * + * This base class is very flexible, and just assumes and the + * Installable refers to a collection of \ref DerivedPath "derived paths" with + * \ref ExtraPathInfo "extra info". + */ struct Installable { virtual ~Installable() { } + /** + * What Installable is this? + * + * Prints back valid CLI syntax that would result in this same + * installable. It doesn't need to be exactly what the user wrote, + * just something that means the same thing. + */ virtual std::string what() const = 0; + /** + * Get the collection of \ref DerivedPathWithInfo "derived paths + * with info" that this \ref Installable instalallable denotes. + * + * This is the main method of this class + */ virtual DerivedPathsWithInfo toDerivedPaths() = 0; + /** + * A convenience wrapper of the above for when we expect an + * installable to produce a single \ref DerivedPath "derived path" + * only. + * + * If no or multiple \ref DerivedPath "derived paths" are produced, + * and error is raised. + */ DerivedPathWithInfo toDerivedPath(); - /* Return a value only if this installable is a store path or a - symlink to it. */ + /** + * Return a value only if this installable is a store path or a + * symlink to it. + * + * \todo should we move this to InstallableDerivedPath? It is only + * supposed to work there anyways. Can always downcast. + */ virtual std::optional getStorePath() { return {}; } - virtual FlakeRef nixpkgsFlakeRef() const - { - return FlakeRef::fromAttrs({{"type","indirect"}, {"id", "nixpkgs"}}); - } - static std::vector build( ref evalStore, ref store, diff --git a/src/nix/build.cc b/src/nix/build.cc index bca20e97c..4e133e288 100644 --- a/src/nix/build.cc +++ b/src/nix/build.cc @@ -1,4 +1,3 @@ -#include "eval.hh" #include "command.hh" #include "common-args.hh" #include "shared.hh" diff --git a/src/nix/bundle.cc b/src/nix/bundle.cc index 7c32a360e..57c355f0c 100644 --- a/src/nix/bundle.cc +++ b/src/nix/bundle.cc @@ -5,6 +5,7 @@ #include "store-api.hh" #include "local-fs-store.hh" #include "fs-accessor.hh" +#include "eval-inline.hh" using namespace nix; diff --git a/src/nix/develop.cc b/src/nix/develop.cc index f06ade008..d8de20586 100644 --- a/src/nix/develop.cc +++ b/src/nix/develop.cc @@ -1,6 +1,6 @@ #include "eval.hh" -#include "command.hh" #include "installable-flake.hh" +#include "command-installable-value.hh" #include "common-args.hh" #include "shared.hh" #include "store-api.hh" @@ -252,7 +252,7 @@ static StorePath getDerivationEnvironment(ref store, ref evalStore throw Error("get-env.sh failed to produce an environment"); } -struct Common : InstallableCommand, MixProfile +struct Common : InstallableValueCommand, MixProfile { std::set ignoreVars{ "BASHOPTS", @@ -374,7 +374,7 @@ struct Common : InstallableCommand, MixProfile return res; } - StorePath getShellOutPath(ref store, ref installable) + StorePath getShellOutPath(ref store, ref installable) { auto path = installable->getStorePath(); if (path && hasSuffix(path->to_string(), "-env")) @@ -393,7 +393,7 @@ struct Common : InstallableCommand, MixProfile } std::pair - getBuildEnvironment(ref store, ref installable) + getBuildEnvironment(ref store, ref installable) { auto shellOutPath = getShellOutPath(store, installable); @@ -481,7 +481,7 @@ struct CmdDevelop : Common, MixEnvironment ; } - void run(ref store, ref installable) override + void run(ref store, ref installable) override { auto [buildEnvironment, gcroot] = getBuildEnvironment(store, installable); @@ -605,7 +605,7 @@ struct CmdPrintDevEnv : Common, MixJSON Category category() override { return catUtility; } - void run(ref store, ref installable) override + void run(ref store, ref installable) override { auto buildEnvironment = getBuildEnvironment(store, installable).first; diff --git a/src/nix/profile.cc b/src/nix/profile.cc index d72dd1a13..88d4a3ce5 100644 --- a/src/nix/profile.cc +++ b/src/nix/profile.cc @@ -254,13 +254,19 @@ struct ProfileManifest } }; -static std::map> +static std::map>> builtPathsPerInstallable( const std::vector, BuiltPathWithResult>> & builtPaths) { - std::map> res; + std::map>> res; for (auto & [installable, builtPath] : builtPaths) { - auto & r = res[&*installable]; + auto & r = res.insert({ + &*installable, + { + {}, + make_ref(), + } + }).first->second; /* Note that there could be conflicting info (e.g. meta.priority fields) if the installable returned multiple derivations. So pick one arbitrarily. FIXME: @@ -307,14 +313,16 @@ struct CmdProfileInstall : InstallablesCommand, MixDefaultProfile for (auto & installable : installables) { ProfileElement element; - auto & [res, info] = builtPaths[&*installable]; + auto iter = builtPaths.find(&*installable); + if (iter == builtPaths.end()) continue; + auto & [res, info] = iter->second; - if (info.originalRef && info.resolvedRef && info.attrPath && info.extendedOutputsSpec) { + if (auto * info2 = dynamic_cast(&*info)) { element.source = ProfileElementSource { - .originalRef = *info.originalRef, - .resolvedRef = *info.resolvedRef, - .attrPath = *info.attrPath, - .outputs = *info.extendedOutputsSpec, + .originalRef = info2->flake.originalRef, + .resolvedRef = info2->flake.resolvedRef, + .attrPath = info2->value.attrPath, + .outputs = info2->value.extendedOutputsSpec, }; } @@ -323,7 +331,12 @@ struct CmdProfileInstall : InstallablesCommand, MixDefaultProfile element.priority = priority ? *priority - : info.priority.value_or(defaultPriority); + : ({ + auto * info2 = dynamic_cast(&*info); + info2 + ? info2->value.priority.value_or(defaultPriority) + : defaultPriority; + }); element.updateStorePaths(getEvalStore(), store, res); @@ -541,19 +554,20 @@ struct CmdProfileUpgrade : virtual SourceExprCommand, MixDefaultProfile, MixProf auto derivedPaths = installable->toDerivedPaths(); if (derivedPaths.empty()) continue; - auto & info = derivedPaths[0].info; - - assert(info.resolvedRef && info.attrPath); + auto * infop = dynamic_cast(&*derivedPaths[0].info); + // `InstallableFlake` should use `ExtraPathInfoFlake`. + assert(infop); + auto & info = *infop; - if (element.source->resolvedRef == info.resolvedRef) continue; + if (element.source->resolvedRef == info.flake.resolvedRef) continue; printInfo("upgrading '%s' from flake '%s' to '%s'", - element.source->attrPath, element.source->resolvedRef, *info.resolvedRef); + element.source->attrPath, element.source->resolvedRef, info.flake.resolvedRef); element.source = ProfileElementSource { .originalRef = installable->flakeRef, - .resolvedRef = *info.resolvedRef, - .attrPath = *info.attrPath, + .resolvedRef = info.flake.resolvedRef, + .attrPath = info.value.attrPath, .outputs = installable->extendedOutputsSpec, }; @@ -582,7 +596,10 @@ struct CmdProfileUpgrade : virtual SourceExprCommand, MixDefaultProfile, MixProf for (size_t i = 0; i < installables.size(); ++i) { auto & installable = installables.at(i); auto & element = manifest.elements[indices.at(i)]; - element.updateStorePaths(getEvalStore(), store, builtPaths[&*installable].first); + element.updateStorePaths( + getEvalStore(), + store, + builtPaths.find(&*installable)->second.first); } updateProfile(manifest.build(store)); -- cgit v1.2.3 From e7b9dc7c8999fd93f168f3e213fb546a5ed7ddd9 Mon Sep 17 00:00:00 2001 From: Claudio Bley Date: Fri, 24 Mar 2023 17:25:17 +0100 Subject: nix-store: Use `long` for `narSize` in graphml output --- src/nix-store/graphml.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/nix-store/graphml.cc b/src/nix-store/graphml.cc index 425d61e53..439557658 100644 --- a/src/nix-store/graphml.cc +++ b/src/nix-store/graphml.cc @@ -57,7 +57,7 @@ void printGraphML(ref store, StorePathSet && roots) << "\n" - << "" + << "" << "" << "" << "\n"; -- cgit v1.2.3 From 2941a599fa6c9fec5d084cec2cb5da3752efc5f8 Mon Sep 17 00:00:00 2001 From: oxalica Date: Sun, 26 Mar 2023 22:39:24 +0800 Subject: Catch eval errors in `hasContent` `legacyPackages` of nixpkgs trigger eval errors in `hasContent`, causing the whole `legacyPackages` being skipped. We should treat it as has-content in that case. --- src/nix/flake.cc | 55 +++++++++++++++++++++++++++++++------------------------ 1 file changed, 31 insertions(+), 24 deletions(-) (limited to 'src') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 0a6616396..df19e87e4 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -1026,36 +1026,43 @@ struct CmdFlakeShow : FlakeCommand, MixJSON 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; + try { + 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; } - 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; + 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; } - return false; - } - // If we don't recognize it, it's probably content - return true; + // If we don't recognize it, it's probably content + return true; + } catch (EvalError & e) { + // Some attrs may contain errors, eg. legacyPackages of + // nixpkgs. We still want to recurse into it, instead of + // skipping it at all. + return true; + } }; std::function Date: Mon, 27 Mar 2023 10:02:10 +0200 Subject: Make some comments in profiles.hh doxygen-enabled These are proper documentation of the API, so they deserve to be here --- src/libstore/profiles.hh | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/libstore/profiles.hh b/src/libstore/profiles.hh index 9d5d4779c..3cadd5c2a 100644 --- a/src/libstore/profiles.hh +++ b/src/libstore/profiles.hh @@ -68,22 +68,32 @@ void lockProfile(PathLocks & lock, const Path & profile); rebuilt. */ std::string optimisticLockProfile(const Path & profile); -/* Creates and returns the path to a directory suitable for storing the user’s - profiles. */ +/** + * Create and return the path to a directory suitable for storing the user’s + * profiles. + */ Path profilesDir(); -/* Returns the path to the profile directory for root (but doesn't try creating it) */ +/** + * Return the path to the profile directory for root (but don't try creating it) + */ Path rootProfilesDir(); -/* Creates and returns the path to the file used for storing the users's channels */ +/** + * Create and return the path to the file used for storing the users's channels + */ Path defaultChannelsDir(); -/* Returns the path to the channel directory for root (but doesn't try creating it) */ +/** + * Return the path to the channel directory for root (but don't try creating it) + */ Path rootChannelsDir(); -/* Resolve the default profile (~/.nix-profile by default, $XDG_STATE_HOME/ - nix/profile if XDG Base Directory Support is enabled), and create if doesn't - exist */ +/** + * Resolve the default profile (~/.nix-profile by default, + * $XDG_STATE_HOME/nix/profile if XDG Base Directory Support is enabled), + * and create if doesn't exist + */ Path getDefaultProfile(); } -- cgit v1.2.3 From a519436e9d78fa6500999485c2c638553a32696f Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 27 Mar 2023 14:08:39 +0200 Subject: ssl-cert-file: Use lists --- src/libstore/globals.hh | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh index 95f6b5e35..6803fb614 100644 --- a/src/libstore/globals.hh +++ b/src/libstore/globals.hh @@ -832,12 +832,17 @@ public: this, getDefaultSSLCertFile(), "ssl-cert-file", R"( The path of a file containing CA certificates used to - authenticate `https://` downloads. It defaults to the first - of `/etc/ssl/certs/ca-certificates.crt` and - `/nix/var/nix/profiles/default/etc/ssl/certs/ca-bundle.crt` - that exists. It can be overriden using the - `NIX_SSL_CERT_FILE` and `SSL_CERT_FILE` environment variable - (in that order of precedence). + authenticate `https://` downloads. Nix by default will use + the first of the following files that exists: + + 1. `/etc/ssl/certs/ca-certificates.crt` + 2. `/nix/var/nix/profiles/default/etc/ssl/certs/ca-bundle.crt` + + The path can be overridden by the following environment + variables, in order of precedence: + + 1. `NIX_SSL_CERT_FILE` + 2. `SSL_CERT_FILE` )"}; #if __linux__ -- cgit v1.2.3 From 8df6f7bb0ff98f206e46c2aa7073bfcb02ee750a Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 27 Mar 2023 12:05:43 -0400 Subject: Fix more profile dirs in docs Picking up where #8078 left off. --- src/nix/upgrade-nix.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/nix/upgrade-nix.md b/src/nix/upgrade-nix.md index 084c80ba2..08757aebd 100644 --- a/src/nix/upgrade-nix.md +++ b/src/nix/upgrade-nix.md @@ -11,7 +11,7 @@ R""( * Upgrade Nix in a specific profile: ```console - # nix upgrade-nix -p /nix/var/nix/profiles/per-user/alice/profile + # nix upgrade-nix -p ~alice/.local/state/nix/profiles/profile ``` # Description -- cgit v1.2.3 From 605c8f7789d9ef446c8f22d8c6a261b315be861d Mon Sep 17 00:00:00 2001 From: Joachim Breitner Date: Tue, 28 Mar 2023 09:35:49 +0200 Subject: Docs: Explain why `import nixpkgs` works in flakes --- src/libexpr/primops.cc | 11 ++++++++--- src/nix/flake.md | 2 ++ 2 files changed, 10 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 3641ee468..73ffe03d4 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -254,9 +254,14 @@ static RegisterPrimOp primop_import({ .args = {"path"}, // TODO turn "normal path values" into link below .doc = R"( - Load, parse and return the Nix expression in the file *path*. If - *path* is a directory, the file ` default.nix ` in that directory - is loaded. Evaluation aborts if the file doesn’t exist or contains + Load, parse and return the Nix expression in the file *path*. + + The value *path* is conveted to a string as described in `builtins.toString`. + + If *path* is a directory, the file ` default.nix ` in that directory + is loaded. + + Evaluation aborts if the file doesn’t exist or contains an incorrect Nix expression. `import` implements Nix’s module system: you can put any Nix expression (such as a set or a function) in a separate file, and use it from Nix expressions in diff --git a/src/nix/flake.md b/src/nix/flake.md index 8eaa41b96..cd9f656e3 100644 --- a/src/nix/flake.md +++ b/src/nix/flake.md @@ -317,6 +317,8 @@ The following attributes are supported in `flake.nix`: also contains some metadata about the inputs. These are: * `outPath`: The path in the Nix store of the flake's source tree. + This way, the attribute set can be passed to `import` as if it was a path, + as in the example above (`import nixpkgs`). * `rev`: The commit hash of the flake's repository, if applicable. -- cgit v1.2.3 From 3ce6d72b2b5dd69a274518f2927d4dde0aa91247 Mon Sep 17 00:00:00 2001 From: Joachim Breitner Date: Tue, 28 Mar 2023 14:38:39 +0200 Subject: Update src/libexpr/primops.cc Co-authored-by: asymmetric --- src/libexpr/primops.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 73ffe03d4..4da7063b2 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -256,7 +256,7 @@ static RegisterPrimOp primop_import({ .doc = R"( Load, parse and return the Nix expression in the file *path*. - The value *path* is conveted to a string as described in `builtins.toString`. + The value *path* is converted to a string as described in `builtins.toString`. If *path* is a directory, the file ` default.nix ` in that directory is loaded. -- cgit v1.2.3 From 957f83207433fec3d9a9b464c6dad0488f3934c8 Mon Sep 17 00:00:00 2001 From: Vaci Koblizek Date: Tue, 28 Mar 2023 17:20:33 +0100 Subject: Avoid a string copy during Hash::to_string --- src/libutil/hash.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/libutil/hash.cc b/src/libutil/hash.cc index d2fd0c15a..5a7be47b2 100644 --- a/src/libutil/hash.cc +++ b/src/libutil/hash.cc @@ -130,7 +130,7 @@ std::string Hash::to_string(Base base, bool includeType) const break; case Base64: case SRI: - s += base64Encode(std::string((const char *) hash, hashSize)); + s += base64Encode(std::string_view((const char *) hash, hashSize)); break; } return s; -- cgit v1.2.3 From ccf7ce26fe6207d7a648715a45985379f772931f Mon Sep 17 00:00:00 2001 From: Vaci Koblizek Date: Wed, 29 Mar 2023 10:44:22 +0100 Subject: return string_view from printHashType rather than string --- src/libutil/hash.cc | 2 +- src/libutil/hash.hh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/libutil/hash.cc b/src/libutil/hash.cc index 5a7be47b2..84832c4ad 100644 --- a/src/libutil/hash.cc +++ b/src/libutil/hash.cc @@ -403,7 +403,7 @@ HashType parseHashType(std::string_view s) throw UsageError("unknown hash algorithm '%1%'", s); } -std::string printHashType(HashType ht) +std::string_view printHashType(HashType ht) { switch (ht) { case htMD5: return "md5"; diff --git a/src/libutil/hash.hh b/src/libutil/hash.hh index 00f70a572..38d09646e 100644 --- a/src/libutil/hash.hh +++ b/src/libutil/hash.hh @@ -133,7 +133,7 @@ HashType parseHashType(std::string_view s); std::optional parseHashTypeOpt(std::string_view s); /* And the reverse. */ -std::string printHashType(HashType ht); +std::string_view printHashType(HashType ht); union Ctx; -- cgit v1.2.3 From a4dd87a2b303af107e0671ce6e8873b5a2d90b3e Mon Sep 17 00:00:00 2001 From: Vaci Koblizek Date: Wed, 29 Mar 2023 10:54:27 +0100 Subject: avoid a string copy in printHash16 --- src/libutil/hash.cc | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/libutil/hash.cc b/src/libutil/hash.cc index 84832c4ad..5735e4715 100644 --- a/src/libutil/hash.cc +++ b/src/libutil/hash.cc @@ -71,12 +71,13 @@ const std::string base16Chars = "0123456789abcdef"; static std::string printHash16(const Hash & hash) { - char buf[hash.hashSize * 2]; + std::string buf; + buf.reserve(hash.hashSize * 2); for (unsigned int i = 0; i < hash.hashSize; i++) { - buf[i * 2] = base16Chars[hash.hash[i] >> 4]; - buf[i * 2 + 1] = base16Chars[hash.hash[i] & 0x0f]; + buf.push_back(base16Chars[hash.hash[i] >> 4]); + buf.push_back(base16Chars[hash.hash[i] & 0x0f]); } - return std::string(buf, hash.hashSize * 2); + return buf; } -- cgit v1.2.3 From 2e6b95dac7bd96c175db995b0cf7a1c3972fe4b8 Mon Sep 17 00:00:00 2001 From: Joachim Breitner Date: Wed, 29 Mar 2023 13:19:51 +0200 Subject: Update src/libexpr/primops.cc Co-authored-by: Eelco Dolstra --- src/libexpr/primops.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 4da7063b2..ea91aac03 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -258,7 +258,7 @@ static RegisterPrimOp primop_import({ The value *path* is converted to a string as described in `builtins.toString`. - If *path* is a directory, the file ` default.nix ` in that directory + If *path* is a directory, the file `default.nix` in that directory is loaded. Evaluation aborts if the file doesn’t exist or contains -- cgit v1.2.3 From 081aadb76ca0fb30fa4de4338a23e2ccf4df0289 Mon Sep 17 00:00:00 2001 From: figsoda Date: Wed, 29 Mar 2023 13:59:14 -0400 Subject: Allow `@` in git refs --- src/libutil/url-parts.hh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/libutil/url-parts.hh b/src/libutil/url-parts.hh index d5e6a2736..afc1df98a 100644 --- a/src/libutil/url-parts.hh +++ b/src/libutil/url-parts.hh @@ -23,7 +23,7 @@ const static std::string absPathRegex = "(?:(?:/" + segmentRegex + ")*/?)"; const static std::string pathRegex = "(?:" + segmentRegex + "(?:/" + segmentRegex + ")*/?)"; // A Git ref (i.e. branch or tag name). -const static std::string refRegexS = "[a-zA-Z0-9][a-zA-Z0-9_.\\/-]*"; // FIXME: check +const static std::string refRegexS = "[a-zA-Z0-9@][a-zA-Z0-9_.\\/@-]*"; // FIXME: check extern std::regex refRegex; // Instead of defining what a good Git Ref is, we define what a bad Git Ref is -- cgit v1.2.3 From 8cab89a94b28a51b8dcbbe4b681df3c633a41aec Mon Sep 17 00:00:00 2001 From: John Ericson Date: Sat, 25 Mar 2023 19:12:44 -0400 Subject: Convert a bunch of comments in headers to Doxygen documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The internal API docs now contain more useful information. Co-authored-by: Théophane Hufschmitt <7226587+thufschmitt@users.noreply.github.com> --- src/libstore/content-address.hh | 72 +++-- src/libstore/derivations.hh | 348 ++++++++++++++------- src/libstore/derived-path.hh | 2 +- src/libstore/outputs-spec.hh | 33 +- src/libstore/path.hh | 21 +- src/libstore/store-api.hh | 674 +++++++++++++++++++++++++--------------- 6 files changed, 756 insertions(+), 394 deletions(-) (limited to 'src') diff --git a/src/libstore/content-address.hh b/src/libstore/content-address.hh index f6a6f5140..634a51c38 100644 --- a/src/libstore/content-address.hh +++ b/src/libstore/content-address.hh @@ -5,44 +5,75 @@ namespace nix { +/** + * An enumeration of the ways we can serialize file system objects. + */ enum struct FileIngestionMethod : uint8_t { + /** + * Flat-file hashing. Directly ingest the contents of a single file + */ Flat = false, + /** + * Recursive (or NAR) hashing. Serializes the file-system object in Nix + * Archive format and ingest that + */ Recursive = true }; +/** + * Somewhat obscure, used by \ref Derivation derivations and + * `builtins.toFile` currently. + */ struct TextHash { + /** + * Hash of the contents of the text/file. + */ Hash hash; }; -/// Pair of a hash, and how the file system was ingested +/** + * For path computed by makeFixedOutputPath. + */ struct FixedOutputHash { + /** + * How the file system objects are serialized + */ FileIngestionMethod method; + /** + * Hash of that serialization + */ Hash hash; + std::string printMethodAlgo() const; }; -/* - We've accumulated several types of content-addressed paths over the years; - fixed-output derivations support multiple hash algorithms and serialisation - methods (flat file vs NAR). Thus, ‘ca’ has one of the following forms: - - * ‘text:sha256:’: For paths - computed by makeTextPath() / addTextToStore(). - - * ‘fixed:::’: For paths computed by - makeFixedOutputPath() / addToStore(). -*/ +/** + * We've accumulated several types of content-addressed paths over the + * years; fixed-output derivations support multiple hash algorithms and + * serialisation methods (flat file vs NAR). Thus, ‘ca’ has one of the + * following forms: + * + * - ‘text:sha256:’: For paths + * computed by Store::makeTextPath() / Store::addTextToStore(). + * + * - ‘fixed:::’: For paths computed by + * Store::makeFixedOutputPath() / Store::addToStore(). + */ typedef std::variant< - TextHash, // for paths computed by makeTextPath() / addTextToStore - FixedOutputHash // for path computed by makeFixedOutputPath + TextHash, + FixedOutputHash > ContentAddress; -/* Compute the prefix to the hash algorithm which indicates how the files were - ingested. */ +/** + * Compute the prefix to the hash algorithm which indicates how the + * files were ingested. + */ std::string makeFileIngestionPrefix(const FileIngestionMethod m); -/* Compute the content-addressability assertion (ValidPathInfo::ca) - for paths created by makeFixedOutputPath() / addToStore(). */ +/** + * Compute the content-addressability assertion (ValidPathInfo::ca) for + * paths created by Store::makeFixedOutputPath() / Store::addToStore(). + */ std::string makeFixedOutputCA(FileIngestionMethod method, const Hash & hash); std::string renderContentAddress(ContentAddress ca); @@ -65,6 +96,11 @@ struct FixedOutputHashMethod { HashType hashType; }; +/** + * Ways of content addressing but not a complete ContentAddress. + * + * A ContentAddress without a Hash. + */ typedef std::variant< TextHashMethod, FixedOutputHashMethod diff --git a/src/libstore/derivations.hh b/src/libstore/derivations.hh index 8456b29e7..a5731f18d 100644 --- a/src/libstore/derivations.hh +++ b/src/libstore/derivations.hh @@ -17,42 +17,72 @@ class Store; /* Abstract syntax of derivations. */ -/* The traditional non-fixed-output derivation type. */ +/** + * The traditional non-fixed-output derivation type. + */ struct DerivationOutputInputAddressed { StorePath path; }; -/* Fixed-output derivations, whose output paths are content addressed - according to that fixed output. */ +/** + * Fixed-output derivations, whose output paths are content + * addressed according to that fixed output. + */ struct DerivationOutputCAFixed { - FixedOutputHash hash; /* hash used for expected hash computation */ + /** + * hash used for expected hash computation + */ + FixedOutputHash hash; + + /** + * Return the \ref StorePath "store path" corresponding to this output + * + * @param drvName The name of the derivation this is an output of, without the `.drv`. + * @param outputName The name of this output. + */ StorePath path(const Store & store, std::string_view drvName, std::string_view outputName) const; }; -/* Floating-output derivations, whose output paths are content addressed, but - not fixed, and so are dynamically calculated from whatever the output ends - up being. */ +/** + * Floating-output derivations, whose output paths are content + * addressed, but not fixed, and so are dynamically calculated from + * whatever the output ends up being. + * */ struct DerivationOutputCAFloating { - /* information used for expected hash computation */ + /** + * How the file system objects will be serialized for hashing + */ FileIngestionMethod method; + + /** + * How the serialization will be hashed + */ HashType hashType; }; -/* Input-addressed output which depends on a (CA) derivation whose hash isn't - * known yet. +/** + * Input-addressed output which depends on a (CA) derivation whose hash + * isn't known yet. */ struct DerivationOutputDeferred {}; -/* Impure output which is moved to a content-addressed location (like - CAFloating) but isn't registered as a realization. +/** + * Impure output which is moved to a content-addressed location (like + * CAFloating) but isn't registered as a realization. */ struct DerivationOutputImpure { - /* information used for expected hash computation */ + /** + * How the file system objects will be serialized for hashing + */ FileIngestionMethod method; + + /** + * How the serialization will be hashed + */ HashType hashType; }; @@ -64,6 +94,9 @@ typedef std::variant< DerivationOutputImpure > _DerivationOutputRaw; +/** + * A single output of a BasicDerivation (and Derivation). + */ struct DerivationOutput : _DerivationOutputRaw { using Raw = _DerivationOutputRaw; @@ -75,9 +108,12 @@ struct DerivationOutput : _DerivationOutputRaw using Deferred = DerivationOutputDeferred; using Impure = DerivationOutputImpure; - /* Note, when you use this function you should make sure that you're passing - the right derivation name. When in doubt, you should use the safer - interface provided by BasicDerivation::outputsAndOptPaths */ + /** + * \note when you use this function you should make sure that you're + * passing the right derivation name. When in doubt, you should use + * the safer interface provided by + * BasicDerivation::outputsAndOptPaths + */ std::optional path(const Store & store, std::string_view drvName, std::string_view outputName) const; inline const Raw & raw() const { @@ -92,26 +128,61 @@ struct DerivationOutput : _DerivationOutputRaw typedef std::map DerivationOutputs; -/* These are analogues to the previous DerivationOutputs data type, but they - also contains, for each output, the (optional) store path in which it would - be written. To calculate values of these types, see the corresponding - functions in BasicDerivation */ +/** + * These are analogues to the previous DerivationOutputs data type, + * but they also contains, for each output, the (optional) store + * path in which it would be written. To calculate values of these + * types, see the corresponding functions in BasicDerivation. + */ typedef std::map>> DerivationOutputsAndOptPaths; -/* For inputs that are sub-derivations, we specify exactly which - output IDs we are interested in. */ +/** + * For inputs that are sub-derivations, we specify exactly which + * output IDs we are interested in. + */ typedef std::map DerivationInputs; +/** + * Input-addressed derivation types + */ struct DerivationType_InputAddressed { + /** + * True iff the derivation type can't be determined statically, + * for instance because it (transitively) depends on a content-addressed + * derivation. + */ bool deferred; }; +/** + * Content-addressed derivation types + */ struct DerivationType_ContentAddressed { + /** + * Whether the derivation should be built safely inside a sandbox. + */ bool sandboxed; + /** + * Whether the derivation's outputs' content-addresses are "fixed" + * or "floating. + * + * - Fixed: content-addresses are written down as part of the + * derivation itself. If the outputs don't end up matching the + * build fails. + * + * - Floating: content-addresses are not written down, we do not + * know them until we perform the build. + */ bool fixed; }; +/** + * Impure derivation type + * + * This is similar at buil-time to the content addressed, not standboxed, not fixed + * type, but has some restrictions on its usage. + */ struct DerivationType_Impure { }; @@ -128,30 +199,38 @@ struct DerivationType : _DerivationTypeRaw { using ContentAddressed = DerivationType_ContentAddressed; using Impure = DerivationType_Impure; - /* Do the outputs of the derivation have paths calculated from their content, - or from the derivation itself? */ + /** + * Do the outputs of the derivation have paths calculated from their + * content, or from the derivation itself? + */ bool isCA() const; - /* Is the content of the outputs fixed a-priori via a hash? Never true for - non-CA derivations. */ + /** + * Is the content of the outputs fixed a priori via a hash? + * Never true for non-CA derivations. + */ bool isFixed() const; - /* Whether the derivation is fully sandboxed. If false, the - sandbox is opened up, e.g. the derivation has access to the - network. Note that whether or not we actually sandbox the - derivation is controlled separately. Always true for non-CA - derivations. */ + /** + * Whether the derivation is fully sandboxed. If false, the sandbox + * is opened up, e.g. the derivation has access to the network. Note + * that whether or not we actually sandbox the derivation is + * controlled separately. Always true for non-CA derivations. + */ bool isSandboxed() const; - /* Whether the derivation is expected to produce the same result - every time, and therefore it only needs to be built once. This - is only false for derivations that have the attribute '__impure - = true'. */ + /** + * Whether the derivation is expected to produce the same result + * every time, and therefore it only needs to be built once. This is + * only false for derivations that have the attribute '__impure = + * true'. + */ bool isPure() const; - /* Does the derivation knows its own output paths? - Only true when there's no floating-ca derivation involved in the - closure, or if fixed output. + /** + * Does the derivation knows its own output paths? + * Only true when there's no floating-ca derivation involved in the + * closure, or if fixed output. */ bool hasKnownOutputPaths() const; @@ -175,15 +254,21 @@ struct BasicDerivation bool isBuiltin() const; - /* Return true iff this is a fixed-output derivation. */ + /** + * Return true iff this is a fixed-output derivation. + */ DerivationType type() const; - /* Return the output names of a derivation. */ + /** + * Return the output names of a derivation. + */ StringSet outputNames() const; - /* Calculates the maps that contains all the DerivationOutputs, but - augmented with knowledge of the Store paths they would be written - into. */ + /** + * Calculates the maps that contains all the DerivationOutputs, but + * augmented with knowledge of the Store paths they would be written + * into. + */ DerivationOutputsAndOptPaths outputsAndOptPaths(const Store & store) const; static std::string_view nameFromPath(const StorePath & storePath); @@ -191,23 +276,33 @@ struct BasicDerivation struct Derivation : BasicDerivation { - DerivationInputs inputDrvs; /* inputs that are sub-derivations */ + /** + * inputs that are sub-derivations + */ + DerivationInputs inputDrvs; - /* Print a derivation. */ + /** + * Print a derivation. + */ std::string unparse(const Store & store, bool maskOutputs, std::map * actualInputs = nullptr) const; - /* Return the underlying basic derivation but with these changes: - - 1. Input drvs are emptied, but the outputs of them that were used are - added directly to input sources. - - 2. Input placeholders are replaced with realized input store paths. */ + /** + * Return the underlying basic derivation but with these changes: + * + * 1. Input drvs are emptied, but the outputs of them that were used + * are added directly to input sources. + * + * 2. Input placeholders are replaced with realized input store + * paths. + */ std::optional tryResolve(Store & store) const; - /* Like the above, but instead of querying the Nix database for - realisations, uses a given mapping from input derivation paths - + output names to actual output store paths. */ + /** + * Like the above, but instead of querying the Nix database for + * realisations, uses a given mapping from input derivation paths + + * output names to actual output store paths. + */ std::optional tryResolve( Store & store, const std::map, StorePath> & inputDrvOutputs) const; @@ -222,81 +317,108 @@ struct Derivation : BasicDerivation class Store; -/* Write a derivation to the Nix store, and return its path. */ +/** + * Write a derivation to the Nix store, and return its path. + */ StorePath writeDerivation(Store & store, const Derivation & drv, RepairFlag repair = NoRepair, bool readOnly = false); -/* Read a derivation from a file. */ +/** + * Read a derivation from a file. + */ Derivation parseDerivation(const Store & store, std::string && s, std::string_view name); -// FIXME: remove +/** + * \todo Remove. + * + * Use Path::isDerivation instead. + */ bool isDerivation(std::string_view fileName); -/* Calculate the name that will be used for the store path for this - output. - - This is usually -, but is just when - the output name is "out". */ +/** + * Calculate the name that will be used for the store path for this + * output. + * + * This is usually -, but is just when + * the output name is "out". + */ std::string outputPathName(std::string_view drvName, std::string_view outputName); -// The hashes modulo of a derivation. -// -// Each output is given a hash, although in practice only the content-addressed -// derivations (fixed-output or not) will have a different hash for each -// output. +/** + * The hashes modulo of a derivation. + * + * Each output is given a hash, although in practice only the content-addressed + * derivations (fixed-output or not) will have a different hash for each + * output. + */ struct DrvHash { + /** + * Map from output names to hashes + */ std::map hashes; enum struct Kind : bool { - // Statically determined derivations. - // This hash will be directly used to compute the output paths + /** + * Statically determined derivations. + * This hash will be directly used to compute the output paths + */ Regular, - // Floating-output derivations (and their reverse dependencies). + + /** + * Floating-output derivations (and their reverse dependencies). + */ Deferred, }; + /** + * The kind of derivation this is, simplified for just "derivation hash + * modulo" purposes. + */ Kind kind; }; void operator |= (DrvHash::Kind & self, const DrvHash::Kind & other) noexcept; -/* Returns hashes with the details of fixed-output subderivations - expunged. - - A fixed-output derivation is a derivation whose outputs have a - specified content hash and hash algorithm. (Currently they must have - exactly one output (`out'), which is specified using the `outputHash' - and `outputHashAlgo' attributes, but the algorithm doesn't assume - this.) We don't want changes to such derivations to propagate upwards - through the dependency graph, changing output paths everywhere. - - For instance, if we change the url in a call to the `fetchurl' - function, we do not want to rebuild everything depending on it---after - all, (the hash of) the file being downloaded is unchanged. So the - *output paths* should not change. On the other hand, the *derivation - paths* should change to reflect the new dependency graph. - - For fixed-output derivations, this returns a map from the name of - each output to its hash, unique up to the output's contents. - - For regular derivations, it returns a single hash of the derivation - ATerm, after subderivations have been likewise expunged from that - derivation. +/** + * Returns hashes with the details of fixed-output subderivations + * expunged. + * + * A fixed-output derivation is a derivation whose outputs have a + * specified content hash and hash algorithm. (Currently they must have + * exactly one output (`out'), which is specified using the `outputHash' + * and `outputHashAlgo' attributes, but the algorithm doesn't assume + * this.) We don't want changes to such derivations to propagate upwards + * through the dependency graph, changing output paths everywhere. + * + * For instance, if we change the url in a call to the `fetchurl' + * function, we do not want to rebuild everything depending on it---after + * all, (the hash of) the file being downloaded is unchanged. So the + * *output paths* should not change. On the other hand, the *derivation + * paths* should change to reflect the new dependency graph. + * + * For fixed-output derivations, this returns a map from the name of + * each output to its hash, unique up to the output's contents. + * + * For regular derivations, it returns a single hash of the derivation + * ATerm, after subderivations have been likewise expunged from that + * derivation. */ DrvHash hashDerivationModulo(Store & store, const Derivation & drv, bool maskOutputs); -/* - Return a map associating each output to a hash that uniquely identifies its - derivation (modulo the self-references). - - FIXME: what is the Hash in this map? +/** + * Return a map associating each output to a hash that uniquely identifies its + * derivation (modulo the self-references). + * + * \todo What is the Hash in this map? */ std::map staticOutputHashes(Store & store, const Derivation & drv); -/* Memoisation of hashDerivationModulo(). */ +/** + * Memoisation of hashDerivationModulo(). + */ typedef std::map DrvHashes; // FIXME: global, though at least thread-safe. @@ -308,21 +430,25 @@ struct Sink; Source & readDerivation(Source & in, const Store & store, BasicDerivation & drv, std::string_view name); void writeDerivation(Sink & out, const Store & store, const BasicDerivation & drv); -/* This creates an opaque and almost certainly unique string - deterministically from the output name. - - It is used as a placeholder to allow derivations to refer to their - own outputs without needing to use the hash of a derivation in - itself, making the hash near-impossible to calculate. */ +/** + * This creates an opaque and almost certainly unique string + * deterministically from the output name. + * + * It is used as a placeholder to allow derivations to refer to their + * own outputs without needing to use the hash of a derivation in + * itself, making the hash near-impossible to calculate. + */ std::string hashPlaceholder(const std::string_view outputName); -/* This creates an opaque and almost certainly unique string - deterministically from a derivation path and output name. - - It is used as a placeholder to allow derivations to refer to - content-addressed paths whose content --- and thus the path - themselves --- isn't yet known. This occurs when a derivation has a - dependency which is a CA derivation. */ +/** + * This creates an opaque and almost certainly unique string + * deterministically from a derivation path and output name. + * + * It is used as a placeholder to allow derivations to refer to + * content-addressed paths whose content --- and thus the path + * themselves --- isn't yet known. This occurs when a derivation has a + * dependency which is a CA derivation. + */ std::string downstreamPlaceholder(const Store & store, const StorePath & drvPath, std::string_view outputName); extern const Hash impureOutputHash; diff --git a/src/libstore/derived-path.hh b/src/libstore/derived-path.hh index 9e0cce377..72dbcc128 100644 --- a/src/libstore/derived-path.hh +++ b/src/libstore/derived-path.hh @@ -105,7 +105,7 @@ using _BuiltPathRaw = std::variant< >; /** - * A built path. Similar to a `DerivedPath`, but enriched with the corresponding + * A built path. Similar to a DerivedPath, but enriched with the corresponding * output path(s). */ struct BuiltPath : _BuiltPathRaw { diff --git a/src/libstore/outputs-spec.hh b/src/libstore/outputs-spec.hh index 46bc35ebc..0b7c98ac9 100644 --- a/src/libstore/outputs-spec.hh +++ b/src/libstore/outputs-spec.hh @@ -9,6 +9,9 @@ namespace nix { +/** + * A non-empty set of outputs, specified by name + */ struct OutputNames : std::set { using std::set::set; @@ -18,6 +21,9 @@ struct OutputNames : std::set { : std::set(s) { assert(!empty()); } + /** + * Needs to be "inherited manually" + */ OutputNames(std::set && s) : std::set(s) { assert(!empty()); } @@ -28,6 +34,9 @@ struct OutputNames : std::set { OutputNames() = delete; }; +/** + * The set of all outputs, without needing to name them explicitly + */ struct AllOutputs : std::monostate { }; typedef std::variant _OutputsSpecRaw; @@ -36,7 +45,9 @@ struct OutputsSpec : _OutputsSpecRaw { using Raw = _OutputsSpecRaw; using Raw::Raw; - /* Force choosing a variant */ + /** + * Force choosing a variant + */ OutputsSpec() = delete; using Names = OutputNames; @@ -52,14 +63,20 @@ struct OutputsSpec : _OutputsSpecRaw { bool contains(const std::string & output) const; - /* Create a new OutputsSpec which is the union of this and that. */ + /** + * Create a new OutputsSpec which is the union of this and that. + */ OutputsSpec union_(const OutputsSpec & that) const; - /* Whether this OutputsSpec is a subset of that. */ + /** + * Whether this OutputsSpec is a subset of that. + */ bool isSubsetOf(const OutputsSpec & outputs) const; - /* Parse a string of the form 'output1,...outputN' or - '*', returning the outputs spec. */ + /** + * Parse a string of the form 'output1,...outputN' or '*', returning + * the outputs spec. + */ static OutputsSpec parse(std::string_view s); static std::optional parseOpt(std::string_view s); @@ -81,8 +98,10 @@ struct ExtendedOutputsSpec : _ExtendedOutputsSpecRaw { return static_cast(*this); } - /* Parse a string of the form 'prefix^output1,...outputN' or - 'prefix^*', returning the prefix and the extended outputs spec. */ + /** + * Parse a string of the form 'prefix^output1,...outputN' or + * 'prefix^*', returning the prefix and the extended outputs spec. + */ static std::pair parse(std::string_view s); static std::optional> parseOpt(std::string_view s); diff --git a/src/libstore/path.hh b/src/libstore/path.hh index 1e5579b90..2730541c6 100644 --- a/src/libstore/path.hh +++ b/src/libstore/path.hh @@ -8,13 +8,22 @@ namespace nix { struct Hash; +/** + * \ref StorePath "Store path" is the fundamental reference type of Nix. + * A store paths refers to a Store object. + * + * See glossary.html#gloss-store-path for more information on a + * conceptual level. + */ class StorePath { std::string baseName; public: - /* Size of the hash part of store paths, in base-32 characters. */ + /** + * Size of the hash part of store paths, in base-32 characters. + */ constexpr static size_t HashLen = 32; // i.e. 160 bits constexpr static size_t MaxPathLen = 211; @@ -45,8 +54,9 @@ public: return baseName != other.baseName; } - /* Check whether a file name ends with the extension for - derivations. */ + /** + * Check whether a file name ends with the extension for derivations. + */ bool isDerivation() const; std::string_view name() const @@ -67,7 +77,10 @@ public: typedef std::set StorePathSet; typedef std::vector StorePaths; -/* Extension of derivations in the Nix store. */ +/** + * The file extension of \ref Derivation derivations when serialized + * into store objects. + */ const std::string drvExtension = ".drv"; } diff --git a/src/libstore/store-api.hh b/src/libstore/store-api.hh index 2f4391c43..194cbe6c4 100644 --- a/src/libstore/store-api.hh +++ b/src/libstore/store-api.hh @@ -55,7 +55,10 @@ namespace nix { */ MakeError(SubstError, Error); -MakeError(BuildError, Error); // denotes a permanent build failure +/** + * denotes a permanent build failure + */ +MakeError(BuildError, Error); MakeError(InvalidPath, Error); MakeError(Unsupported, Error); MakeError(SubstituteGone, Error); @@ -78,7 +81,9 @@ enum CheckSigsFlag : bool { NoCheckSigs = false, CheckSigs = true }; enum SubstituteFlag : bool { NoSubstitute = false, Substitute = true }; enum AllowInvalidFlag : bool { DisallowInvalid = false, AllowInvalid = true }; -/* Magic header of exportPath() output (obsolete). */ +/** + * Magic header of exportPath() output (obsolete). + */ const uint32_t exportMagic = 0x4558494e; @@ -153,17 +158,26 @@ protected: struct PathInfoCacheValue { - // Time of cache entry creation or update + /** + * Time of cache entry creation or update + */ std::chrono::time_point time_point = std::chrono::steady_clock::now(); - // Null if missing + /** + * Null if missing + */ std::shared_ptr value; - // Whether the value is valid as a cache entry. The path may not exist. + /** + * Whether the value is valid as a cache entry. The path may not + * exist. + */ bool isKnownNow(); - // Past tense, because a path can only be assumed to exists when - // isKnownNow() && didExist() + /** + * Past tense, because a path can only be assumed to exists when + * isKnownNow() && didExist() + */ inline bool didExist() { return value != nullptr; } @@ -197,35 +211,53 @@ public: std::string printStorePath(const StorePath & path) const; - // FIXME: remove + /** + * Deprecated + * + * \todo remove + */ StorePathSet parseStorePathSet(const PathSet & paths) const; PathSet printStorePathSet(const StorePathSet & path) const; - /* Display a set of paths in human-readable form (i.e., between quotes - and separated by commas). */ + /** + * Display a set of paths in human-readable form (i.e., between quotes + * and separated by commas). + */ std::string showPaths(const StorePathSet & paths); - /* Return true if ‘path’ is in the Nix store (but not the Nix - store itself). */ + /** + * @return true if ‘path’ is in the Nix store (but not the Nix + * store itself). + */ bool isInStore(PathView path) const; - /* Return true if ‘path’ is a store path, i.e. a direct child of - the Nix store. */ + /** + * @return true if ‘path’ is a store path, i.e. a direct child of the + * Nix store. + */ bool isStorePath(std::string_view path) const; - /* Split a path like /nix/store/-/ into - /nix/store/- and /. */ + /** + * Split a path like /nix/store/-/ into + * /nix/store/- and /. + */ std::pair toStorePath(PathView path) const; - /* Follow symlinks until we end up with a path in the Nix store. */ + /** + * Follow symlinks until we end up with a path in the Nix store. + */ Path followLinksToStore(std::string_view path) const; - /* Same as followLinksToStore(), but apply toStorePath() to the - result. */ + /** + * Same as followLinksToStore(), but apply toStorePath() to the + * result. + */ StorePath followLinksToStorePath(std::string_view path) const; - /* Constructs a unique store path name. */ + /** + * Constructs a unique store path name. + */ StorePath makeStorePath(std::string_view type, std::string_view hash, std::string_view name) const; StorePath makeStorePath(std::string_view type, @@ -246,33 +278,40 @@ public: const StorePathSet & references = {}, bool hasSelfReference = false) const; - /* This is the preparatory part of addToStore(); it computes the - store path to which srcPath is to be copied. Returns the store - path and the cryptographic hash of the contents of srcPath. */ + /** + * Preparatory part of addToStore(). + * + * @return the store path to which srcPath is to be copied + * and the cryptographic hash of the contents of srcPath. + */ std::pair computeStorePathForPath(std::string_view name, const Path & srcPath, FileIngestionMethod method = FileIngestionMethod::Recursive, HashType hashAlgo = htSHA256, PathFilter & filter = defaultPathFilter) const; - /* Preparatory part of addTextToStore(). - - !!! Computation of the path should take the references given to - addTextToStore() into account, otherwise we have a (relatively - minor) security hole: a caller can register a source file with - bogus references. If there are too many references, the path may - not be garbage collected when it has to be (not really a problem, - the caller could create a root anyway), or it may be garbage - collected when it shouldn't be (more serious). - - Hashing the references would solve this (bogus references would - simply yield a different store path, so other users wouldn't be - affected), but it has some backwards compatibility issues (the - hashing scheme changes), so I'm not doing that for now. */ + /** + * Preparatory part of addTextToStore(). + * + * !!! Computation of the path should take the references given to + * addTextToStore() into account, otherwise we have a (relatively + * minor) security hole: a caller can register a source file with + * bogus references. If there are too many references, the path may + * not be garbage collected when it has to be (not really a problem, + * the caller could create a root anyway), or it may be garbage + * collected when it shouldn't be (more serious). + * + * Hashing the references would solve this (bogus references would + * simply yield a different store path, so other users wouldn't be + * affected), but it has some backwards compatibility issues (the + * hashing scheme changes), so I'm not doing that for now. + */ StorePath computeStorePathForText( std::string_view name, std::string_view s, const StorePathSet & references) const; - /* Check whether a path is valid. */ + /** + * Check whether a path is valid. + */ bool isValidPath(const StorePath & path); protected: @@ -281,53 +320,68 @@ protected: public: - /* If requested, substitute missing paths. This - implements nix-copy-closure's --use-substitutes - flag. */ + /** + * If requested, substitute missing paths. This + * implements nix-copy-closure's --use-substitutes + * flag. + */ void substitutePaths(const StorePathSet & paths); - /* Query which of the given paths is valid. Optionally, try to - substitute missing paths. */ + /** + * Query which of the given paths is valid. Optionally, try to + * substitute missing paths. + */ virtual StorePathSet queryValidPaths(const StorePathSet & paths, SubstituteFlag maybeSubstitute = NoSubstitute); - /* Query the set of all valid paths. Note that for some store - backends, the name part of store paths may be replaced by 'x' - (i.e. you'll get /nix/store/-x rather than - /nix/store/-). Use queryPathInfo() to obtain the - full store path. FIXME: should return a set of - std::variant to get rid of this hack. */ + /** + * Query the set of all valid paths. Note that for some store + * backends, the name part of store paths may be replaced by 'x' + * (i.e. you'll get /nix/store/-x rather than + * /nix/store/-). Use queryPathInfo() to obtain the + * full store path. FIXME: should return a set of + * std::variant to get rid of this hack. + */ virtual StorePathSet queryAllValidPaths() { unsupported("queryAllValidPaths"); } constexpr static const char * MissingName = "x"; - /* Query information about a valid path. It is permitted to omit - the name part of the store path. */ + /** + * Query information about a valid path. It is permitted to omit + * the name part of the store path. + */ ref queryPathInfo(const StorePath & path); - /* Asynchronous version of queryPathInfo(). */ + /** + * Asynchronous version of queryPathInfo(). + */ void queryPathInfo(const StorePath & path, Callback> callback) noexcept; - /* Query the information about a realisation. */ + /** + * Query the information about a realisation. + */ std::shared_ptr queryRealisation(const DrvOutput &); - /* Asynchronous version of queryRealisation(). */ + /** + * Asynchronous version of queryRealisation(). + */ void queryRealisation(const DrvOutput &, Callback> callback) noexcept; - /* Check whether the given valid path info is sufficiently attested, by - either being signed by a trusted public key or content-addressed, in - order to be included in the given store. - - These same checks would be performed in addToStore, but this allows an - earlier failure in the case where dependencies need to be added too, but - the addToStore wouldn't fail until those dependencies are added. Also, - we don't really want to add the dependencies listed in a nar info we - don't trust anyyways. - */ + /** + * Check whether the given valid path info is sufficiently attested, by + * either being signed by a trusted public key or content-addressed, in + * order to be included in the given store. + * + * These same checks would be performed in addToStore, but this allows an + * earlier failure in the case where dependencies need to be added too, but + * the addToStore wouldn't fail until those dependencies are added. Also, + * we don't really want to add the dependencies listed in a nar info we + * don't trust anyyways. + */ virtual bool pathInfoIsUntrusted(const ValidPathInfo &) { return true; @@ -347,53 +401,77 @@ protected: public: - /* Queries the set of incoming FS references for a store path. - The result is not cleared. */ + /** + * Queries the set of incoming FS references for a store path. + * The result is not cleared. + */ virtual void queryReferrers(const StorePath & path, StorePathSet & referrers) { unsupported("queryReferrers"); } - /* Return all currently valid derivations that have `path' as an - output. (Note that the result of `queryDeriver()' is the - derivation that was actually used to produce `path', which may - not exist anymore.) */ + /** + * @return all currently valid derivations that have `path' as an + * output. + * + * (Note that the result of `queryDeriver()' is the derivation that + * was actually used to produce `path', which may not exist + * anymore.) + */ virtual StorePathSet queryValidDerivers(const StorePath & path) { return {}; }; - /* Query the outputs of the derivation denoted by `path'. */ + /** + * Query the outputs of the derivation denoted by `path'. + */ virtual StorePathSet queryDerivationOutputs(const StorePath & path); - /* Query the mapping outputName => outputPath for the given derivation. All - outputs are mentioned so ones mising the mapping are mapped to - `std::nullopt`. */ + /** + * Query the mapping outputName => outputPath for the given + * derivation. All outputs are mentioned so ones mising the mapping + * are mapped to `std::nullopt`. + */ virtual std::map> queryPartialDerivationOutputMap(const StorePath & path); - /* Query the mapping outputName=>outputPath for the given derivation. - Assume every output has a mapping and throw an exception otherwise. */ + /** + * Query the mapping outputName=>outputPath for the given derivation. + * Assume every output has a mapping and throw an exception otherwise. + */ OutputPathMap queryDerivationOutputMap(const StorePath & path); - /* Query the full store path given the hash part of a valid store - path, or empty if the path doesn't exist. */ + /** + * Query the full store path given the hash part of a valid store + * path, or empty if the path doesn't exist. + */ virtual std::optional queryPathFromHashPart(const std::string & hashPart) = 0; - /* Query which of the given paths have substitutes. */ + /** + * Query which of the given paths have substitutes. + */ virtual StorePathSet querySubstitutablePaths(const StorePathSet & paths) { return {}; }; - /* Query substitute info (i.e. references, derivers and download - sizes) of a map of paths to their optional ca values. The info - of the first succeeding substituter for each path will be - returned. If a path does not have substitute info, it's omitted - from the resulting ‘infos’ map. */ + /** + * Query substitute info (i.e. references, derivers and download + * sizes) of a map of paths to their optional ca values. The info of + * the first succeeding substituter for each path will be returned. + * If a path does not have substitute info, it's omitted from the + * resulting ‘infos’ map. + */ virtual void querySubstitutablePathInfos(const StorePathCAMap & paths, SubstitutablePathInfos & infos) { return; }; - /* Import a path into the store. */ + /** + * Import a path into the store. + */ virtual void addToStore(const ValidPathInfo & info, Source & narSource, RepairFlag repair = NoRepair, CheckSigsFlag checkSigs = CheckSigs) = 0; - // A list of paths infos along with a source providing the content of the - // associated store path + /** + * A list of paths infos along with a source providing the content + * of the associated store path + */ using PathsSource = std::vector>>; - /* Import multiple paths into the store. */ + /** + * Import multiple paths into the store. + */ virtual void addMultipleToStore( Source & source, RepairFlag repair = NoRepair, @@ -405,10 +483,14 @@ public: RepairFlag repair = NoRepair, CheckSigsFlag checkSigs = CheckSigs); - /* Copy the contents of a path to the store and register the - validity the resulting path. The resulting path is returned. - The function object `filter' can be used to exclude files (see - libutil/archive.hh). */ + /** + * Copy the contents of a path to the store and register the + * validity the resulting path. + * + * @return The resulting path is returned. + * @param filter This function can be used to exclude files (see + * libutil/archive.hh). + */ virtual StorePath addToStore( std::string_view name, const Path & srcPath, @@ -418,26 +500,33 @@ public: RepairFlag repair = NoRepair, const StorePathSet & references = StorePathSet()); - /* Copy the contents of a path to the store and register the - validity the resulting path, using a constant amount of - memory. */ + /** + * Copy the contents of a path to the store and register the + * validity the resulting path, using a constant amount of + * memory. + */ ValidPathInfo addToStoreSlow(std::string_view name, const Path & srcPath, FileIngestionMethod method = FileIngestionMethod::Recursive, HashType hashAlgo = htSHA256, std::optional expectedCAHash = {}); - /* Like addToStore(), but the contents of the path are contained - in `dump', which is either a NAR serialisation (if recursive == - true) or simply the contents of a regular file (if recursive == - false). - `dump` may be drained */ - // FIXME: remove? + /** + * Like addToStore(), but the contents of the path are contained + * in `dump', which is either a NAR serialisation (if recursive == + * true) or simply the contents of a regular file (if recursive == + * false). + * `dump` may be drained + * + * \todo remove? + */ virtual StorePath addToStoreFromDump(Source & dump, std::string_view name, FileIngestionMethod method = FileIngestionMethod::Recursive, HashType hashAlgo = htSHA256, RepairFlag repair = NoRepair, const StorePathSet & references = StorePathSet()) { unsupported("addToStoreFromDump"); } - /* Like addToStore, but the contents written to the output path is - a regular file containing the given string. */ + /** + * Like addToStore, but the contents written to the output path is a + * regular file containing the given string. + */ virtual StorePath addTextToStore( std::string_view name, std::string_view s, @@ -458,140 +547,180 @@ public: virtual void registerDrvOutput(const Realisation & output, CheckSigsFlag checkSigs) { return registerDrvOutput(output); } - /* Write a NAR dump of a store path. */ + /** + * Write a NAR dump of a store path. + */ virtual void narFromPath(const StorePath & path, Sink & sink) = 0; - /* For each path, if it's a derivation, build it. Building a - derivation means ensuring that the output paths are valid. If - they are already valid, this is a no-op. Otherwise, validity - can be reached in two ways. First, if the output paths is - substitutable, then build the path that way. Second, the - output paths can be created by running the builder, after - recursively building any sub-derivations. For inputs that are - not derivations, substitute them. */ + /** + * For each path, if it's a derivation, build it. Building a + * derivation means ensuring that the output paths are valid. If + * they are already valid, this is a no-op. Otherwise, validity + * can be reached in two ways. First, if the output paths is + * substitutable, then build the path that way. Second, the + * output paths can be created by running the builder, after + * recursively building any sub-derivations. For inputs that are + * not derivations, substitute them. + */ virtual void buildPaths( const std::vector & paths, BuildMode buildMode = bmNormal, std::shared_ptr evalStore = nullptr); - /* Like `buildPaths()`, but return a vector of `BuildResult`s - corresponding to each element in `paths`. Note that in case of - a build/substitution error, this function won't throw an - exception, but return a `BuildResult` containing an error - message. */ + /** + * Like buildPaths(), but return a vector of \ref BuildResult + * BuildResults corresponding to each element in paths. Note that in + * case of a build/substitution error, this function won't throw an + * exception, but return a BuildResult containing an error message. + */ virtual std::vector buildPathsWithResults( const std::vector & paths, BuildMode buildMode = bmNormal, std::shared_ptr evalStore = nullptr); - /* Build a single non-materialized derivation (i.e. not from an - on-disk .drv file). - - ‘drvPath’ is used to deduplicate worker goals so it is imperative that - is correct. That said, it doesn't literally need to be store path that - would be calculated from writing this derivation to the store: it is OK - if it instead is that of a Derivation which would resolve to this (by - taking the outputs of it's input derivations and adding them as input - sources) such that the build time referenceable-paths are the same. - - In the input-addressed case, we usually *do* use an "original" - unresolved derivations's path, as that is what will be used in the - `buildPaths` case. Also, the input-addressed output paths are verified - only by that contents of that specific unresolved derivation, so it is - nice to keep that information around so if the original derivation is - ever obtained later, it can be verified whether the trusted user in fact - used the proper output path. - - In the content-addressed case, we want to always use the - resolved drv path calculated from the provided derivation. This serves - two purposes: - - - It keeps the operation trustless, by ruling out a maliciously - invalid drv path corresponding to a non-resolution-equivalent - derivation. - - - For the floating case in particular, it ensures that the derivation - to output mapping respects the resolution equivalence relation, so - one cannot choose different resolution-equivalent derivations to - subvert dependency coherence (i.e. the property that one doesn't end - up with multiple different versions of dependencies without - explicitly choosing to allow it). - */ + /** + * Build a single non-materialized derivation (i.e. not from an + * on-disk .drv file). + * + * @param drvPath This is used to deduplicate worker goals so it is + * imperative that is correct. That said, it doesn't literally need + * to be store path that would be calculated from writing this + * derivation to the store: it is OK if it instead is that of a + * Derivation which would resolve to this (by taking the outputs of + * it's input derivations and adding them as input sources) such + * that the build time referenceable-paths are the same. + * + * In the input-addressed case, we usually *do* use an "original" + * unresolved derivations's path, as that is what will be used in the + * buildPaths case. Also, the input-addressed output paths are verified + * only by that contents of that specific unresolved derivation, so it is + * nice to keep that information around so if the original derivation is + * ever obtained later, it can be verified whether the trusted user in fact + * used the proper output path. + * + * In the content-addressed case, we want to always use the resolved + * drv path calculated from the provided derivation. This serves two + * purposes: + * + * - It keeps the operation trustless, by ruling out a maliciously + * invalid drv path corresponding to a non-resolution-equivalent + * derivation. + * + * - For the floating case in particular, it ensures that the derivation + * to output mapping respects the resolution equivalence relation, so + * one cannot choose different resolution-equivalent derivations to + * subvert dependency coherence (i.e. the property that one doesn't end + * up with multiple different versions of dependencies without + * explicitly choosing to allow it). + */ virtual BuildResult buildDerivation(const StorePath & drvPath, const BasicDerivation & drv, BuildMode buildMode = bmNormal); - /* Ensure that a path is valid. If it is not currently valid, it - may be made valid by running a substitute (if defined for the - path). */ + /** + * Ensure that a path is valid. If it is not currently valid, it + * may be made valid by running a substitute (if defined for the + * path). + */ virtual void ensurePath(const StorePath & path); - /* Add a store path as a temporary root of the garbage collector. - The root disappears as soon as we exit. */ + /** + * Add a store path as a temporary root of the garbage collector. + * The root disappears as soon as we exit. + */ virtual void addTempRoot(const StorePath & path) { debug("not creating temporary root, store doesn't support GC"); } - /* Return a string representing information about the path that - can be loaded into the database using `nix-store --load-db' or - `nix-store --register-validity'. */ + /** + * @return a string representing information about the path that + * can be loaded into the database using `nix-store --load-db' or + * `nix-store --register-validity'. + */ std::string makeValidityRegistration(const StorePathSet & paths, bool showDerivers, bool showHash); - /* Write a JSON representation of store path metadata, such as the - hash and the references. If ‘includeImpureInfo’ is true, - variable elements such as the registration time are - included. If ‘showClosureSize’ is true, the closure size of - each path is included. */ + /** + * Write a JSON representation of store path metadata, such as the + * hash and the references. + * + * @param includeImpureInfo If true, variable elements such as the + * registration time are included. + * + * @param showClosureSize If true, the closure size of each path is + * included. + */ nlohmann::json pathInfoToJSON(const StorePathSet & storePaths, bool includeImpureInfo, bool showClosureSize, Base hashBase = Base32, AllowInvalidFlag allowInvalid = DisallowInvalid); - /* Return the size of the closure of the specified path, that is, - the sum of the size of the NAR serialisation of each path in - the closure. */ + /** + * @return the size of the closure of the specified path, that is, + * the sum of the size of the NAR serialisation of each path in the + * closure. + */ std::pair getClosureSize(const StorePath & storePath); - /* Optimise the disk space usage of the Nix store by hard-linking files - with the same contents. */ + /** + * Optimise the disk space usage of the Nix store by hard-linking files + * with the same contents. + */ virtual void optimiseStore() { }; - /* Check the integrity of the Nix store. Returns true if errors - remain. */ + /** + * Check the integrity of the Nix store. + * + * @return true if errors remain. + */ virtual bool verifyStore(bool checkContents, RepairFlag repair = NoRepair) { return false; }; - /* Return an object to access files in the Nix store. */ + /** + * @return An object to access files in the Nix store. + */ virtual ref getFSAccessor() { unsupported("getFSAccessor"); } - /* Repair the contents of the given path by redownloading it using - a substituter (if available). */ + /** + * Repair the contents of the given path by redownloading it using + * a substituter (if available). + */ virtual void repairPath(const StorePath & path) { unsupported("repairPath"); } - /* Add signatures to the specified store path. The signatures are - not verified. */ + /** + * Add signatures to the specified store path. The signatures are + * not verified. + */ virtual void addSignatures(const StorePath & storePath, const StringSet & sigs) { unsupported("addSignatures"); } /* Utility functions. */ - /* Read a derivation, after ensuring its existence through - ensurePath(). */ + /** + * Read a derivation, after ensuring its existence through + * ensurePath(). + */ Derivation derivationFromPath(const StorePath & drvPath); - /* Read a derivation (which must already be valid). */ + /** + * Read a derivation (which must already be valid). + */ Derivation readDerivation(const StorePath & drvPath); - /* Read a derivation from a potentially invalid path. */ + /** + * Read a derivation from a potentially invalid path. + */ Derivation readInvalidDerivation(const StorePath & drvPath); - /* Place in `out' the set of all store paths in the file system - closure of `storePath'; that is, all paths than can be directly - or indirectly reached from it. `out' is not cleared. If - `flipDirection' is true, the set of paths that can reach - `storePath' is returned; that is, the closures under the - `referrers' relation instead of the `references' relation is - returned. */ + /** + * @param [out] out Place in here the set of all store paths in the + * file system closure of `storePath'; that is, all paths than can + * be directly or indirectly reached from it. `out' is not cleared. + * + * @param flipDirection If true, the set of paths that can reach + * `storePath' is returned; that is, the closures under the + * `referrers' relation instead of the `references' relation is + * returned. + */ virtual void computeFSClosure(const StorePathSet & paths, StorePathSet & out, bool flipDirection = false, bool includeOutputs = false, bool includeDerivers = false); @@ -600,27 +729,34 @@ public: StorePathSet & out, bool flipDirection = false, bool includeOutputs = false, bool includeDerivers = false); - /* Given a set of paths that are to be built, return the set of - derivations that will be built, and the set of output paths - that will be substituted. */ + /** + * Given a set of paths that are to be built, return the set of + * derivations that will be built, and the set of output paths that + * will be substituted. + */ virtual void queryMissing(const std::vector & targets, StorePathSet & willBuild, StorePathSet & willSubstitute, StorePathSet & unknown, uint64_t & downloadSize, uint64_t & narSize); - /* Sort a set of paths topologically under the references - relation. If p refers to q, then p precedes q in this list. */ + /** + * Sort a set of paths topologically under the references + * relation. If p refers to q, then p precedes q in this list. + */ StorePaths topoSortPaths(const StorePathSet & paths); - /* Export multiple paths in the format expected by ‘nix-store - --import’. */ + /** + * Export multiple paths in the format expected by ‘nix-store + * --import’. + */ void exportPaths(const StorePathSet & paths, Sink & sink); void exportPath(const StorePath & path, Sink & sink); - /* Import a sequence of NAR dumps created by exportPaths() into - the Nix store. Optionally, the contents of the NARs are - preloaded into the specified FS accessor to speed up subsequent - access. */ + /** + * Import a sequence of NAR dumps created by exportPaths() into the + * Nix store. Optionally, the contents of the NARs are preloaded + * into the specified FS accessor to speed up subsequent access. + */ StorePaths importPaths(Source & source, CheckSigsFlag checkSigs = CheckSigs); struct Stats @@ -642,8 +778,9 @@ public: const Stats & getStats(); - /* Computes the full closure of of a set of store-paths for e.g. - derivations that need this information for `exportReferencesGraph`. + /** + * Computes the full closure of of a set of store-paths for e.g. + * derivations that need this information for `exportReferencesGraph`. */ StorePathSet exportReferences(const StorePathSet & storePaths, const StorePathSet & inputPaths); @@ -654,18 +791,24 @@ public: */ std::optional getBuildDerivationPath(const StorePath &); - /* Hack to allow long-running processes like hydra-queue-runner to - occasionally flush their path info cache. */ + /** + * Hack to allow long-running processes like hydra-queue-runner to + * occasionally flush their path info cache. + */ void clearPathInfoCache() { state.lock()->pathInfoCache.clear(); } - /* Establish a connection to the store, for store types that have - a notion of connection. Otherwise this is a no-op. */ + /** + * Establish a connection to the store, for store types that have + * a notion of connection. Otherwise this is a no-op. + */ virtual void connect() { }; - /* Get the protocol version of this store or it's connection. */ + /** + * Get the protocol version of this store or it's connection. + */ virtual unsigned int getProtocol() { return 0; @@ -681,7 +824,7 @@ public: return toRealPath(printStorePath(storePath)); } - /* + /** * Synchronises the options of the client with those of the daemon * (a no-op when there’s no daemon) */ @@ -693,7 +836,13 @@ protected: Stats stats; - /* Unsupported methods. */ + /** + * Helper for methods that are not unsupported: this is used for + * default definitions for virtual methods that are meant to be overriden. + * + * \todo Using this should be a last resort. It is better to make + * the method "virtual pure" and/or move it to a subclass. + */ [[noreturn]] void unsupported(const std::string & op) { throw Unsupported("operation '%s' is not supported by store '%s'", op, getUri()); @@ -702,7 +851,9 @@ protected: }; -/* Copy a path from one store to another. */ +/** + * Copy a path from one store to another. + */ void copyStorePath( Store & srcStore, Store & dstStore, @@ -711,12 +862,14 @@ void copyStorePath( CheckSigsFlag checkSigs = CheckSigs); -/* Copy store paths from one store to another. The paths may be copied - in parallel. They are copied in a topologically sorted order (i.e. - if A is a reference of B, then A is copied before B), but the set - of store paths is not automatically closed; use copyClosure() for - that. Returns a map of what each path was copied to the dstStore - as. */ +/** + * Copy store paths from one store to another. The paths may be copied + * in parallel. They are copied in a topologically sorted order (i.e. if + * A is a reference of B, then A is copied before B), but the set of + * store paths is not automatically closed; use copyClosure() for that. + * + * @return a map of what each path was copied to the dstStore as. + */ std::map copyPaths( Store & srcStore, Store & dstStore, const RealisedPath::Set &, @@ -731,7 +884,9 @@ std::map copyPaths( CheckSigsFlag checkSigs = CheckSigs, SubstituteFlag substitute = NoSubstitute); -/* Copy the closure of `paths` from `srcStore` to `dstStore`. */ +/** + * Copy the closure of `paths` from `srcStore` to `dstStore`. + */ void copyClosure( Store & srcStore, Store & dstStore, const RealisedPath::Set & paths, @@ -746,52 +901,61 @@ void copyClosure( CheckSigsFlag checkSigs = CheckSigs, SubstituteFlag substitute = NoSubstitute); -/* Remove the temporary roots file for this process. Any temporary - root becomes garbage after this point unless it has been registered - as a (permanent) root. */ +/** + * Remove the temporary roots file for this process. Any temporary + * root becomes garbage after this point unless it has been registered + * as a (permanent) root. + */ void removeTempRoots(); -/* Resolve the derived path completely, failing if any derivation output - is unknown. */ +/** + * Resolve the derived path completely, failing if any derivation output + * is unknown. + */ OutputPathMap resolveDerivedPath(Store &, const DerivedPath::Built &, Store * evalStore = nullptr); -/* Return a Store object to access the Nix store denoted by - ‘uri’ (slight misnomer...). Supported values are: - - * ‘local’: The Nix store in /nix/store and database in - /nix/var/nix/db, accessed directly. - - * ‘daemon’: The Nix store accessed via a Unix domain socket - connection to nix-daemon. - - * ‘unix://’: The Nix store accessed via a Unix domain socket - connection to nix-daemon, with the socket located at . - - * ‘auto’ or ‘’: Equivalent to ‘local’ or ‘daemon’ depending on - whether the user has write access to the local Nix - store/database. - - * ‘file://’: A binary cache stored in . - - * ‘https://’: A binary cache accessed via HTTP. - - * ‘s3://’: A writable binary cache stored on Amazon's Simple - Storage Service. - - * ‘ssh://[user@]’: A remote Nix store accessed by running - ‘nix-store --serve’ via SSH. - - You can pass parameters to the store implementation by appending - ‘?key=value&key=value&...’ to the URI. -*/ +/** + * @return a Store object to access the Nix store denoted by + * ‘uri’ (slight misnomer...). + * + * @param uri Supported values are: + * + * - ‘local’: The Nix store in /nix/store and database in + * /nix/var/nix/db, accessed directly. + * + * - ‘daemon’: The Nix store accessed via a Unix domain socket + * connection to nix-daemon. + * + * - ‘unix://’: The Nix store accessed via a Unix domain socket + * connection to nix-daemon, with the socket located at . + * + * - ‘auto’ or ‘’: Equivalent to ‘local’ or ‘daemon’ depending on + * whether the user has write access to the local Nix + * store/database. + * + * - ‘file://’: A binary cache stored in . + * + * - ‘https://’: A binary cache accessed via HTTP. + * + * - ‘s3://’: A writable binary cache stored on Amazon's Simple + * Storage Service. + * + * - ‘ssh://[user@]’: A remote Nix store accessed by running + * ‘nix-store --serve’ via SSH. + * + * You can pass parameters to the store implementation by appending + * ‘?key=value&key=value&...’ to the URI. + */ ref openStore(const std::string & uri = settings.storeUri.get(), const Store::Params & extraParams = Store::Params()); -/* Return the default substituter stores, defined by the - ‘substituters’ option and various legacy options. */ +/** + * @return the default substituter stores, defined by the + * ‘substituters’ option and various legacy options. + */ std::list> getDefaultSubstituters(); struct StoreFactory @@ -834,8 +998,10 @@ struct RegisterStoreImplementation }; -/* Display a set of paths in human-readable form (i.e., between quotes - and separated by commas). */ +/** + * Display a set of paths in human-readable form (i.e., between quotes + * and separated by commas). + */ std::string showPaths(const PathSet & paths); @@ -844,7 +1010,9 @@ std::optional decodeValidPathInfo( std::istream & str, std::optional hashGiven = std::nullopt); -/* Split URI into protocol+hierarchy part and its parameter set. */ +/** + * Split URI into protocol+hierarchy part and its parameter set. + */ std::pair splitUriAndParams(const std::string & uri); std::optional getDerivationCA(const BasicDerivation & drv); -- cgit v1.2.3 From 36b059748d06e39fb3e588463bbc4ddcd2255f27 Mon Sep 17 00:00:00 2001 From: Alexander Bantyev Date: Thu, 23 Mar 2023 19:27:41 +0400 Subject: Split nix-env and nix-store documentation per-subcommand Documentation on "classic" commands with many sub-commands are notoriously hard to discover due to lack of overview and anchor links. Additionally the information on common options and environment variables is not accessible offline in man pages, and therefore often overlooked by readers. With this change, each sub-command of nix-store and nix-env gets its own page in the manual (listed in the table of contents), and each own man page. Also, man pages for each subcommand now (again) list common options and environment variables. While this makes each page quite long and some common parameters don't apply, this should still make it easier to navigate as that additional information was not accessible on the command line at all. It is now possible to run 'nix-store -- --help` to display help pages for the given subcommand. Co-authored-by: Valentin Gagarin --- src/nix-env/nix-env.cc | 49 +++++++++++++++++++------- src/nix-store/nix-store.cc | 85 ++++++++++++++++++++++++++++++++++------------ 2 files changed, 101 insertions(+), 33 deletions(-) (limited to 'src') diff --git a/src/nix-env/nix-env.cc b/src/nix-env/nix-env.cc index aa7ada37d..f076ffdb0 100644 --- a/src/nix-env/nix-env.cc +++ b/src/nix-env/nix-env.cc @@ -1387,6 +1387,8 @@ static int main_nix_env(int argc, char * * argv) { Strings opFlags, opArgs; Operation op = 0; + std::string opName; + bool showHelp = false; RepairFlag repair = NoRepair; std::string file; @@ -1426,37 +1428,59 @@ static int main_nix_env(int argc, char * * argv) Operation oldOp = op; if (*arg == "--help") - showManPage("nix-env"); + showHelp = true; else if (*arg == "--version") op = opVersion; - else if (*arg == "--install" || *arg == "-i") + else if (*arg == "--install" || *arg == "-i") { op = opInstall; + opName = "-install"; + } else if (*arg == "--force-name") // undocumented flag for nix-install-package globals.forceName = getArg(*arg, arg, end); - else if (*arg == "--uninstall" || *arg == "-e") + else if (*arg == "--uninstall" || *arg == "-e") { op = opUninstall; - else if (*arg == "--upgrade" || *arg == "-u") + opName = "-uninstall"; + } + else if (*arg == "--upgrade" || *arg == "-u") { op = opUpgrade; - else if (*arg == "--set-flag") + opName = "-upgrade"; + } + else if (*arg == "--set-flag") { op = opSetFlag; - else if (*arg == "--set") + opName = arg->substr(1); + } + else if (*arg == "--set") { op = opSet; - else if (*arg == "--query" || *arg == "-q") + opName = arg->substr(1); + } + else if (*arg == "--query" || *arg == "-q") { op = opQuery; + opName = "-query"; + } else if (*arg == "--profile" || *arg == "-p") globals.profile = absPath(getArg(*arg, arg, end)); else if (*arg == "--file" || *arg == "-f") file = getArg(*arg, arg, end); - else if (*arg == "--switch-profile" || *arg == "-S") + else if (*arg == "--switch-profile" || *arg == "-S") { op = opSwitchProfile; - else if (*arg == "--switch-generation" || *arg == "-G") + opName = "-switch-profile"; + } + else if (*arg == "--switch-generation" || *arg == "-G") { op = opSwitchGeneration; - else if (*arg == "--rollback") + opName = "-switch-generation"; + } + else if (*arg == "--rollback") { op = opRollback; - else if (*arg == "--list-generations") + opName = arg->substr(1); + } + else if (*arg == "--list-generations") { op = opListGenerations; - else if (*arg == "--delete-generations") + opName = arg->substr(1); + } + else if (*arg == "--delete-generations") { op = opDeleteGenerations; + opName = arg->substr(1); + } else if (*arg == "--dry-run") { printInfo("(dry run; not doing anything)"); globals.dryRun = true; @@ -1485,6 +1509,7 @@ static int main_nix_env(int argc, char * * argv) myArgs.parseCmdline(argvToStrings(argc, argv)); + if (showHelp) showManPage("nix-env" + opName); if (!op) throw UsageError("no operation specified"); auto store = openStore(); diff --git a/src/nix-store/nix-store.cc b/src/nix-store/nix-store.cc index 54479489f..a62cb874f 100644 --- a/src/nix-store/nix-store.cc +++ b/src/nix-store/nix-store.cc @@ -1024,62 +1024,104 @@ static int main_nix_store(int argc, char * * argv) Strings opFlags, opArgs; Operation op = 0; bool readFromStdIn = false; + std::string opName; + bool showHelp = false; parseCmdLine(argc, argv, [&](Strings::iterator & arg, const Strings::iterator & end) { Operation oldOp = op; if (*arg == "--help") - showManPage("nix-store"); + showHelp = true; else if (*arg == "--version") op = opVersion; - else if (*arg == "--realise" || *arg == "--realize" || *arg == "-r") + else if (*arg == "--realise" || *arg == "--realize" || *arg == "-r") { op = opRealise; - else if (*arg == "--add" || *arg == "-A") + opName = "-realise"; + } + else if (*arg == "--add" || *arg == "-A"){ op = opAdd; - else if (*arg == "--add-fixed") + opName = "-add"; + } + else if (*arg == "--add-fixed") { op = opAddFixed; + opName = arg->substr(1); + } else if (*arg == "--print-fixed-path") op = opPrintFixedPath; - else if (*arg == "--delete") + else if (*arg == "--delete") { op = opDelete; - else if (*arg == "--query" || *arg == "-q") + opName = arg->substr(1); + } + else if (*arg == "--query" || *arg == "-q") { op = opQuery; - else if (*arg == "--print-env") + opName = "-query"; + } + else if (*arg == "--print-env") { op = opPrintEnv; - else if (*arg == "--read-log" || *arg == "-l") + opName = arg->substr(1); + } + else if (*arg == "--read-log" || *arg == "-l") { op = opReadLog; - else if (*arg == "--dump-db") + opName = "-read-log"; + } + else if (*arg == "--dump-db") { op = opDumpDB; - else if (*arg == "--load-db") + opName = arg->substr(1); + } + else if (*arg == "--load-db") { op = opLoadDB; + opName = arg->substr(1); + } else if (*arg == "--register-validity") op = opRegisterValidity; else if (*arg == "--check-validity") op = opCheckValidity; - else if (*arg == "--gc") + else if (*arg == "--gc") { op = opGC; - else if (*arg == "--dump") + opName = arg->substr(1); + } + else if (*arg == "--dump") { op = opDump; - else if (*arg == "--restore") + opName = arg->substr(1); + } + else if (*arg == "--restore") { op = opRestore; - else if (*arg == "--export") + opName = arg->substr(1); + } + else if (*arg == "--export") { op = opExport; - else if (*arg == "--import") + opName = arg->substr(1); + } + else if (*arg == "--import") { op = opImport; + opName = arg->substr(1); + } else if (*arg == "--init") op = opInit; - else if (*arg == "--verify") + else if (*arg == "--verify") { op = opVerify; - else if (*arg == "--verify-path") + opName = arg->substr(1); + } + else if (*arg == "--verify-path") { op = opVerifyPath; - else if (*arg == "--repair-path") + opName = arg->substr(1); + } + else if (*arg == "--repair-path") { op = opRepairPath; - else if (*arg == "--optimise" || *arg == "--optimize") + opName = arg->substr(1); + } + else if (*arg == "--optimise" || *arg == "--optimize") { op = opOptimise; - else if (*arg == "--serve") + opName = "-optimise"; + } + else if (*arg == "--serve") { op = opServe; - else if (*arg == "--generate-binary-cache-key") + opName = arg->substr(1); + } + else if (*arg == "--generate-binary-cache-key") { op = opGenerateBinaryCacheKey; + opName = arg->substr(1); + } else if (*arg == "--add-root") gcRoot = absPath(getArg(*arg, arg, end)); else if (*arg == "--stdin" && !isatty(STDIN_FILENO)) @@ -1109,6 +1151,7 @@ static int main_nix_store(int argc, char * * argv) return true; }); + if (showHelp) showManPage("nix-store" + opName); if (!op) throw UsageError("no operation specified"); if (op != opDump && op != opRestore) /* !!! hack */ -- cgit v1.2.3 From 1b6cfe6fa1f27046216a7b2a88b0513b1f6be4c1 Mon Sep 17 00:00:00 2001 From: Joachim Breitner Date: Thu, 30 Mar 2023 10:40:04 +0200 Subject: More precise wording --- src/libexpr/primops.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index ea91aac03..fdac53df2 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -256,7 +256,9 @@ static RegisterPrimOp primop_import({ .doc = R"( Load, parse and return the Nix expression in the file *path*. - The value *path* is converted to a string as described in `builtins.toString`. + The value *path* can be a path, a string, or an attribute set with an + `__toString` attribute or a `outPath` attribute (as derivations or falke + inputs typically have). If *path* is a directory, the file `default.nix` in that directory is loaded. -- cgit v1.2.3 From 02050bd055120874f38194b9790c70c84792c369 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 30 Mar 2023 10:49:21 +0200 Subject: Typo --- src/libexpr/primops.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index fdac53df2..72faeada8 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -257,7 +257,7 @@ static RegisterPrimOp primop_import({ Load, parse and return the Nix expression in the file *path*. The value *path* can be a path, a string, or an attribute set with an - `__toString` attribute or a `outPath` attribute (as derivations or falke + `__toString` attribute or a `outPath` attribute (as derivations or flake inputs typically have). If *path* is a directory, the file `default.nix` in that directory -- cgit v1.2.3